pngwn / MDsveX

A markdown preprocessor for Svelte.
https://mdsvex.pngwn.io
MIT License
2.44k stars 102 forks source link

named slots #263

Open nosovk opened 3 years ago

nosovk commented 3 years ago

We use custom components to substitute standart tags. like <a>, <h2>, <blockquote> in our particular case we need to know content of blockquote, like:

> ## title
> content
text

Initial idea was to put 2 different slots in blockquote component, like

<div>
<div><slot name="title">no title</slot><div>
<div><slot /></div>
</div>

and do a little trick with h2 component:

<svelte:fragment slot="title"><slot /></svelte:fragment>

bit tricky, but common idea pretty simple - we wan't to catch nested component in exact slot.

it's falled with strange error: <svelte:fragment> must be a child of a component it means that h2 tag wasn't child of blockquote. Which seems strange. Normal slots works fine, but any usage of named slots seems to be breaking. I can't understand is problem in mdsvex or rather in SlotTemplate inside Svelte.

pngwn commented 3 years ago

I think i'll need to see a full reproduction to debug this.

nosovk commented 3 years ago

I've created repo with reproduction of problem

It builds, and you can see expected result at http://localhost:3000/faq

to broke it we need to uncomment next line: https://github.com/nosovk/svetle-md-slots-reproduction/blob/main/src/lib/Faq/h2.svelte#L4 and comment first line. As a result file faq.md will broke with mentioned error.

imhul commented 3 years ago

nice catch, can't say that behavior is logical, it seems that problem in Svelte, rather in mdsvex.

nosovk commented 3 years ago

I think it's just too complex case and probably I should just should leave it as tags. Because there will be second question - if I will add more then one h2 inside blockquote? Then there will be more than one slot content with the same name provided for blockquote, and then what behaviour will be correct? Which slot should be rendered then?

krry commented 3 years ago

Think I've got a similar case for you.

I'm using custom components for h1, h2, h3, and blockquote. Here is H1.svelte:

<script>
    import nanoid from 'nano-id'
    import ChainLink from '../ChainLink.svelte'
    const id = nanoid()
</script>

<section class="line l1">
    <h1 {id}><slot name="body" /></h1>
    <ChainLink {id}><slot name="title" /></ChainLink>
</section>

I'd like to pass a truncated bit of the \<h1>'s content to ChainLink to use as the titles in a nav. Does this make sense? Can I use named slots like this? Or can I somehow pass the content of the slot to the ChainLink component as a prop?

nosovk commented 3 years ago

Hm, looks like example above should work. You don't pass named slot to h1, but use it on ChainLink component, which should work as expected

krry commented 3 years ago

Hm, looks like example above should work. You don't pass named slot to h1, but use it on ChainLink component, which should work as expected

Thanks. It turns out that Svelte's use:action does what I want. Not sure if this is idiomatic, but here's how the component works today.

<script>
    import { chain } from '$lib/stores/chains';
    import { hashify, tweetify } from '$lib/parsers';

    let hash,
        title,
        classes = $$props.class ? $$props.class : '';

    function chainUp(node) {
        hash = hashify(node);
        title = tweetify(node);
        if (node.parentElement.parentElement.firstElementChild === node) {
            hash = '';
        }
        chain.add({
            hash,
            title,
            tag: 'h1',
        });
    }
</script>

<section id={hash} class="chainlink l1 {classes}">
    <h1 use:chainUp><slot /></h1>
</section>