import Wrapper from './shared/Wrapper'; import Renderer from '../Renderer'; import Block from '../Block'; import Slot from '../../nodes/Slot'; import FragmentWrapper from './Fragment'; import deindent from '../../utils/deindent'; import { sanitize, quote_prop_if_necessary } from '../../../utils/names'; import add_to_set from '../../utils/add_to_set'; import get_slot_data from '../../utils/get_slot_data'; import { stringify_props } from '../../utils/stringify_props'; import Expression from '../../nodes/shared/Expression'; export default class SlotWrapper extends Wrapper { node: Slot; fragment: FragmentWrapper; var = '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.fragment = new FragmentWrapper( renderer, block, 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: string, parent_nodes: string ) { const { renderer } = this; const { slot_name } = this.node; let get_slot_changes; let get_slot_context; if (this.node.values.size > 0) { get_slot_changes = renderer.component.get_unique_name(`get_${sanitize(slot_name)}_slot_changes`); get_slot_context = renderer.component.get_unique_name(`get_${sanitize(slot_name)}_slot_context`); const context_props = get_slot_data(this.node.values, false); const changes_props = []; 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).dependencies); add_to_set(dependencies, (chunk as Expression).contextual_dependencies); } }); if (attribute.dependencies.size > 0) { changes_props.push(`${attribute.name}: ${[...attribute.dependencies].join(' || ')}`); } }); const arg = dependencies.size > 0 ? `{ ${Array.from(dependencies).join(', ')} }` : '{}'; renderer.blocks.push(deindent` const ${get_slot_changes} = (${arg}) => (${stringify_props(changes_props)}); const ${get_slot_context} = (${arg}) => (${stringify_props(context_props)}); `); } else { get_slot_changes = 'null'; get_slot_context = 'null'; } const slot = block.get_unique_name(`${sanitize(slot_name)}_slot`); const slot_definition = block.get_unique_name(`${sanitize(slot_name)}_slot`); block.builders.init.add_block(deindent` const ${slot_definition} = ctx.$$slots${quote_prop_if_necessary(slot_name)}; const ${slot} = @create_slot(${slot_definition}, ctx, ${get_slot_context}); `); const mount_before = block.builders.mount.toString(); block.builders.create.push_condition(`!${slot}`); block.builders.claim.push_condition(`!${slot}`); block.builders.hydrate.push_condition(`!${slot}`); block.builders.mount.push_condition(`!${slot}`); block.builders.update.push_condition(`!${slot}`); block.builders.destroy.push_condition(`!${slot}`); const listeners = block.event_listeners; block.event_listeners = []; this.fragment.render(block, parent_node, parent_nodes); block.render_listeners(`_${slot}`); block.event_listeners = listeners; block.builders.create.pop_condition(); block.builders.claim.pop_condition(); block.builders.hydrate.pop_condition(); block.builders.mount.pop_condition(); block.builders.update.pop_condition(); block.builders.destroy.pop_condition(); block.builders.create.add_line( `if (${slot}) ${slot}.c();` ); block.builders.claim.add_line( `if (${slot}) ${slot}.l(${parent_nodes});` ); const mount_leadin = block.builders.mount.toString() !== mount_before ? `else` : `if (${slot})`; block.builders.mount.add_block(deindent` ${mount_leadin} { ${slot}.m(${parent_node || '#target'}, ${parent_node ? 'null' : 'anchor'}); } `); block.builders.intro.add_line( `if (${slot} && ${slot}.i) ${slot}.i(#local);` ); block.builders.outro.add_line( `if (${slot} && ${slot}.o) ${slot}.o(#local);` ); let update_conditions = [...this.dependencies].map(name => `changed.${name}`).join(' || '); if (this.dependencies.size > 1) update_conditions = `(${update_conditions})`; block.builders.update.add_block(deindent` if (${slot} && ${slot}.p && ${update_conditions}) { ${slot}.p(@get_slot_changes(${slot_definition}, ctx, changed, ${get_slot_changes}), @get_slot_context(${slot_definition}, ctx, ${get_slot_context})); } `); block.builders.destroy.add_line( `if (${slot}) ${slot}.d(detaching);` ); } }