Skip to content

Commit e1dfadf

Browse files
committed
refactor and expose createSlot, slot to svelte top import
1 parent fa2f8b1 commit e1dfadf

File tree

9 files changed

+75
-52
lines changed

9 files changed

+75
-52
lines changed

package.json

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,6 @@
5151
"import": "./store/index.mjs",
5252
"require": "./store/index.js"
5353
},
54-
"./slot": {
55-
"import": "./slot/index.mjs",
56-
"require": "./slot/index.js"
57-
},
5854
"./transition": {
5955
"import": "./transition/index.mjs",
6056
"require": "./transition/index.js"

src/runtime/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,5 @@ export {
1111
createEventDispatcher,
1212
SvelteComponentDev as SvelteComponent
1313
} from 'svelte/internal';
14+
15+
export { createSlot, slot } from 'svelte/slot';

src/runtime/internal/Component.ts

Lines changed: 30 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,23 @@ import { blank_object, is_empty, is_function, run, run_all, noop } from './utils
44
import { children, detach } from './dom';
55
import { transition_in } from './transitions';
66

7-
interface Fragment {
8-
key: string|null;
9-
first: null;
7+
export interface FragmentMinimal {
108
/* create */ c: () => void;
119
/* claim */ l: (nodes: any) => void;
10+
/* mount */ m: (target: HTMLElement, anchor: HTMLElement) => void;
11+
/* destroy */ d: (detaching: 0|1) => void;
12+
13+
}
14+
interface Fragment extends FragmentMinimal {
15+
key: string|null;
16+
first: null;
1217
/* hydrate */ h: () => void;
13-
/* mount */ m: (target: HTMLElement, anchor: any) => void;
1418
/* update */ p: (ctx: any, dirty: any) => void;
1519
/* measure */ r: () => void;
1620
/* fix */ f: () => void;
1721
/* animate */ a: () => void;
1822
/* intro */ i: (local: any) => void;
1923
/* outro */ o: (local: any) => void;
20-
/* destroy */ d: (detaching: 0|1) => void;
2124
}
2225
interface T$$ {
2326
dirty: number[];
@@ -96,7 +99,26 @@ function make_dirty(component: SvelteComponent, i) {
9699
component.$$.dirty[(i / 31) | 0] |= (1 << (i % 31));
97100
}
98101

99-
export function init(component: SvelteComponent, options, instance, create_fragment, not_equal, props, dirty = [-1]) {
102+
export type Props = Record<string, any>;
103+
104+
export type SvelteSlotOptions = {
105+
props?: Props;
106+
}
107+
108+
export type SvelteComponentOptions = {
109+
target: Element;
110+
anchor?: Element;
111+
hydrate?: boolean;
112+
intro?: boolean;
113+
slots?: unknown;
114+
} & SvelteSlotOptions;
115+
116+
export type SvelteComponentOptionsPrivate = {
117+
target?: Element;
118+
$$inline?: boolean;
119+
} & SvelteComponentOptions;
120+
121+
export function init(component: SvelteComponent, options: SvelteComponentOptions, instance, create_fragment, not_equal, props: Props, dirty = [-1]) {
100122
const parent_component = current_component;
101123
set_current_component(component);
102124

@@ -167,11 +189,7 @@ export function init(component: SvelteComponent, options, instance, create_fragm
167189
set_current_component(parent_component);
168190
}
169191

170-
export const SvelteElement = (() => {
171-
if (typeof HTMLElement !== 'function') {
172-
return
173-
}
174-
return class extends HTMLElement {
192+
export class SvelteElement extends HTMLElement {
175193
$$: T$$;
176194
$$set?: ($$props: any) => void;
177195
constructor() {
@@ -214,8 +232,7 @@ export const SvelteElement = (() => {
214232
this.$$.skip_bound = false;
215233
}
216234
}
217-
};
218-
})();
235+
}
219236

220237
export class SvelteComponent {
221238
$$: T$$;

src/runtime/internal/dev.ts

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { custom_event, append, insert, detach, listen, attr } from './dom';
2-
import { SvelteComponent } from './Component';
2+
import { SvelteComponent, Props, SvelteComponentOptions, SvelteComponentOptionsPrivate, SvelteSlotOptions} from './Component';
33

44
export function dispatch_dev<T=any>(type: string, detail?: T) {
55
document.dispatchEvent(custom_event(type, { version: '__VERSION__', ...detail }));
@@ -97,7 +97,6 @@ export function validate_slots(name, slot, keys) {
9797
}
9898
}
9999

100-
type Props = Record<string, any>;
101100
export interface SvelteComponentDev {
102101
$set(props?: Props): void;
103102
$on<T = any>(event: string, callback: (event: CustomEvent<T>) => void): () => void;
@@ -106,15 +105,9 @@ export interface SvelteComponentDev {
106105
}
107106

108107
export class SvelteComponentDev extends SvelteComponent {
109-
constructor(options: {
110-
target: Element;
111-
anchor?: Element;
112-
props?: Props;
113-
hydrate?: boolean;
114-
intro?: boolean;
115-
$$inline?: boolean;
116-
}) {
117-
if (!options || (!options.target && !options.$$inline)) {
108+
constructor(options: SvelteComponentOptions) {
109+
const privateOptions: SvelteComponentOptionsPrivate = options;
110+
if (!privateOptions || (!privateOptions.target && !privateOptions.$$inline)) {
118111
throw new Error("'target' is a required option");
119112
}
120113

src/runtime/slot/index.ts

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
import { noop, insert, detach } from 'svelte/internal';
1+
import { noop, insert, detach, FragmentMinimal, SvelteSlotOptions, SvelteComponentOptionsPrivate } from 'svelte/internal';
2+
import { SvelteComponent } from '..';
23

3-
function create_root_slot_fn(elements) {
4-
return function () {
4+
function create_root_slot_fn(elements: Node[]) {
5+
return function (): FragmentMinimal {
56
return {
67
c: noop,
78

@@ -22,11 +23,19 @@ function create_root_slot_fn(elements) {
2223
};
2324
}
2425

25-
export function createSlot(input) {
26-
var key, tmp, slots={};
27-
for (key in input) {
28-
tmp = input[key];
29-
slots[key] = [create_root_slot_fn(Array.isArray(tmp) ? tmp : [tmp])];
26+
export function createSlot(input: Record<string, Node | Node[]>) {
27+
const slots: Record<string, ReturnType<typeof create_root_slot_fn>[]> = {};
28+
for (const key in input) {
29+
const nodeOrNodeList = input[key];
30+
const nodeList = Array.isArray(nodeOrNodeList) ? nodeOrNodeList : [nodeOrNodeList];
31+
slots[key] = [create_root_slot_fn(nodeList)];
3032
}
3133
return slots;
3234
}
35+
36+
export function slot(componentClass: typeof SvelteComponent, options: SvelteSlotOptions): Element[] {
37+
const wrapper = document.createElement("div");
38+
new componentClass({...options, target: wrapper} as SvelteComponentOptionsPrivate) as any;
39+
// @TODO this is a workaround until src/compiler/compile/render_dom/Block.ts is extended to expose created HTML element
40+
return Array.from(wrapper.children);;
41+
}

test/helpers.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ global.navigator = window.navigator;
6161
global.getComputedStyle = window.getComputedStyle;
6262
global.requestAnimationFrame = null; // placeholder, filled in using set_raf
6363
global.window = window;
64+
global.HTMLElement = window.HTMLElement;
6465

6566
// add missing ecmascript globals to window
6667
for (const key of Object.getOwnPropertyNames(global)) {
Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { createSlot } from 'svelte/slot';
1+
import { createSlot, slot } from 'svelte';
22

33
export default {
44
options(window) {
@@ -12,39 +12,41 @@ export default {
1212
const another_slot_el = window.document.createTextNode('another slot');
1313
const conditional_slot_el = window.document.createElement('div');
1414
conditional_slot_el.innerHTML = 'conditional slot';
15+
const Nested = require(`./nested.svelte`).default;
1516
return {
1617
slots: createSlot({
1718
default: default_el,
1819
'my-slot': my_slot_els,
1920
'another-slot-with-content': another_slot_el,
2021
'conditional-slot': conditional_slot_el,
22+
'slot-with-content-from-nested': slot(Nested),
2123
}),
2224
};
2325
},
2426

2527
test({ assert, component, target }) {
26-
assert.htmlEqual(target.innerHTML, `
27-
default slot: <div>default slot custom content</div>
28-
named slot: <div>first my slot element</div><div>second my slot element</div>
29-
slot with default content: default content
30-
another slot with content: another slot
31-
conditional slot: <div>conditional slot</div>
32-
conditional slot with content: default content`);
28+
const expectedHtmlWhenSlotIsVisible = `
29+
default slot: <div>default slot custom content</div>
30+
named slot: <div>first my slot element</div><div>second my slot element</div>
31+
slot with default content: default content
32+
another slot with content: another slot
33+
slot with content from nested: <div>this div is in nested.svelte</div><span>this span is in nested.svelte</span>
34+
conditional slot: <div>conditional slot</div>
35+
conditional slot with content: default content`;
36+
37+
assert.htmlEqual(target.innerHTML, expectedHtmlWhenSlotIsVisible);
3338

3439
component.is_slot_visible = false;
3540
assert.htmlEqual(target.innerHTML, `
3641
default slot: <div>default slot custom content</div>
3742
named slot: <div>first my slot element</div><div>second my slot element</div>
3843
slot with default content: default content
39-
another slot with content: another slot`);
44+
another slot with content: another slot
45+
slot with content from nested: <div>this div is in nested.svelte</div><span>this span is in nested.svelte</span>`);
4046

4147
component.is_slot_visible = true;
42-
assert.htmlEqual(target.innerHTML, `
43-
default slot: <div>default slot custom content</div>
44-
named slot: <div>first my slot element</div><div>second my slot element</div>
45-
slot with default content: default content
46-
another slot with content: another slot
47-
conditional slot: <div>conditional slot</div>
48-
conditional slot with content: default content`);
48+
assert.htmlEqual(target.innerHTML, expectedHtmlWhenSlotIsVisible);
49+
// @TODO once src/compiler/compile/render_dom/Block.ts is extended to expose created HTML elements
50+
// and nested component can be referenced directly, test mutating nested child props
4951
}
5052
};

test/runtime/samples/root-component-slot/main.svelte

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ default slot: <slot />
22
named slot: <slot name="my-slot" />
33
slot with default content: <slot name="slot-with-content">default content</slot>
44
another slot with content: <slot name="another-slot-with-content">default content</slot>
5+
slot with content from nested: <slot name="slot-with-content-from-nested">default content</slot>
56
{#if is_slot_visible}
67
conditional slot: <slot name="conditional-slot" />
78
conditional slot with content: <slot name="conditional-slot-with-content">default content</slot>
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
<div>this div is in nested.svelte</div>
2+
<span>this span is in nested.svelte</span>

0 commit comments

Comments
 (0)