Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Targeted Slots - passing and mixing attributes and styles to component, access the slotted element from inside the component #68

Closed
wants to merge 20 commits into from

Conversation

lukaszpolowczyk
Copy link

@lukaszpolowczyk lukaszpolowczyk commented Aug 7, 2022

Rendered.

People who might like it (and my inspirations):
@Tropix126 @Zizico2 @ghughes

@Zizico2
Copy link

Zizico2 commented Aug 7, 2022

To me this feels like the culmination of the discussion about this topic. I think the use of svelte:element is incredibly intuitive. We see it as a dynamic element anyway, we're just adding even more dynamicness (?).

I think this supercedes my RFC in every way. I won't close it for a while since there might be some more discussion over there that's doesn't fit in this RFC. I digress...

Giving my 2 cents on some of the unanswered questions:

The attribute value written in Parent, should overwrite the attribute value written in Child. Maybe it should be possible to decide the order of overwriting?

I don't think it should. It would make slots more cumbersome to work with. You don't know what your getting. It stops being consistent.

Passing more than one targeted:name to one target and to slot:subname.

I can't come up with a use case either. I feel like unless we can find a use case, it's hard to think about advantages and drawbacks, so we shouldn't add it, just because it is added complexity.

Someone sees <svelte:element slot="name"/> and thinks it's a simple slot. And it is a Targeted Slot. Therefore, perhaps the name of the slot attribute should be changed into another one. But I don't know into which one?

Is this a problem? Targeted slots are a superset of slots anyway. They can use it as a simple slot. It's literally a "1 child slot".

This was referenced Aug 13, 2022
@mimbrown
Copy link

Very interesting proposal here. A couple pushbacks:

  1. The double use of <svelte:element> in both parent and child makes it, IMO, difficult to understand what is going on.
  2. The way this stands, it's pretty easy to write a component with a targeted slot, and comparatively difficult to use them. Given that it's generally going to be library authors creating components with slots and userlanders using them, that seems a bit backwards to me (normally the complexity, if it's unavoidable, should be offloaded onto the library author).

@lukaszpolowczyk
Copy link
Author

lukaszpolowczyk commented Sep 10, 2022

@mimbrown

  1. There is an option to make the inside of the target called <svelte:target/> or <target/> (straight from Declarative Actions), but it would still be such a slot, it would have a targeted:name attribute: <svelte:target targeted:name>.
    But the basic distinction is to use the slot="name" attribute in Parent, and targeted:name in Child. I thought that was sufficient?

  2. Show an example that it would be difficult to use this. That is, what would you want to use it for, and what would be the difficulty in doing so?
    Too much code in Parent? Or what do you have in mind? Show exactly.

@mimbrown
Copy link

There is an option to make the inside of the target called svelte:target/ or (straight from Declarative Actions), but it would still be such a slot, it would have a targeted:name attribute: <svelte:target targeted:name>.
But the basic distinction is to use the slot="name" attribute in Parent, and targeted:name in Child. I thought that was sufficient?

That does help, thanks for pointing that out.

Show an example that it would be difficult to use this. That is, what would you want to use it for, and what would be the difficulty in doing so?
Too much code in Parent? Or what do you have in mind? Show exactly.

In my original use case, I was thinking about how much more naturally something like a material-design button is composable functionality, not a reusable component. So, with Declarative Actions-like syntax (in a contrived example), the consumer markup would look something like this:

<script>
import { matRaisedButton, matOutlinedButton } from 'some-lib';
</script>

<div class="actions">
  <button use:matOutlinedButton class="custom-class">Cancel</button>
  <a href="#" use:matRaisedButton class="custom-class">Save</a>
</div>

Whereas targeted actions (as I understand it) would be something like:

<script>
import { MatRaisedButton, MatOutlinedButton } from 'some-lib';
</script>

<div class="actions">
  <MatOutlinedButton><svelte:element slot="button" class="custom-class" />Cancel</MatOutlinedButton>
  <MatRaisedButton><svelte:element slot="button" this="a" href="#" class="custom-class" />Save</MatRaisedButton>
</div>

So, yes, more keystrokes, but that's not actually what I don't like. In the first example, it's transparently obvious what the markup will be. The second is a lot more cognitive load.

Minor additions - `<svelte:target>`.
Minor additions - style order method.
Formatting fix in one place.
@lukaszpolowczyk
Copy link
Author

lukaszpolowczyk commented Sep 10, 2022

@mimbrown The problem this="a", is a problem in general <svelte:element/>. That' s why it exists, so that there is a dynamic tagName.

To make it simpler in Targeted Slots, you could still allow the use of any element, not just <svelte:element/> - but I really don't know if that is technically possible, which is why I added it in "Rejected at this time". If it is, I'd be happy. :)

Then the example with a would look like this (I also changed the name of the slot, because the name of the slot in Child, you can give any):

  <MatRaisedButton><a slot="link" href="#" class="custom-class">Save</a></MatRaisedButton>

And as for the amount of code in Parent - It's because of the universality of Targeted Slots - that you can use more than one slot.
But you can think of exceptions. Here we have an exception that one slot is enough.

You can think about the syntax (exception) - a shortcut:

<svelte:element slot={MatOutlinedButton} class="custom-class">Cancel</svelte:element>

This would be a literal shortcut, you could handle it with a preprocessor that simply converts this code, to the target code:

  <MatOutlinedButton><svelte:element slot="default" class="custom-class">Cancel</svelte:element></MatOutlinedButton>

That this preprocessor would have to name slot="default", and you would have to handle this in your Child component via targeted:default.

Note1: This syntax would be very similar to use:matRaisedButton, but still more flexible.
Note2: This has absolutely nothing to do with <svelte:component this={matRaisedButton}/>, it should not be confused.


You could also build such an exception into the normal behavior of Targeted Slots, but that complicates the proposal.
I simply did not want to complicate the proposal.

The syntax slot={MatRaisedButton} complicates the proposal in several ways:

  • E.g., it makes it difficult to pass properties to a component (because it is then an element, not a component).
    One would have to use the syntax, e.g. pass:val, to pass props to MatRaisedButton as if it were a component.
  • There would be the problem of deciding whether on:event should be received as for a component or as for an element.
  • You would have to reserve targeted:default, which would handle the Targeted Slot without a name.

This is something I didn't describe in "Rejected at this time," because it has too many ambiguities and complicates the proposal. I only wrote about "cross-mix attributes (between Component and element)", which I reject at this point.


Using any element tagName and slot={MatRaisedButton} e.g.

<a slot={MatRaisedButton} href="#" class="custom-class">Save</a>

...is nice, but I don't know if technically possible and some problems need to be solved.

It may be possible, but I'm waiting for some SvelteJS member to say something.

@mimbrown
Copy link

My thoughts are clarifying on this. At first I thought the difference here was just a difference in mental model, but that's not true. This proposal is about a svelte component that provides targeted access to its elements. This is a very worthy goal. I see some really good applications, for example solving the long-standing problem that reusable form elements generally have the underlying input element nested underneath other elements (like a label), but consumers of those components normally want to add custom classes, listeners, and attributes to the input element, not its parent. Targeted slots would solve that issue.

But the Declarative Actions proposal is about defining a reusable action (or composable) that the consumer can use however they wish, including by "composing" multiple actions with an element that they create. That means the consumer can do this:

<script>
import dropTarget from './dropTarget.svelte';
import matRaisedButton from './matRaisedButton.svelte';
</script>

<button use:matRaisedButton use:dropTarget class="custom-class">Click or drop</button>

Unless I'm very much mistaken, this is functionality that targeted slots can't do. I'm trying to say I don't think targeted slots is a superset solution to declarative actions, they're different proposals.

@lukaszpolowczyk
Copy link
Author

@mimbrown Inside Child you can refer to the button element, using bind:this={target}.

<!-- Child.svelte -->
<script>
 let target
</script>
<svelte:element targeted:name bind:this={target}/>

In this code, target is the node equivalent of use:action.

But the equivalent of many use:action1 use:action2 is not in Targeted Slots. This is true.
I would have to think about how to include such a thing in my proposal.

Nothing prevents you from using use:action use:action next to the Targeted Slot, but I understand that this is not enough.

I don't know if it will work for me, but I will try to think of something.

@mimbrown
Copy link

@lukaszpolowczyk I don't think it should be included, because I think it's a separate issue. Declarative Actions is a proposal for allowing declarative, composable pieces of functionality to be defined. Targeted slots is a proposal for giving consumers insight and "targetability" into a custom svelte component. Two separate possible solutions to two different problems.

@lukaszpolowczyk
Copy link
Author

@mimbrown But my inspiration was Declarative Actions, among other things.
The creator of Declarative Actions said that my proposal replaces Declarative Actions.
Declarative Actions has the very purpose you described. :D

I see no reason to separate it.

At this point I have no idea, so there is nothing to talk about.

If something comes to mind, I will just describe it.

@mimbrown
Copy link

@Zizico2 Do you have any desire to weigh in on this?

@lukaszpolowczyk
Copy link
Author

lukaszpolowczyk commented Jul 24, 2023

@mimbrown @Zizico2 I have a simple idea for a replacement for a lot of use:action.

<!-- Parent.svelte -->
<svelte:component bind:these={[ Child1, Child2 ]}>
  <svelte:element slot="name"/>
</svelte:component>

The bind:these attribute allows you to pass several declarative actions, and combine in one slot, as long as there is the same slot name in Child1 and Child2.

The issue that this requires the same slot name is rather a problem, you would need to be able to set the slot name under each Child1 Child2, because Child2 may have a different slot name than Child1.

What do you guys think?

I am not adding this to the description of this rfc for now.

EDIT: Eventually, this should only work if there are unnamed slots inside Child1 and Child2. Then there is no problem.

@dummdidumm
Copy link
Member

Snippets will replace slots (they are still around but deprecated) in Svelte 5 and will have a lot more flexibility with regarding to passing things around. Therefore closing this RFC - thank you.

@dummdidumm dummdidumm closed this Nov 16, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants