Skip to content

Commit

Permalink
support $$slots in custom elements
Browse files Browse the repository at this point in the history
  • Loading branch information
tanhauhau committed Oct 30, 2020
1 parent 148b610 commit cf90e23
Show file tree
Hide file tree
Showing 4 changed files with 75 additions and 2 deletions.
8 changes: 7 additions & 1 deletion src/compiler/compile/render_dom/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -467,14 +467,20 @@ export default function dom(
}

if (options.customElement) {

let init_props = x`@attribute_to_object(this.attributes)`;
if (uses_slots) {
init_props = x`{ ...${init_props}, $$slots: @get_custom_elements_slots(this) }`;
}

const declaration = b`
class ${name} extends @SvelteElement {
constructor(options) {
super();
${css.code && b`this.shadowRoot.innerHTML = \`<style>${css.code.replace(/\\/g, '\\\\')}${options.dev ? `\n/*# sourceMappingURL=${css.map.toUrl()} */` : ''}</style>\`;`}
@init(this, { target: this.shadowRoot, props: @attribute_to_object(this.attributes) }, ${definition}, ${has_create_fragment ? 'create_fragment': 'null'}, ${not_equal}, ${prop_indexes}, ${dirty});
@init(this, { target: this.shadowRoot, props: ${init_props} }, ${definition}, ${has_create_fragment ? 'create_fragment': 'null'}, ${not_equal}, ${prop_indexes}, ${dirty});
${dev_props_check}
Expand Down
10 changes: 9 additions & 1 deletion src/runtime/internal/dom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -361,10 +361,18 @@ export class HtmlTag {
}
}

export function attribute_to_object(attributes) {
export function attribute_to_object(attributes: NamedNodeMap) {
const result = {};
for (const attribute of attributes) {
result[attribute.name] = attribute.value;
}
return result;
}

export function get_custom_elements_slots(element: HTMLElement) {
const result = {};
element.childNodes.forEach((node: Element) => {
result[node.slot || 'default'] = true;
});
return result;
}
31 changes: 31 additions & 0 deletions test/custom-elements/samples/$$slot/main.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<script>
let data = '';
if ($$slots.b) {
data = 'foo';
}
export function getData() {
return data;
}
function toString(data) {
const result = {};
const sortedKeys = Object.keys(data).sort();
sortedKeys.forEach(key => result[key] = data[key]);
return JSON.stringify(result);
}
</script>

<svelte:options tag="custom-element"/>

<slot></slot>
<slot name="a"></slot>
<p>$$slots: {toString($$slots)}</p>
{#if $$slots.b}
<div>
<slot name="b"></slot>
</div>
{:else}
<p>Slot b is not available</p>
{/if}
28 changes: 28 additions & 0 deletions test/custom-elements/samples/$$slot/test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import * as assert from 'assert';
import './main.svelte';

export default function (target) {
target.innerHTML = `
<custom-element><span slot="a">hello world</span><span>bye</span><span>world</span></custom-element>
<custom-element><span slot="a">hello world</span><span slot="b">hello world</span><span>bye world</span></custom-element>
`;

const [a, b] = target.querySelectorAll('custom-element');

assert.htmlEqual(a.shadowRoot.innerHTML, `
<slot></slot>
<slot name="a"></slot>
<p>$$slots: {"a":true,"default":true}</p>
<p>Slot b is not available</p>
`);

assert.htmlEqual(b.shadowRoot.innerHTML, `
<slot></slot>
<slot name="a"></slot>
<p>$$slots: {"a":true,"b":true,"default":true}</p>
<div><slot name="b"></slot></div>
`);

assert.equal(a.getData(), '');
assert.equal(b.getData(), 'foo');
}

0 comments on commit cf90e23

Please sign in to comment.