allow <slot> to be part of a slot (#4295)

pull/5472/head
Tan Li Hau 5 years ago committed by GitHub
parent 0645631b28
commit 8056829a91
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -2,6 +2,7 @@
## Unreleased
* Support `<slot slot="...">` ([#2079](https://github.com/sveltejs/svelte/issues/2079))
* Add `EventSource` to known globals ([#5463](https://github.com/sveltejs/svelte/issues/5463))
* Fix compiler exception with `~`/`+` combinators and `{...spread}` attributes ([#5465](https://github.com/sveltejs/svelte/issues/5465))

@ -0,0 +1,61 @@
import ElementWrapper from './index';
import SlotWrapper from '../Slot';
import Block from '../../Block';
import { sanitize } from '../../../../utils/names';
import InlineComponentWrapper from '../InlineComponent';
import create_debugging_comment from '../shared/create_debugging_comment';
import { get_slot_definition } from '../shared/get_slot_definition';
export default function create_slot_block(attribute, element: ElementWrapper | SlotWrapper, block: Block) {
const owner = find_slot_owner(element.parent);
if (owner && owner.node.type === 'InlineComponent') {
const name = attribute.get_static_value() as string;
if (!((owner as unknown) as InlineComponentWrapper).slots.has(name)) {
const child_block = block.child({
comment: create_debugging_comment(element.node, element.renderer.component),
name: element.renderer.component.get_unique_name(
`create_${sanitize(name)}_slot`
),
type: 'slot'
});
const { scope, lets } = element.node;
const seen = new Set(lets.map(l => l.name.name));
((owner as unknown) as InlineComponentWrapper).node.lets.forEach(l => {
if (!seen.has(l.name.name)) lets.push(l);
});
((owner as unknown) as InlineComponentWrapper).slots.set(
name,
get_slot_definition(child_block, scope, lets)
);
element.renderer.blocks.push(child_block);
}
element.slot_block = ((owner as unknown) as InlineComponentWrapper).slots.get(
name
).block;
return element.slot_block;
}
return block;
}
function find_slot_owner(owner) {
while (owner) {
if (owner.node.type === 'InlineComponent') {
break;
}
if (owner.node.type === 'Element' && /-/.test(owner.node.name)) {
break;
}
owner = owner.parent;
}
return owner;
}

@ -2,7 +2,7 @@ import Renderer from '../../Renderer';
import Element from '../../../nodes/Element';
import Wrapper from '../shared/Wrapper';
import Block from '../../Block';
import { is_void, sanitize } from '../../../../utils/names';
import { is_void } from '../../../../utils/names';
import FragmentWrapper from '../Fragment';
import { escape_html, string_literal } from '../../../utils/stringify';
import TextWrapper from '../Text';
@ -14,12 +14,9 @@ import StyleAttributeWrapper from './StyleAttribute';
import SpreadAttributeWrapper from './SpreadAttribute';
import { dimensions } from '../../../../utils/patterns';
import Binding from './Binding';
import InlineComponentWrapper from '../InlineComponent';
import add_to_set from '../../../utils/add_to_set';
import { add_event_handler } from '../shared/add_event_handlers';
import { add_action } from '../shared/add_actions';
import create_debugging_comment from '../shared/create_debugging_comment';
import { get_slot_definition } from '../shared/get_slot_definition';
import bind_this from '../shared/bind_this';
import { is_head } from '../shared/is_head';
import { Identifier } from 'estree';
@ -28,6 +25,7 @@ import { extract_names } from 'periscopic';
import Action from '../../../nodes/Action';
import MustacheTagWrapper from '../MustacheTag';
import RawMustacheTagWrapper from '../RawMustacheTag';
import create_slot_block from './create_slot_block';
interface BindingGroup {
events: string[];
@ -177,47 +175,7 @@ export default class ElementWrapper extends Wrapper {
this.attributes = this.node.attributes.map(attribute => {
if (attribute.name === 'slot') {
// TODO make separate subclass for this?
let owner = this.parent;
while (owner) {
if (owner.node.type === 'InlineComponent') {
break;
}
if (owner.node.type === 'Element' && /-/.test(owner.node.name)) {
break;
}
owner = owner.parent;
}
if (owner && owner.node.type === 'InlineComponent') {
const name = attribute.get_static_value() as string;
if (!(owner as unknown as InlineComponentWrapper).slots.has(name)) {
const child_block = block.child({
comment: create_debugging_comment(node, this.renderer.component),
name: this.renderer.component.get_unique_name(`create_${sanitize(name)}_slot`),
type: 'slot'
});
const { scope, lets } = this.node;
const seen = new Set(lets.map(l => l.name.name));
(owner as unknown as InlineComponentWrapper).node.lets.forEach(l => {
if (!seen.has(l.name.name)) lets.push(l);
});
(owner as unknown as InlineComponentWrapper).slots.set(
name,
get_slot_definition(child_block, scope, lets)
);
this.renderer.blocks.push(child_block);
}
this.slot_block = (owner as unknown as InlineComponentWrapper).slots.get(name).block;
block = this.slot_block;
}
block = create_slot_block(attribute, this, block);
}
if (attribute.name === 'style') {
return new StyleAttributeWrapper(this, block, attribute);

@ -12,11 +12,13 @@ import Expression from '../../nodes/shared/Expression';
import is_dynamic from './shared/is_dynamic';
import { Identifier, ObjectExpression } from 'estree';
import create_debugging_comment from './shared/create_debugging_comment';
import create_slot_block from './Element/create_slot_block';
export default class SlotWrapper extends Wrapper {
node: Slot;
fragment: FragmentWrapper;
fallback: Block | null = null;
slot_block: Block;
var: Identifier = { type: 'Identifier', name: 'slot' };
dependencies: Set<string> = new Set(['$$scope']);
@ -42,6 +44,10 @@ export default class SlotWrapper extends Wrapper {
renderer.blocks.push(this.fallback);
}
if (this.node.values.has('slot')) {
block = create_slot_block(this.node.values.get('slot'), this, block);
}
this.fragment = new FragmentWrapper(
renderer,
this.fallback,
@ -71,6 +77,10 @@ export default class SlotWrapper extends Wrapper {
const { slot_name } = this.node;
if (this.slot_block) {
block = this.slot_block;
}
let get_slot_changes_fn;
let get_slot_context_fn;

@ -2,9 +2,18 @@ import Renderer, { RenderOptions } from '../Renderer';
import Slot from '../../nodes/Slot';
import { x } from 'code-red';
import get_slot_data from '../../utils/get_slot_data';
import { get_slot_scope } from './shared/get_slot_scope';
export default function(node: Slot, renderer: Renderer, options: RenderOptions) {
export default function(node: Slot, renderer: Renderer, options: RenderOptions & {
slot_scopes: Map<any, any>;
}) {
const slot_data = get_slot_data(node.values);
const slot = node.get_static_attribute_value('slot');
const nearest_inline_component = node.find_nearest(/InlineComponent/);
if (slot && nearest_inline_component) {
renderer.push();
}
renderer.push();
renderer.render(node.children, options);
@ -15,4 +24,17 @@ export default function(node: Slot, renderer: Renderer, options: RenderOptions)
? #slots.${node.slot_name}(${slot_data})
: ${result}
`);
if (slot && nearest_inline_component) {
const lets = node.lets;
const seen = new Set(lets.map(l => l.name.name));
nearest_inline_component.lets.forEach(l => {
if (!seen.has(l.name.name)) lets.push(l);
});
options.slot_scopes.set(slot, {
input: get_slot_scope(node.lets),
output: renderer.pop()
});
}
}

@ -0,0 +1,8 @@
<script>
import Two from './Two.svelte';
export let a, b;
</script>
<Two {b} let:two>
<slot name="one" slot="two" one={a} two={two}></slot>
</Two>

@ -0,0 +1,4 @@
<script>
export let b;
</script>
<slot name="two" two={b}></slot>

@ -0,0 +1,18 @@
export default {
html: `
<p slot='one'>one: 1 two: 2</p>
`,
test({ assert, component, target }) {
component.a = 3;
component.b = 4;
assert.htmlEqual(target.innerHTML, `
<p slot='one'>one: 3 two: 4</p>
`);
component.a = 5;
component.b = 6;
assert.htmlEqual(target.innerHTML, `
<p slot='one'>one: 5 two: 6</p>
`);
}
};

@ -0,0 +1,9 @@
<script>
import One from './One.svelte';
export let a = 1;
export let b = 2;
</script>
<One {a} {b}>
<p slot='one' let:one let:two>one: {one} two: {two}</p>
</One>
Loading…
Cancel
Save