From efe25555cfa449c230e31123bb3af4ea6dd2e336 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Sat, 26 Aug 2017 16:30:04 -0400 Subject: [PATCH] client-side named slots --- src/generators/dom/index.ts | 2 +- src/generators/dom/interfaces.ts | 13 +++++++++++++ src/generators/dom/preprocess.ts | 9 +-------- src/generators/dom/visit.ts | 5 +++-- src/generators/dom/visitors/Component.ts | 5 +++-- src/generators/dom/visitors/EachBlock.ts | 7 ++++--- .../dom/visitors/Element/Element.ts | 18 ++++++++++++------ src/generators/dom/visitors/IfBlock.ts | 19 +++++++++++-------- src/generators/dom/visitors/Slot.ts | 11 +++++++++-- src/generators/dom/visitors/index.ts | 5 ++++- .../samples/component-slot-named/Nested.html | 5 +++++ .../samples/component-slot-named/_config.js | 10 ++++++++++ .../samples/component-slot-named/main.html | 16 ++++++++++++++++ 13 files changed, 92 insertions(+), 33 deletions(-) create mode 100644 test/runtime/samples/component-slot-named/Nested.html create mode 100644 test/runtime/samples/component-slot-named/_config.js create mode 100644 test/runtime/samples/component-slot-named/main.html diff --git a/src/generators/dom/index.ts b/src/generators/dom/index.ts index eddeba3906..2619a527a4 100644 --- a/src/generators/dom/index.ts +++ b/src/generators/dom/index.ts @@ -72,7 +72,7 @@ export default function dom( generator.stylesheet.warnOnUnusedSelectors(options.onwarn); parsed.html.children.forEach((node: Node) => { - visit(generator, block, state, node, []); + visit(generator, block, state, node, [], []); }); const builder = new CodeBuilder(); diff --git a/src/generators/dom/interfaces.ts b/src/generators/dom/interfaces.ts index 858680b7df..37dc449d1d 100644 --- a/src/generators/dom/interfaces.ts +++ b/src/generators/dom/interfaces.ts @@ -1,3 +1,7 @@ +import { DomGenerator } from './index'; +import Block from './Block'; +import { Node } from '../../interfaces'; + export interface State { name?: string; namespace: string; @@ -11,3 +15,12 @@ export interface State { usesComponent?: boolean; selectBindingDependencies?: string[]; } + +export type Visitor = ( + generator: DomGenerator, + block: Block, + state: State, + node: Node, + elementStack: Node[], + componentStack: Node[] +) => void; \ No newline at end of file diff --git a/src/generators/dom/preprocess.ts b/src/generators/dom/preprocess.ts index 2ca548a801..47593f250f 100644 --- a/src/generators/dom/preprocess.ts +++ b/src/generators/dom/preprocess.ts @@ -348,7 +348,7 @@ const preprocessors = { if (slot) { // TODO validate slots — no nesting, no dynamic names... const component = componentStack[componentStack.length - 1]; - component._slots.add(slot); + component._slots.add(slot.value[0].data); } const name = block.getUniqueName( @@ -376,16 +376,9 @@ const preprocessors = { (node.name === ':Self' ? generator.name : node.name).toLowerCase() ); - // node._block = block.child({ - // name: generator.getUniqueName(`create_${name}_yield_fragment`), - // }); - if (node.children) node._slots = new Set(['default']); // TODO only include default if there are unslotted children - // generator.blocks.push(node._block); preprocessChildren(generator, block, node._state, node, inEachBlock, elementStack, componentStack.concat(node), stripWhitespace, nextSibling); - // block.addDependencies(node._block.dependencies); - // node._block.hasUpdateMethod = node._block.dependencies.size > 0; } else { if (node.name === 'pre' || node.name === 'textarea') stripWhitespace = false; preprocessChildren(generator, block, node._state, node, inEachBlock, elementStack.concat(node), componentStack, stripWhitespace, nextSibling); diff --git a/src/generators/dom/visit.ts b/src/generators/dom/visit.ts index 82fc23c03b..c5e8f0bed4 100644 --- a/src/generators/dom/visit.ts +++ b/src/generators/dom/visit.ts @@ -9,8 +9,9 @@ export default function visit( block: Block, state: State, node: Node, - elementStack: Node[] + elementStack: Node[], + componentStack: Node[] ) { const visitor = visitors[node.type]; - visitor(generator, block, state, node, elementStack); + visitor(generator, block, state, node, elementStack, componentStack); } diff --git a/src/generators/dom/visitors/Component.ts b/src/generators/dom/visitors/Component.ts index ae01ea2f37..6757de1a8a 100644 --- a/src/generators/dom/visitors/Component.ts +++ b/src/generators/dom/visitors/Component.ts @@ -43,7 +43,8 @@ export default function visitComponent( block: Block, state: State, node: Node, - elementStack: Node[] + elementStack: Node[], + componentStack: Node[] ) { generator.hasComponents = true; @@ -56,7 +57,7 @@ export default function visitComponent( componentInitProperties.push(`slots: { ${slots.join(', ')} }`); node.children.forEach((child: Node) => { - visit(generator, block, node._state, child, elementStack); + visit(generator, block, node._state, child, elementStack, componentStack.concat(node)); }); } diff --git a/src/generators/dom/visitors/EachBlock.ts b/src/generators/dom/visitors/EachBlock.ts index 1c8a9c9346..ee4796946d 100644 --- a/src/generators/dom/visitors/EachBlock.ts +++ b/src/generators/dom/visitors/EachBlock.ts @@ -10,7 +10,8 @@ export default function visitEachBlock( block: Block, state: State, node: Node, - elementStack: Node[] + elementStack: Node[], + componentStack: Node[] ) { const each_block = generator.getUniqueName(`each_block`); const create_each_block = node._block.name; @@ -125,12 +126,12 @@ export default function visitEachBlock( } node.children.forEach((child: Node) => { - visit(generator, node._block, node._state, child, elementStack); + visit(generator, node._block, node._state, child, elementStack, componentStack); }); if (node.else) { node.else.children.forEach((child: Node) => { - visit(generator, node.else._block, node.else._state, child, elementStack); + visit(generator, node.else._block, node.else._state, child, elementStack, componentStack); }); } } diff --git a/src/generators/dom/visitors/Element/Element.ts b/src/generators/dom/visitors/Element/Element.ts index 61ebe6d8df..e5a91d28d4 100644 --- a/src/generators/dom/visitors/Element/Element.ts +++ b/src/generators/dom/visitors/Element/Element.ts @@ -37,7 +37,8 @@ export default function visitElement( block: Block, state: State, node: Node, - elementStack: Node[] + elementStack: Node[], + componentStack: Node[] ) { if (node.name in meta) { return meta[node.name](generator, block, node); @@ -48,13 +49,18 @@ export default function visitElement( } if (generator.components.has(node.name) || node.name === ':Self') { - return visitComponent(generator, block, state, node, elementStack); + return visitComponent(generator, block, state, node, elementStack, componentStack); } const childState = node._state; const name = childState.parentNode; - const isToplevel = !state.parentNode; + const slot = node.attributes.find((attribute: Node) => attribute.name === 'slot'); + const parentNode = slot ? + `${componentStack[componentStack.length - 1]._state.name}._slotted.${slot.value[0].data}` : // TODO this looks bonkers + state.parentNode; + + const isToplevel = !parentNode; block.addVariable(name); block.builders.create.addLine( @@ -77,9 +83,9 @@ export default function visitElement( `); } - if (state.parentNode) { + if (parentNode) { block.builders.mount.addLine( - `@appendNode( ${name}, ${state.parentNode} );` + `@appendNode( ${name}, ${parentNode} );` ); } else { block.builders.mount.addLine(`@insertNode( ${name}, #target, anchor );`); @@ -195,7 +201,7 @@ export default function visitElement( } node.children.forEach((child: Node) => { - visit(generator, block, childState, child, elementStack.concat(node)); + visit(generator, block, childState, child, elementStack.concat(node), componentStack); }); if (node.lateUpdate) { diff --git a/src/generators/dom/visitors/IfBlock.ts b/src/generators/dom/visitors/IfBlock.ts index 193edd872b..e45ab1fb20 100644 --- a/src/generators/dom/visitors/IfBlock.ts +++ b/src/generators/dom/visitors/IfBlock.ts @@ -20,7 +20,8 @@ function getBranches( block: Block, state: State, node: Node, - elementStack: Node[] + elementStack: Node[], + componentStack: Node[] ) { const branches = [ { @@ -32,11 +33,11 @@ function getBranches( }, ]; - visitChildren(generator, block, state, node, elementStack); + visitChildren(generator, block, state, node, elementStack, componentStack); if (isElseIf(node.else)) { branches.push( - ...getBranches(generator, block, state, node.else.children[0], elementStack) + ...getBranches(generator, block, state, node.else.children[0], elementStack, componentStack) ); } else { branches.push({ @@ -48,7 +49,7 @@ function getBranches( }); if (node.else) { - visitChildren(generator, block, state, node.else, elementStack); + visitChildren(generator, block, state, node.else, elementStack, componentStack); } } @@ -60,10 +61,11 @@ function visitChildren( block: Block, state: State, node: Node, - elementStack: Node[] + elementStack: Node[], + componentStack: Node[] ) { node.children.forEach((child: Node) => { - visit(generator, node._block, node._state, child, elementStack); + visit(generator, node._block, node._state, child, elementStack, componentStack); }); } @@ -72,7 +74,8 @@ export default function visitIfBlock( block: Block, state: State, node: Node, - elementStack: Node[] + elementStack: Node[], + componentStack: Node[] ) { const name = generator.getUniqueName(`if_block`); const anchor = node.needsAnchor @@ -80,7 +83,7 @@ export default function visitIfBlock( : (node.next && node.next._state.name) || 'null'; const params = block.params.join(', '); - const branches = getBranches(generator, block, state, node, elementStack); + const branches = getBranches(generator, block, state, node, elementStack, componentStack); const hasElse = isElseBranch(branches[branches.length - 1]); const if_name = hasElse ? '' : `if ( ${name} ) `; diff --git a/src/generators/dom/visitors/Slot.ts b/src/generators/dom/visitors/Slot.ts index b35ad5b4f8..c95398d29c 100644 --- a/src/generators/dom/visitors/Slot.ts +++ b/src/generators/dom/visitors/Slot.ts @@ -11,7 +11,8 @@ export default function visitSlot( block: Block, state: State, node: Node, - elementStack: Node[] + elementStack: Node[], + componentStack: Node[] ) { const slotName = getStaticAttributeValue(node, 'name') || 'default'; const name = block.getUniqueName(`slot_${slotName}`); @@ -27,13 +28,19 @@ export default function visitSlot( state.parentNode ); + if (slotName !== 'default') { + block.builders.hydrate.addBlock(deindent` + @setAttribute(${name}, 'name', '${slotName}'); + `); + } + block.builders.create.pushCondition(`!${content_name}`); block.builders.mount.pushCondition(`!${content_name}`); block.builders.unmount.pushCondition(`!${content_name}`); block.builders.destroy.pushCondition(`!${content_name}`); node.children.forEach((child: Node) => { - visit(generator, block, node._state, child, elementStack.concat(node)); + visit(generator, block, node._state, child, elementStack.concat(node), componentStack); }); block.builders.create.popCondition(); diff --git a/src/generators/dom/visitors/index.ts b/src/generators/dom/visitors/index.ts index c690a2ba9c..3a6a1d02cf 100644 --- a/src/generators/dom/visitors/index.ts +++ b/src/generators/dom/visitors/index.ts @@ -5,8 +5,9 @@ import MustacheTag from './MustacheTag'; import RawMustacheTag from './RawMustacheTag'; import Text from './Text'; import YieldTag from './YieldTag'; +import { Visitor } from '../interfaces'; -export default { +const visitors: Record = { EachBlock, Element, IfBlock, @@ -15,3 +16,5 @@ export default { Text, YieldTag, }; + +export default visitors; \ No newline at end of file diff --git a/test/runtime/samples/component-slot-named/Nested.html b/test/runtime/samples/component-slot-named/Nested.html new file mode 100644 index 0000000000..665555820a --- /dev/null +++ b/test/runtime/samples/component-slot-named/Nested.html @@ -0,0 +1,5 @@ +
+ + + +
\ No newline at end of file diff --git a/test/runtime/samples/component-slot-named/_config.js b/test/runtime/samples/component-slot-named/_config.js new file mode 100644 index 0000000000..17b59a1bd4 --- /dev/null +++ b/test/runtime/samples/component-slot-named/_config.js @@ -0,0 +1,10 @@ +export default { + solo: true, + html: ` +
+ Hello +

bar

+

foo

+
+ ` +}; diff --git a/test/runtime/samples/component-slot-named/main.html b/test/runtime/samples/component-slot-named/main.html new file mode 100644 index 0000000000..40718e261f --- /dev/null +++ b/test/runtime/samples/component-slot-named/main.html @@ -0,0 +1,16 @@ + + Hello + +

foo

+

bar

+
+ +