handle dynamic slot content

pull/8457/head
Simon Holthausen 3 years ago
parent a4102f0b4a
commit 00d04053df

@ -132,6 +132,10 @@ export default class SlotWrapper extends Wrapper {
const ${slot_definition} = ${renderer.reference('#slots')}.${slot_name}; const ${slot_definition} = ${renderer.reference('#slots')}.${slot_name};
const ${slot} = @create_slot(${slot_definition}, #ctx, ${renderer.reference('$$scope')}, ${get_slot_context_fn}); const ${slot} = @create_slot(${slot_definition}, #ctx, ${renderer.reference('$$scope')}, ${get_slot_context_fn});
${has_fallback ? b`const ${slot_or_fallback} = ${slot} || ${this.fallback.name}(#ctx);` : null} ${has_fallback ? b`const ${slot_or_fallback} = ${slot} || ${this.fallback.name}(#ctx);` : null}
${has_fallback && this.renderer.options.customElement && this.renderer.component.component_options.tag
// This ensures that fallback content is rendered into the <slot> element given by the custom element wrapper
? b`if (${slot_or_fallback}.$$c_e) { ${slot_or_fallback}.$$c_e = ${this.fallback.name}(#ctx); }`
: null}
`); `);
block.chunks.create.push( block.chunks.create.push(

@ -172,25 +172,36 @@ if (typeof HTMLElement === 'function') {
connectedCallback() { connectedCallback() {
this.$$connected = true; this.$$connected = true;
if (!this.$$component) { if (!this.$$component) {
function create_slot(name: string) { function create_slot(name: string, $$c_e = false) {
return () => { return () => {
let node: HTMLSlotElement; let node: HTMLSlotElement;
return { const obj = {
c: function create() { c: function create() {
node = document.createElement('slot'); node = document.createElement('slot');
if (name !== 'default') { if (name !== 'default') {
node.setAttribute('name', name); node.setAttribute('name', name);
} }
if (typeof obj.$$c_e === 'object') {
(obj.$$c_e as any).c()
}
}, },
m: function mount(target: HTMLElement, anchor?: HTMLElement) { m: function mount(target: HTMLElement, anchor?: HTMLElement) {
insert(target, node, anchor); insert(target, node, anchor);
if (typeof obj.$$c_e === 'object') {
(obj.$$c_e as any).m(node, anchor)
}
}, },
d: function destroy(detaching: boolean) { d: function destroy(detaching: boolean) {
if (detaching) { if (detaching) {
detach(node); if (typeof obj.$$c_e === 'object') {
(obj.$$c_e as any).d(detaching)
} }
detach(node);
} }
},
$$c_e
}; };
return obj;
}; };
} }
@ -202,6 +213,15 @@ if (typeof HTMLElement === 'function') {
} }
} }
if (!Object.keys($$slots).length && this.$$slots.length) {
// There are potentially slots, but we didn't find any. This could be due to Svelte adding the child nodes
// only after the component is created (in the mount phase). In this case, pass in placeholder slots.
// The drawback is that all $$slots properties will be `true`, even if the component doesn't use them.
for (const name of this.$$slots) {
$$slots[name] = [create_slot(name, true)];
}
}
for (const attribute of this.attributes) { for (const attribute of this.attributes) {
// this.$$data takes precedence over this.attributes // this.$$data takes precedence over this.attributes
const name = this.$$get_prop_name(attribute.name); const name = this.$$get_prop_name(attribute.name);

@ -0,0 +1,10 @@
<svelte:options tag={null} />
<script>
import "./my-widget.svelte";
export let name;
</script>
<my-widget>
<p>default {name}</p>
</my-widget>

@ -0,0 +1,4 @@
<svelte:options tag="my-widget" />
<slot>fallback</slot>
<slot name="named"><p>named fallback</p></slot>

@ -0,0 +1,19 @@
import * as assert from 'assert';
import Component from './main.svelte';
export default function (target) {
const component = new Component({ target, props: { name: 'slot' } });
const ce = target.querySelector('my-widget');
assert.htmlEqual(ce.shadowRoot.innerHTML, `
<slot>fallback</slot>
<slot name=\"named\"><p>named fallback</p></slot>
`);
component.name = 'slot2';
assert.htmlEqual(ce.shadowRoot.innerHTML, `
<slot>fallback</slot>
<slot name=\"named\"><p>named fallback</p></slot>
`);
}
Loading…
Cancel
Save