sveltejs / svelte

web development for the rest of us
https://svelte.dev
MIT License
79.83k stars 4.24k forks source link

Allow slot='...' attribute on single top level element #6057

Open firewave-remo opened 3 years ago

firewave-remo commented 3 years ago

Is your feature request related to a problem? Please describe. I am working on a Component Library for myself. I would like to use the slot='...' attribute in a component without being a child of the component. Currently i get the following error:

Element with a slot='...' attribute must be a child of a component or a descendant of a custom element

REPL: https://svelte.dev/repl/9fbd3ef30184481a9edae5dbde4e574b?version=3.35.0

Currently i have to use the slot name on the Component itself like this:

<Navbar>
    ...
    <NavItem>
        <NavIcon slot="icon">Icon 1</NavIcon>
        <NavLabel slot="label">Label</NavLabel>
    </NavItem>
    ...
</Navbar>

This feels somewhat redundant.

Describe the solution you'd like I would like to be able to directly insert the children without the slot name, because the child itself knows the slot name:

<Navbar>
    ...
    <!-- I would like to use it like this, where the Component itself knows the slot -->
    <NavItem>
        <NavIcon>Icon 3</NavIcon>
        <NavLabel>Label 3</NavLabel>
    </NavItem>
    ...
</Navbar>

and in NavIcon.svelte:

<div slot="icon">
    <slot />
</div>

Technically the component is a child of a component or a descendant of a custom element, If the slot='...' attribute is restricted to be a single top level element of a component.

How important is this feature to you? Nice to have.

PatrickG commented 3 years ago

Duplicate of #5979

firewave-remo commented 3 years ago

@PatrickG I don't think it's a duplicate. #5979 would like to be able to "feed" multiple slots from one component. I would only like to be able to move the slot name to the child itself when there is just a single top element. Of course they are related but i also think they need a different approach to be solved.

YamiOdymel commented 3 years ago

The concept is great if you are making a UI library and user only need to use <NavItem> instead of <div slot="item">. I've seen lots of React component libraries doing this with swapping children but this might be easier with JSX.

vospascal commented 3 years ago

you can just do slot ... https://svelte.dev/repl/8529d01291644a878ff2136660c88132?version=3.38.3 like this? else you might have to do something like this. the other examples dont work any more but they where a awkward

{#if $$slots.icon}

{/if}

dummdidumm commented 3 years ago

I'm not sure this is a good idea. It might look slick at first, especially for component libraries, but it might also be very confusing what's going on since you don't see the relationship between parent and child slots in the component itself anymore. Also if people swap out the components without applying the same logic to their new component it breaks and they would wonder why that is. Lastly, you now would have two ways to describe the slot relationship.

firewave-remo commented 3 years ago

Valid points. But some points you mentioned also apply to the current situation. If you swap out the components and you forget slot="foo" then it breaks as well. And you already have to know that these 2 Components need to be in a hierarchy to know that you have to add the slot="foo". For me, this feels redundant.

The reason I brought this up, was because I have worked with Vuetify in Vue in the past and it was a nice DX. The relationship between parent and child is normally clear because of the naming of your components. Navbar --> NavIcon

brgrz commented 3 years ago

Well @vospascal offered a simple solution which just works (today!) with noname slots

firewave-remo commented 3 years ago

@brgrz What if you have other markup between the 2 slots? This won't work, or do I miss something?

brgrz commented 3 years ago

@firewave-remo Other markup, for example?

firewave-remo commented 3 years ago

@brgrz

<div>
    <li>
        <slot name="icon" />
        <div>
            Whatever
        </div>
        <slot name="label" />
    </li>
</div>
brgrz commented 3 years ago

The point of @vospascal solution is to have minimal components which only contain the default slot and no named slots and then you compose those minimal components using the component tag names removing the need for the slot attribute. Just as he shows in REPL. I'm actually using the same idea in a project of mine atm.

One issue I do see with this solution is that components have to be provided in the correct order (since there is only one big default slot) unless you use somekind of flex/grid layout within the composing component to enable ordering/placement.