client-side named slots

pull/787/head
Rich Harris 7 years ago
parent 7a8c8fd577
commit efe25555cf

@ -72,7 +72,7 @@ export default function dom(
generator.stylesheet.warnOnUnusedSelectors(options.onwarn); generator.stylesheet.warnOnUnusedSelectors(options.onwarn);
parsed.html.children.forEach((node: Node) => { parsed.html.children.forEach((node: Node) => {
visit(generator, block, state, node, []); visit(generator, block, state, node, [], []);
}); });
const builder = new CodeBuilder(); const builder = new CodeBuilder();

@ -1,3 +1,7 @@
import { DomGenerator } from './index';
import Block from './Block';
import { Node } from '../../interfaces';
export interface State { export interface State {
name?: string; name?: string;
namespace: string; namespace: string;
@ -11,3 +15,12 @@ export interface State {
usesComponent?: boolean; usesComponent?: boolean;
selectBindingDependencies?: string[]; selectBindingDependencies?: string[];
} }
export type Visitor = (
generator: DomGenerator,
block: Block,
state: State,
node: Node,
elementStack: Node[],
componentStack: Node[]
) => void;

@ -348,7 +348,7 @@ const preprocessors = {
if (slot) { if (slot) {
// TODO validate slots — no nesting, no dynamic names... // TODO validate slots — no nesting, no dynamic names...
const component = componentStack[componentStack.length - 1]; const component = componentStack[componentStack.length - 1];
component._slots.add(slot); component._slots.add(slot.value[0].data);
} }
const name = block.getUniqueName( const name = block.getUniqueName(
@ -376,16 +376,9 @@ const preprocessors = {
(node.name === ':Self' ? generator.name : node.name).toLowerCase() (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 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); 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 { } else {
if (node.name === 'pre' || node.name === 'textarea') stripWhitespace = false; if (node.name === 'pre' || node.name === 'textarea') stripWhitespace = false;
preprocessChildren(generator, block, node._state, node, inEachBlock, elementStack.concat(node), componentStack, stripWhitespace, nextSibling); preprocessChildren(generator, block, node._state, node, inEachBlock, elementStack.concat(node), componentStack, stripWhitespace, nextSibling);

@ -9,8 +9,9 @@ export default function visit(
block: Block, block: Block,
state: State, state: State,
node: Node, node: Node,
elementStack: Node[] elementStack: Node[],
componentStack: Node[]
) { ) {
const visitor = visitors[node.type]; const visitor = visitors[node.type];
visitor(generator, block, state, node, elementStack); visitor(generator, block, state, node, elementStack, componentStack);
} }

@ -43,7 +43,8 @@ export default function visitComponent(
block: Block, block: Block,
state: State, state: State,
node: Node, node: Node,
elementStack: Node[] elementStack: Node[],
componentStack: Node[]
) { ) {
generator.hasComponents = true; generator.hasComponents = true;
@ -56,7 +57,7 @@ export default function visitComponent(
componentInitProperties.push(`slots: { ${slots.join(', ')} }`); componentInitProperties.push(`slots: { ${slots.join(', ')} }`);
node.children.forEach((child: Node) => { node.children.forEach((child: Node) => {
visit(generator, block, node._state, child, elementStack); visit(generator, block, node._state, child, elementStack, componentStack.concat(node));
}); });
} }

@ -10,7 +10,8 @@ export default function visitEachBlock(
block: Block, block: Block,
state: State, state: State,
node: Node, node: Node,
elementStack: Node[] elementStack: Node[],
componentStack: Node[]
) { ) {
const each_block = generator.getUniqueName(`each_block`); const each_block = generator.getUniqueName(`each_block`);
const create_each_block = node._block.name; const create_each_block = node._block.name;
@ -125,12 +126,12 @@ export default function visitEachBlock(
} }
node.children.forEach((child: Node) => { 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) { if (node.else) {
node.else.children.forEach((child: Node) => { 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);
}); });
} }
} }

@ -37,7 +37,8 @@ export default function visitElement(
block: Block, block: Block,
state: State, state: State,
node: Node, node: Node,
elementStack: Node[] elementStack: Node[],
componentStack: Node[]
) { ) {
if (node.name in meta) { if (node.name in meta) {
return meta[node.name](generator, block, node); return meta[node.name](generator, block, node);
@ -48,13 +49,18 @@ export default function visitElement(
} }
if (generator.components.has(node.name) || node.name === ':Self') { 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 childState = node._state;
const name = childState.parentNode; 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.addVariable(name);
block.builders.create.addLine( block.builders.create.addLine(
@ -77,9 +83,9 @@ export default function visitElement(
`); `);
} }
if (state.parentNode) { if (parentNode) {
block.builders.mount.addLine( block.builders.mount.addLine(
`@appendNode( ${name}, ${state.parentNode} );` `@appendNode( ${name}, ${parentNode} );`
); );
} else { } else {
block.builders.mount.addLine(`@insertNode( ${name}, #target, anchor );`); block.builders.mount.addLine(`@insertNode( ${name}, #target, anchor );`);
@ -195,7 +201,7 @@ export default function visitElement(
} }
node.children.forEach((child: Node) => { 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) { if (node.lateUpdate) {

@ -20,7 +20,8 @@ function getBranches(
block: Block, block: Block,
state: State, state: State,
node: Node, node: Node,
elementStack: Node[] elementStack: Node[],
componentStack: Node[]
) { ) {
const branches = [ 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)) { if (isElseIf(node.else)) {
branches.push( branches.push(
...getBranches(generator, block, state, node.else.children[0], elementStack) ...getBranches(generator, block, state, node.else.children[0], elementStack, componentStack)
); );
} else { } else {
branches.push({ branches.push({
@ -48,7 +49,7 @@ function getBranches(
}); });
if (node.else) { 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, block: Block,
state: State, state: State,
node: Node, node: Node,
elementStack: Node[] elementStack: Node[],
componentStack: Node[]
) { ) {
node.children.forEach((child: 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, block: Block,
state: State, state: State,
node: Node, node: Node,
elementStack: Node[] elementStack: Node[],
componentStack: Node[]
) { ) {
const name = generator.getUniqueName(`if_block`); const name = generator.getUniqueName(`if_block`);
const anchor = node.needsAnchor const anchor = node.needsAnchor
@ -80,7 +83,7 @@ export default function visitIfBlock(
: (node.next && node.next._state.name) || 'null'; : (node.next && node.next._state.name) || 'null';
const params = block.params.join(', '); 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 hasElse = isElseBranch(branches[branches.length - 1]);
const if_name = hasElse ? '' : `if ( ${name} ) `; const if_name = hasElse ? '' : `if ( ${name} ) `;

@ -11,7 +11,8 @@ export default function visitSlot(
block: Block, block: Block,
state: State, state: State,
node: Node, node: Node,
elementStack: Node[] elementStack: Node[],
componentStack: Node[]
) { ) {
const slotName = getStaticAttributeValue(node, 'name') || 'default'; const slotName = getStaticAttributeValue(node, 'name') || 'default';
const name = block.getUniqueName(`slot_${slotName}`); const name = block.getUniqueName(`slot_${slotName}`);
@ -27,13 +28,19 @@ export default function visitSlot(
state.parentNode state.parentNode
); );
if (slotName !== 'default') {
block.builders.hydrate.addBlock(deindent`
@setAttribute(${name}, 'name', '${slotName}');
`);
}
block.builders.create.pushCondition(`!${content_name}`); block.builders.create.pushCondition(`!${content_name}`);
block.builders.mount.pushCondition(`!${content_name}`); block.builders.mount.pushCondition(`!${content_name}`);
block.builders.unmount.pushCondition(`!${content_name}`); block.builders.unmount.pushCondition(`!${content_name}`);
block.builders.destroy.pushCondition(`!${content_name}`); block.builders.destroy.pushCondition(`!${content_name}`);
node.children.forEach((child: Node) => { 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(); block.builders.create.popCondition();

@ -5,8 +5,9 @@ import MustacheTag from './MustacheTag';
import RawMustacheTag from './RawMustacheTag'; import RawMustacheTag from './RawMustacheTag';
import Text from './Text'; import Text from './Text';
import YieldTag from './YieldTag'; import YieldTag from './YieldTag';
import { Visitor } from '../interfaces';
export default { const visitors: Record<string, Visitor> = {
EachBlock, EachBlock,
Element, Element,
IfBlock, IfBlock,
@ -15,3 +16,5 @@ export default {
Text, Text,
YieldTag, YieldTag,
}; };
export default visitors;

@ -0,0 +1,5 @@
<div>
<slot/>
<slot name='bar'/>
<slot name='foo'/>
</div>

@ -0,0 +1,10 @@
export default {
solo: true,
html: `
<div>
<slot>Hello</slot>
<slot name='bar'><p slot='bar'>bar</p></slot>
<slot name='foo'><p slot='foo'>foo</p></slot>
</div>
`
};

@ -0,0 +1,16 @@
<Nested>
Hello
<p slot='foo'>foo</p>
<p slot='bar'>bar</p>
</Nested>
<script>
import Nested from './Nested.html';
export default {
components: {
Nested
}
};
</script>
Loading…
Cancel
Save