Skip to content

Commit fb851f1

Browse files
committed
allow <slot> to be part of a slot
1 parent b39282a commit fb851f1

File tree

8 files changed

+134
-42
lines changed

8 files changed

+134
-42
lines changed
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import ElementWrapper from './index';
2+
import Block from '../../Block';
3+
import { sanitize } from '../../../../utils/names';
4+
import InlineComponentWrapper from '../InlineComponent';
5+
import create_debugging_comment from '../shared/create_debugging_comment';
6+
import { get_slot_definition } from '../shared/get_slot_definition';
7+
8+
export default function create_slot_block(attribute, element: ElementWrapper, block: Block) {
9+
const owner = find_slot_owner(element.parent);
10+
11+
if (owner && owner.node.type === 'InlineComponent') {
12+
const name = attribute.get_static_value() as string;
13+
14+
if (!((owner as unknown) as InlineComponentWrapper).slots.has(name)) {
15+
const child_block = block.child({
16+
comment: create_debugging_comment(element.node, element.renderer.component),
17+
name: element.renderer.component.get_unique_name(
18+
`create_${sanitize(name)}_slot`
19+
),
20+
type: 'slot',
21+
});
22+
23+
const { scope, lets } = element.node;
24+
const seen = new Set(lets.map(l => l.name.name));
25+
26+
((owner as unknown) as InlineComponentWrapper).node.lets.forEach(l => {
27+
if (!seen.has(l.name.name)) lets.push(l);
28+
});
29+
30+
((owner as unknown) as InlineComponentWrapper).slots.set(
31+
name,
32+
get_slot_definition(child_block, scope, lets)
33+
);
34+
element.renderer.blocks.push(child_block);
35+
}
36+
37+
element.slot_block = ((owner as unknown) as InlineComponentWrapper).slots.get(
38+
name
39+
).block;
40+
41+
return element.slot_block;
42+
}
43+
44+
return block;
45+
}
46+
47+
function find_slot_owner(owner) {
48+
while (owner) {
49+
if (owner.node.type === 'InlineComponent') {
50+
break;
51+
}
52+
53+
if (owner.node.type === 'Element' && /-/.test(owner.node.name)) {
54+
break;
55+
}
56+
57+
owner = owner.parent;
58+
}
59+
return owner;
60+
}

src/compiler/compile/render_dom/wrappers/Element/index.ts

Lines changed: 2 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import { Identifier } from 'estree';
2626
import EventHandler from './EventHandler';
2727
import { extract_names } from 'periscopic';
2828
import Action from '../../../nodes/Action';
29+
import create_slot_block from './create_slot_block';
2930

3031
const events = [
3132
{
@@ -170,47 +171,7 @@ export default class ElementWrapper extends Wrapper {
170171

171172
this.attributes = this.node.attributes.map(attribute => {
172173
if (attribute.name === 'slot') {
173-
// TODO make separate subclass for this?
174-
let owner = this.parent;
175-
while (owner) {
176-
if (owner.node.type === 'InlineComponent') {
177-
break;
178-
}
179-
180-
if (owner.node.type === 'Element' && /-/.test(owner.node.name)) {
181-
break;
182-
}
183-
184-
owner = owner.parent;
185-
}
186-
187-
if (owner && owner.node.type === 'InlineComponent') {
188-
const name = attribute.get_static_value() as string;
189-
190-
if (!(owner as unknown as InlineComponentWrapper).slots.has(name)) {
191-
const child_block = block.child({
192-
comment: create_debugging_comment(node, this.renderer.component),
193-
name: this.renderer.component.get_unique_name(`create_${sanitize(name)}_slot`),
194-
type: 'slot'
195-
});
196-
197-
const { scope, lets } = this.node;
198-
const seen = new Set(lets.map(l => l.name.name));
199-
200-
(owner as unknown as InlineComponentWrapper).node.lets.forEach(l => {
201-
if (!seen.has(l.name.name)) lets.push(l);
202-
});
203-
204-
(owner as unknown as InlineComponentWrapper).slots.set(
205-
name,
206-
get_slot_definition(child_block, scope, lets)
207-
);
208-
this.renderer.blocks.push(child_block);
209-
}
210-
211-
this.slot_block = (owner as unknown as InlineComponentWrapper).slots.get(name).block;
212-
block = this.slot_block;
213-
}
174+
block = create_slot_block(attribute, this, block);
214175
}
215176
if (attribute.name === 'style') {
216177
return new StyleAttributeWrapper(this, block, attribute);

src/compiler/compile/render_dom/wrappers/Slot.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,12 @@ import get_slot_data from '../../utils/get_slot_data';
1010
import Expression from '../../nodes/shared/Expression';
1111
import is_dynamic from './shared/is_dynamic';
1212
import { Identifier, ObjectExpression } from 'estree';
13+
import create_slot_block from './Element/create_slot_block'
1314

1415
export default class SlotWrapper extends Wrapper {
1516
node: Slot;
1617
fragment: FragmentWrapper;
18+
slot_block: Block;
1719

1820
var: Identifier = { type: 'Identifier', name: 'slot' };
1921
dependencies: Set<string> = new Set(['$$scope']);
@@ -30,6 +32,10 @@ export default class SlotWrapper extends Wrapper {
3032
this.cannot_use_innerhtml();
3133
this.not_static_content();
3234

35+
if (this.node.values.has('slot')) {
36+
block = create_slot_block(this.node.values.get('slot'), this, block);
37+
}
38+
3339
this.fragment = new FragmentWrapper(
3440
renderer,
3541
block,
@@ -59,6 +65,10 @@ export default class SlotWrapper extends Wrapper {
5965

6066
const { slot_name } = this.node;
6167

68+
if (this.slot_block) {
69+
block = this.slot_block;
70+
}
71+
6272
let get_slot_changes_fn;
6373
let get_slot_context_fn;
6474

src/compiler/compile/render_ssr/handlers/Slot.ts

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,18 @@ import Renderer, { RenderOptions } from '../Renderer';
22
import Slot from '../../nodes/Slot';
33
import { x } from 'code-red';
44
import get_slot_data from '../../utils/get_slot_data';
5+
import { get_slot_scope } from './shared/get_slot_scope';
56

6-
export default function(node: Slot, renderer: Renderer, options: RenderOptions) {
7+
export default function(node: Slot, renderer: Renderer, options: RenderOptions & {
8+
slot_scopes: Map<any, any>;
9+
}) {
710
const slot_data = get_slot_data(node.values);
11+
const slot = node.get_static_attribute_value('slot');
12+
const nearest_inline_component = node.find_nearest(/InlineComponent/);
13+
14+
if (slot && nearest_inline_component) {
15+
renderer.push();
16+
}
817

918
renderer.push();
1019
renderer.render(node.children, options);
@@ -15,4 +24,17 @@ export default function(node: Slot, renderer: Renderer, options: RenderOptions)
1524
? $$slots.${node.slot_name}(${slot_data})
1625
: ${result}
1726
`);
27+
28+
if (slot && nearest_inline_component) {
29+
const lets = node.lets;
30+
const seen = new Set(lets.map(l => l.name.name));
31+
32+
nearest_inline_component.lets.forEach(l => {
33+
if (!seen.has(l.name.name)) lets.push(l);
34+
});
35+
options.slot_scopes.set(slot, {
36+
input: get_slot_scope(node.lets),
37+
output: renderer.pop()
38+
});
39+
}
1840
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<script>
2+
import Two from './Two.svelte';
3+
export let a, b;
4+
</script>
5+
6+
<Two {b} let:two>
7+
<slot name="one" slot="two" one={a} two={two}></slot>
8+
</Two>
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<script>
2+
export let b;
3+
</script>
4+
<slot name="two" two={b}></slot>
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
export default {
2+
html: `
3+
<p slot='one'>one: 1 two: 2</p>
4+
`,
5+
test({ assert, component, target }) {
6+
component.a = 3;
7+
component.b = 4;
8+
assert.htmlEqual(target.innerHTML, `
9+
<p slot='one'>one: 3 two: 4</p>
10+
`);
11+
12+
component.a = 5;
13+
component.b = 6;
14+
assert.htmlEqual(target.innerHTML, `
15+
<p slot='one'>one: 5 two: 6</p>
16+
`);
17+
}
18+
};
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<script>
2+
import One from './One.svelte';
3+
export let a = 1;
4+
export let b = 2;
5+
</script>
6+
7+
<One {a} {b}>
8+
<p slot='one' let:one let:two>one: {one} two: {two}</p>
9+
</One>

0 commit comments

Comments
 (0)