mirror of https://github.com/sveltejs/svelte
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
203 lines
6.0 KiB
203 lines
6.0 KiB
import Wrapper from './shared/Wrapper';
|
|
import Renderer from '../Renderer';
|
|
import Block from '../Block';
|
|
import Slot from '../../nodes/Slot';
|
|
import FragmentWrapper from './Fragment';
|
|
import { b, p, x } from 'code-red';
|
|
import { sanitize } from '../../../utils/names';
|
|
import add_to_set from '../../utils/add_to_set';
|
|
import get_slot_data from '../../utils/get_slot_data';
|
|
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';
|
|
|
|
export default class SlotWrapper extends Wrapper {
|
|
node: Slot;
|
|
fragment: FragmentWrapper;
|
|
fallback: Block | null = null;
|
|
|
|
var: Identifier = { type: 'Identifier', name: 'slot' };
|
|
dependencies: Set<string> = new Set(['$$scope']);
|
|
|
|
constructor(
|
|
renderer: Renderer,
|
|
block: Block,
|
|
parent: Wrapper,
|
|
node: Slot,
|
|
strip_whitespace: boolean,
|
|
next_sibling: Wrapper
|
|
) {
|
|
super(renderer, block, parent, node);
|
|
this.cannot_use_innerhtml();
|
|
this.not_static_content();
|
|
|
|
if (this.node.children.length) {
|
|
this.fallback = block.child({
|
|
comment: create_debugging_comment(this.node.children[0], this.renderer.component),
|
|
name: this.renderer.component.get_unique_name(`fallback_block`),
|
|
type: 'fallback'
|
|
});
|
|
}
|
|
|
|
this.fragment = new FragmentWrapper(
|
|
renderer,
|
|
this.fallback,
|
|
node.children,
|
|
parent,
|
|
strip_whitespace,
|
|
next_sibling
|
|
);
|
|
|
|
this.node.values.forEach(attribute => {
|
|
add_to_set(this.dependencies, attribute.dependencies);
|
|
});
|
|
|
|
block.add_dependencies(this.dependencies);
|
|
|
|
// we have to do this, just in case
|
|
block.add_intro();
|
|
block.add_outro();
|
|
}
|
|
|
|
render(
|
|
block: Block,
|
|
parent_node: Identifier,
|
|
parent_nodes: Identifier
|
|
) {
|
|
const { renderer } = this;
|
|
|
|
const { slot_name } = this.node;
|
|
|
|
let get_slot_changes_fn;
|
|
let get_slot_context_fn;
|
|
|
|
if (this.node.values.size > 0) {
|
|
get_slot_changes_fn = renderer.component.get_unique_name(`get_${sanitize(slot_name)}_slot_changes`);
|
|
get_slot_context_fn = renderer.component.get_unique_name(`get_${sanitize(slot_name)}_slot_context`);
|
|
|
|
const changes = x`{}` as ObjectExpression;
|
|
|
|
const dependencies = new Set();
|
|
|
|
this.node.values.forEach(attribute => {
|
|
attribute.chunks.forEach(chunk => {
|
|
if ((chunk as Expression).dependencies) {
|
|
add_to_set(dependencies, (chunk as Expression).contextual_dependencies);
|
|
|
|
// add_to_set(dependencies, (chunk as Expression).dependencies);
|
|
(chunk as Expression).dependencies.forEach(name => {
|
|
const variable = renderer.component.var_lookup.get(name);
|
|
if (variable && !variable.hoistable) dependencies.add(name);
|
|
});
|
|
}
|
|
});
|
|
|
|
const dynamic_dependencies = Array.from(attribute.dependencies).filter(name => {
|
|
if (this.node.scope.is_let(name)) return true;
|
|
const variable = renderer.component.var_lookup.get(name);
|
|
return is_dynamic(variable);
|
|
});
|
|
|
|
if (dynamic_dependencies.length > 0) {
|
|
changes.properties.push(p`${attribute.name}: ${renderer.dirty(dynamic_dependencies)}`);
|
|
}
|
|
});
|
|
|
|
renderer.blocks.push(b`
|
|
const ${get_slot_changes_fn} = #dirty => ${changes};
|
|
const ${get_slot_context_fn} = #ctx => ${get_slot_data(this.node.values, block)};
|
|
`);
|
|
} else {
|
|
get_slot_changes_fn = 'null';
|
|
get_slot_context_fn = 'null';
|
|
}
|
|
|
|
if (this.fallback) {
|
|
this.fragment.render(this.fallback, null, x`#nodes` as Identifier);
|
|
renderer.blocks.push(this.fallback);
|
|
}
|
|
|
|
const slot = block.get_unique_name(`${sanitize(slot_name)}_slot`);
|
|
const slot_definition = block.get_unique_name(`${sanitize(slot_name)}_slot_template`);
|
|
const slot_or_fallback = this.fallback ? block.get_unique_name(`${sanitize(slot_name)}_slot_or_fallback`) : slot;
|
|
|
|
block.chunks.init.push(b`
|
|
const ${slot_definition} = ${renderer.reference('$$slots')}.${slot_name};
|
|
const ${slot} = @create_slot(${slot_definition}, #ctx, ${renderer.reference('$$scope')}, ${get_slot_context_fn});
|
|
${this.fallback ? b`const ${slot_or_fallback} = ${slot} || ${this.fallback.name}(#ctx);` : null}
|
|
`);
|
|
|
|
block.chunks.create.push(
|
|
b`if (${slot_or_fallback}) ${slot_or_fallback}.c();`
|
|
);
|
|
|
|
if (renderer.options.hydratable) {
|
|
block.chunks.claim.push(
|
|
b`if (${slot_or_fallback}) ${slot_or_fallback}.l(${parent_nodes});`
|
|
);
|
|
}
|
|
|
|
block.chunks.mount.push(b`
|
|
if (${slot_or_fallback}) {
|
|
${slot_or_fallback}.m(${parent_node || '#target'}, ${parent_node ? 'null' : 'anchor'});
|
|
}
|
|
`);
|
|
|
|
block.chunks.intro.push(
|
|
b`@transition_in(${slot_or_fallback}, #local);`
|
|
);
|
|
|
|
block.chunks.outro.push(
|
|
b`@transition_out(${slot_or_fallback}, #local);`
|
|
);
|
|
|
|
const is_dependency_dynamic = name => {
|
|
if (name === '$$scope') return true;
|
|
if (this.node.scope.is_let(name)) return true;
|
|
const variable = renderer.component.var_lookup.get(name);
|
|
return is_dynamic(variable);
|
|
};
|
|
|
|
const dynamic_dependencies = Array.from(this.dependencies).filter(is_dependency_dynamic);
|
|
|
|
const fallback_dynamic_dependencies = this.fallback
|
|
? Array.from(this.fallback.dependencies).filter(is_dependency_dynamic)
|
|
: [];
|
|
|
|
const slot_update = b`
|
|
if (${slot}.p && ${renderer.dirty(dynamic_dependencies)}) {
|
|
${slot}.p(
|
|
@get_slot_context(${slot_definition}, #ctx, ${renderer.reference('$$scope')}, ${get_slot_context_fn}),
|
|
@get_slot_changes(${slot_definition}, ${renderer.reference('$$scope')}, #dirty, ${get_slot_changes_fn})
|
|
);
|
|
}
|
|
`;
|
|
const fallback_update = this.fallback && fallback_dynamic_dependencies.length > 0 && b`
|
|
if (${slot_or_fallback} && ${slot_or_fallback}.p && ${renderer.dirty(fallback_dynamic_dependencies)}) {
|
|
${slot_or_fallback}.p(#ctx, #dirty);
|
|
}
|
|
`;
|
|
|
|
if (fallback_update) {
|
|
block.chunks.update.push(b`
|
|
if (${slot}) {
|
|
${slot_update}
|
|
} else {
|
|
${fallback_update}
|
|
}
|
|
`);
|
|
} else {
|
|
block.chunks.update.push(b`
|
|
if (${slot}) {
|
|
${slot_update}
|
|
}
|
|
`);
|
|
}
|
|
|
|
block.chunks.destroy.push(
|
|
b`if (${slot_or_fallback}) ${slot_or_fallback}.d(detaching);`
|
|
);
|
|
}
|
|
}
|