Implement dynamic slot attribute

pull/8535/head
Nguyen Tran 3 years ago
parent 2f32f948ee
commit ae02d4720a

@ -529,10 +529,6 @@ export default class Element extends Node {
} }
if (name === 'slot') { if (name === 'slot') {
if (!attribute.is_static) {
return component.error(attribute, compiler_errors.invalid_slot_attribute);
}
if (component.slot_outlets.has(name)) { if (component.slot_outlets.has(name)) {
return component.error(attribute, compiler_errors.duplicate_slot_attribute(name)); return component.error(attribute, compiler_errors.duplicate_slot_attribute(name));

@ -16,6 +16,7 @@ export default class SlotTemplate extends Node {
const_tags: ConstTag[]; const_tags: ConstTag[];
slot_attribute: Attribute; slot_attribute: Attribute;
slot_template_name: string = 'default'; slot_template_name: string = 'default';
is_static: boolean = true;
constructor( constructor(
component: Component, component: Component,
@ -44,14 +45,17 @@ export default class SlotTemplate extends Node {
case 'Attribute': { case 'Attribute': {
if (node.name === 'slot') { if (node.name === 'slot') {
this.slot_attribute = new Attribute(component, this, scope, node); this.slot_attribute = new Attribute(component, this, scope, node);
if (!this.slot_attribute.is_static) { if (this.slot_attribute.is_static) {
return component.error(node, compiler_errors.invalid_slot_attribute); const value = this.slot_attribute.get_static_value();
if (typeof value === 'boolean') {
return component.error(node, compiler_errors.invalid_slot_attribute_value_missing);
}
this.slot_template_name = value as string;
this.is_static = true;
} else {
this.slot_template_name = component.get_unique_name('dynamic_slot_template').name;
this.is_static = false;
} }
const value = this.slot_attribute.get_static_value();
if (typeof value === 'boolean') {
return component.error(node, compiler_errors.invalid_slot_attribute_value_missing);
}
this.slot_template_name = value as string;
break; break;
} }
throw new Error(`Invalid attribute '${node.name}' in <svelte:fragment>`); throw new Error(`Invalid attribute '${node.name}' in <svelte:fragment>`);

@ -29,7 +29,8 @@ const regex_invalid_variable_identifier_characters = /[^a-zA-Z_$]/g;
export default class InlineComponentWrapper extends Wrapper { export default class InlineComponentWrapper extends Wrapper {
var: Identifier; var: Identifier;
slots: Map<string, SlotDefinition> = new Map(); slots: Map<SlotTemplate, SlotDefinition> = new Map();
staic_slot_names: Set<string> = new Set();
node: InlineComponent; node: InlineComponent;
fragment: FragmentWrapper; fragment: FragmentWrapper;
children: Array<Wrapper | FragmentWrapper> = []; children: Array<Wrapper | FragmentWrapper> = [];
@ -95,14 +96,20 @@ export default class InlineComponentWrapper extends Wrapper {
block.add_outro(); block.add_outro();
} }
set_slot(name: string, slot_definition: SlotDefinition) { set_slot(slot: SlotTemplate, slot_definition: SlotDefinition) {
if (this.slots.has(name)) { if (slot.is_static) {
if (name === 'default') { const name = slot.slot_template_name;
throw new Error('Found elements without slot attribute when using slot="default"'); if (this.staic_slot_names.has(name)) {
if (name === 'default') {
throw new Error('Found elements without slot attribute when using slot="default"');
}
throw new Error(`Duplicate slot name "${name}" in <${this.node.name}>`);
} else {
this.staic_slot_names.add(name);
} }
throw new Error(`Duplicate slot name "${name}" in <${this.node.name}>`);
} }
this.slots.set(name, slot_definition);
this.slots.set(slot, slot_definition);
} }
warn_if_reactive() { warn_if_reactive() {
@ -167,8 +174,10 @@ export default class InlineComponentWrapper extends Wrapper {
const initial_props = this.slots.size > 0 const initial_props = this.slots.size > 0
? [ ? [
p`$$slots: { p`$$slots: {
${Array.from(this.slots).map(([name, slot]) => { ${Array.from(this.slots).map(([slot_template, slot]) => {
return p`${name}: [${slot.block.name}, ${slot.get_context || null}, ${slot.get_changes || null}]`; const { is_static, slot_template_name, slot_attribute } = slot_template;
const slot_expression = is_static ? { type: 'Literal', value: slot_template_name } : slot_attribute.get_value(block);
return p`[${slot_expression}]: [${slot.block.name}, ${slot.get_context || null}, ${slot.get_changes || null}]`;
})} })}
}`, }`,
p`$$scope: { p`$$scope: {

@ -52,10 +52,7 @@ export default class SlotTemplateWrapper extends Wrapper {
if (!seen.has(l.name.name)) lets.push(l); if (!seen.has(l.name.name)) lets.push(l);
}); });
this.parent.set_slot( this.parent.set_slot(this.node, get_slot_definition(this.block, scope, lets));
slot_template_name,
get_slot_definition(this.block, scope, lets)
);
this.fragment = new FragmentWrapper( this.fragment = new FragmentWrapper(
renderer, renderer,

@ -77,9 +77,9 @@ export default function(node: InlineComponent, renderer: Renderer, options: Rend
slot_scopes slot_scopes
})); }));
slot_scopes.forEach(({ input, output, statements }, name) => { slot_scopes.forEach(({ input, output, statements }, slot_exp) => {
slot_fns.push( slot_fns.push(
p`${name}: (${input}) => { ${statements}; return ${output}; }` p`[${slot_exp}]: (${input}) => { ${statements}; return ${output}; }`
); );
}); });
} }

@ -9,7 +9,7 @@ export default function(node: Slot, renderer: Renderer, options: RenderOptions &
slot_scopes: Map<any, any>; slot_scopes: Map<any, any>;
}) { }) {
const slot_data = get_slot_data(node.values); const slot_data = get_slot_data(node.values);
const slot = node.get_static_attribute_value('slot'); const slot = node.values.get('slot')?.get_value(null);
const nearest_inline_component = node.find_nearest(/InlineComponent/); const nearest_inline_component = node.find_nearest(/InlineComponent/);
if (slot && nearest_inline_component) { if (slot && nearest_inline_component) {

@ -29,7 +29,8 @@ export default function(node: SlotTemplate, renderer: Renderer, options: RenderO
throw new Error(`Duplicate slot name "${node.slot_template_name}" in <${parent_inline_component.name}>`); throw new Error(`Duplicate slot name "${node.slot_template_name}" in <${parent_inline_component.name}>`);
} }
options.slot_scopes.set(node.slot_template_name, { const slot_expression = node.is_static ? { type: 'Literal', value: node.slot_template_name } : node.slot_attribute.get_value(null);
options.slot_scopes.set(slot_expression, {
input: get_slot_scope(node.lets), input: get_slot_scope(node.lets),
output: slot_fragment_content, output: slot_fragment_content,
statements: get_const_tags(node.const_tags) statements: get_const_tags(node.const_tags)

Loading…
Cancel
Save