contextual bindings

pull/1864/head
Rich Harris 7 years ago
parent e2f36d0d04
commit 7a8e15ed6b

@ -11,7 +11,7 @@ export interface BlockOptions {
renderer?: Renderer; renderer?: Renderer;
comment?: string; comment?: string;
key?: string; key?: string;
bindings?: Map<string, () => string>; bindings?: Map<string, () => { object: string, property: string, snippet: string }>;
contextOwners?: Map<string, EachBlockWrapper>; contextOwners?: Map<string, EachBlockWrapper>;
dependencies?: Set<string>; dependencies?: Set<string>;
} }
@ -29,7 +29,7 @@ export default class Block {
dependencies: Set<string>; dependencies: Set<string>;
bindings: Map<string, () => string>; bindings: Map<string, () => { object: string, property: string, snippet: string }>;
contextOwners: Map<string, EachBlockWrapper>; contextOwners: Map<string, EachBlockWrapper>;
builders: { builders: {

@ -101,7 +101,11 @@ export default class EachBlockWrapper extends Wrapper {
this.block.contextOwners.set(prop.key.name, this); this.block.contextOwners.set(prop.key.name, this);
// TODO this doesn't feel great // TODO this doesn't feel great
this.block.bindings.set(prop.key.name, () => `ctx.${this.vars.each_block_value}[ctx.${this.indexName}]${prop.tail}`); this.block.bindings.set(prop.key.name, () => ({
object: this.vars.each_block_value,
property: this.indexName,
snippet: `${this.vars.each_block_value}[${this.indexName}]${prop.tail}`
}));
}); });
if (this.node.index) { if (this.node.index) {

@ -160,7 +160,8 @@ export default class BindingWrapper {
needsLock: !isReadOnly && needsLock, needsLock: !isReadOnly && needsLock,
updateCondition: updateConditions.length ? updateConditions.join(' && ') : undefined, updateCondition: updateConditions.length ? updateConditions.join(' && ') : undefined,
isReadOnlyMediaAttribute: this.isReadOnlyMediaAttribute(), isReadOnlyMediaAttribute: this.isReadOnlyMediaAttribute(),
dependencies: this.node.expression.dependencies dependencies: this.node.expression.dependencies,
contextual_dependencies: this.node.expression.contextual_dependencies
}; };
} }
} }
@ -226,13 +227,14 @@ function getEventHandler(
? getTailSnippet(binding.node.expression.node) ? getTailSnippet(binding.node.expression.node)
: ''; : '';
const head = block.bindings.get(name); const { object, property, snippet } = block.bindings.get(name)();
return { return {
usesContext: true, usesContext: true,
usesState: true, usesState: true,
mutation: `${head()}${tail} = ${value};`, mutation: `${snippet}${tail} = ${value};`,
props: dependenciesArray.map(prop => `${prop}: ctx.${prop}`) props: dependenciesArray.map(prop => `${prop}: ctx.${prop}`),
contextual_dependencies: new Set([object, property])
}; };
} }
@ -241,7 +243,8 @@ function getEventHandler(
usesContext: false, usesContext: false,
usesState: true, usesState: true,
mutation: `${snippet} = ${value};`, mutation: `${snippet} = ${value};`,
props: dependenciesArray.map((prop: string) => `${prop}: ctx.${prop}`) props: dependenciesArray.map((prop: string) => `${prop}: ctx.${prop}`),
contextual_dependencies: new Set()
}; };
} }
@ -251,7 +254,8 @@ function getEventHandler(
usesContext: false, usesContext: false,
usesState: false, usesState: false,
mutation: `${snippet} = ${value};`, mutation: `${snippet} = ${value};`,
props props,
contextual_dependencies: new Set()
}; };
} }

@ -439,10 +439,12 @@ export default class ElementWrapper extends Wrapper {
const needsLock = group.bindings.some(binding => binding.needsLock); const needsLock = group.bindings.some(binding => binding.needsLock);
const deps = new Set(); const dependencies = new Set();
const contextual_dependencies = new Set();
group.bindings.forEach(binding => { group.bindings.forEach(binding => {
addToSet(deps, binding.dependencies); addToSet(dependencies, binding.dependencies);
addToSet(contextual_dependencies, binding.handler.contextual_dependencies);
if (!binding.updateDom) return; if (!binding.updateDom) return;
@ -467,6 +469,35 @@ export default class ElementWrapper extends Wrapper {
// TODO figure out how to handle locks // TODO figure out how to handle locks
let callee;
// TODO dry this out — similar code for event handlers and component bindings
if (contextual_dependencies.size > 0) {
const deps = Array.from(contextual_dependencies);
block.builders.init.addBlock(deindent`
function ${handler}() {
ctx.${handler}.call(this, ctx);
}
`);
this.renderer.component.event_handlers.push({
name: handler,
body: deindent`
function ${handler}({ ${deps.join(', ')} }) {
${
animation_frame && deindent`
cancelAnimationFrame(${animation_frame});
if (!${this.var}.paused) ${animation_frame} = requestAnimationFrame(${handler});`
}
${mutations.length > 0 && mutations}
${Array.from(dependencies).map(dep => `$$make_dirty('${dep}');`)}
}
`
});
callee = handler;
} else {
this.renderer.component.event_handlers.push({ this.renderer.component.event_handlers.push({
name: handler, name: handler,
body: deindent` body: deindent`
@ -477,12 +508,14 @@ export default class ElementWrapper extends Wrapper {
if (!${this.var}.paused) ${animation_frame} = requestAnimationFrame(${handler});` if (!${this.var}.paused) ${animation_frame} = requestAnimationFrame(${handler});`
} }
${mutations.length > 0 && mutations} ${mutations.length > 0 && mutations}
${Array.from(deps).map(dep => `$$make_dirty('${dep}');`)} ${Array.from(dependencies).map(dep => `$$make_dirty('${dep}');`)}
console.log('changed');
} }
` `
}); });
callee = `ctx.${handler}`;
}
group.events.forEach(name => { group.events.forEach(name => {
if (name === 'resize') { if (name === 'resize') {
// special case // special case
@ -490,7 +523,7 @@ export default class ElementWrapper extends Wrapper {
block.addVariable(resize_listener); block.addVariable(resize_listener);
block.builders.mount.addLine( block.builders.mount.addLine(
`${resize_listener} = @addResizeListener(${this.var}, ctx.${handler});` `${resize_listener} = @addResizeListener(${this.var}, ${callee});`
); );
block.builders.destroy.addLine( block.builders.destroy.addLine(
@ -498,11 +531,11 @@ export default class ElementWrapper extends Wrapper {
); );
} else { } else {
block.builders.hydrate.addLine( block.builders.hydrate.addLine(
`@addListener(${this.var}, "${name}", ctx.${handler});` `@addListener(${this.var}, "${name}", ${callee});`
); );
block.builders.destroy.addLine( block.builders.destroy.addLine(
`@removeListener(${this.var}, "${name}", ctx.${handler});` `@removeListener(${this.var}, "${name}", ${callee});`
); );
} }
}); });

@ -44,13 +44,13 @@ export default class InlineComponentWrapper extends Wrapper {
if (binding.isContextual) { if (binding.isContextual) {
// we need to ensure that the each block creates a context including // we need to ensure that the each block creates a context including
// the list and the index, if they're not otherwise referenced // the list and the index, if they're not otherwise referenced
const { name } = getObject(binding.value.node); const { name } = getObject(binding.expression.node);
const eachBlock = block.contextOwners.get(name); const eachBlock = block.contextOwners.get(name);
eachBlock.hasBinding = true; eachBlock.hasBinding = true;
} }
block.addDependencies(binding.value.dependencies); block.addDependencies(binding.expression.dependencies);
}); });
this.node.handlers.forEach(handler => { this.node.handlers.forEach(handler => {
@ -200,24 +200,24 @@ export default class InlineComponentWrapper extends Wrapper {
const builder = new CodeBuilder(); const builder = new CodeBuilder();
this.node.bindings.forEach((binding: Binding) => { this.node.bindings.forEach((binding: Binding) => {
let { name: key } = getObject(binding.value.node); let { name: key } = getObject(binding.expression.node);
let setFromChild; let setFromChild;
if (binding.isContextual) { if (binding.isContextual) {
const computed = isComputed(binding.value.node); const computed = isComputed(binding.expression.node);
const tail = binding.value.node.type === 'MemberExpression' ? getTailSnippet(binding.value.node) : ''; const tail = binding.expression.node.type === 'MemberExpression' ? getTailSnippet(binding.expression.node) : '';
const head = block.bindings.get(key); const { object, property, snippet } = block.bindings.get(key)();
const lhs = binding.value.node.type === 'MemberExpression' const lhs = binding.expression.node.type === 'MemberExpression'
? binding.value.snippet ? binding.expression.snippet
: `${head()}${tail} = childState${quotePropIfNecessary(binding.name)}`; : `${snippet} = childState${quotePropIfNecessary(binding.name)}`;
setFromChild = deindent` setFromChild = deindent`
${lhs} = childState${quotePropIfNecessary(binding.name)}; ${lhs} = childState${quotePropIfNecessary(binding.name)};
${[...binding.value.dependencies] ${[...binding.expression.dependencies]
.map((name: string) => { .map((name: string) => {
const isStoreProp = name[0] === '$'; const isStoreProp = name[0] === '$';
const prop = isStoreProp ? name.slice(1) : name; const prop = isStoreProp ? name.slice(1) : name;
@ -239,9 +239,9 @@ export default class InlineComponentWrapper extends Wrapper {
if (isStoreProp) hasStoreBindings = true; if (isStoreProp) hasStoreBindings = true;
else hasLocalBindings = true; else hasLocalBindings = true;
if (binding.value.node.type === 'MemberExpression') { if (binding.expression.node.type === 'MemberExpression') {
setFromChild = deindent` setFromChild = deindent`
${binding.value.snippet} = childState${quotePropIfNecessary(binding.name)}; ${binding.expression.snippet} = childState${quotePropIfNecessary(binding.name)};
${newState}${quotePropIfNecessary(prop)} = ctx${quotePropIfNecessary(key)}; ${newState}${quotePropIfNecessary(prop)} = ctx${quotePropIfNecessary(key)};
`; `;
} }
@ -252,8 +252,8 @@ export default class InlineComponentWrapper extends Wrapper {
} }
statements.push(deindent` statements.push(deindent`
if (${binding.value.snippet} !== void 0) { if (${binding.expression.snippet} !== void 0) {
${name_initial_data}${quotePropIfNecessary(binding.name)} = ${binding.value.snippet}; ${name_initial_data}${quotePropIfNecessary(binding.name)} = ${binding.expression.snippet};
${name_updating}${quotePropIfNecessary(binding.name)} = true; ${name_updating}${quotePropIfNecessary(binding.name)} = true;
}` }`
); );
@ -264,9 +264,9 @@ export default class InlineComponentWrapper extends Wrapper {
); );
updates.push(deindent` updates.push(deindent`
if (!${name_updating}${quotePropIfNecessary(binding.name)} && ${[...binding.value.dependencies].map((dependency: string) => `changed.${dependency}`).join(' || ')}) { if (!${name_updating}${quotePropIfNecessary(binding.name)} && ${[...binding.expression.dependencies].map((dependency: string) => `changed.${dependency}`).join(' || ')}) {
${name_changes}${quotePropIfNecessary(binding.name)} = ${binding.value.snippet}; ${name_changes}${quotePropIfNecessary(binding.name)} = ${binding.expression.snippet};
${name_updating}${quotePropIfNecessary(binding.name)} = ${binding.value.snippet} !== void 0; ${name_updating}${quotePropIfNecessary(binding.name)} = ${binding.expression.snippet} !== void 0;
} }
`); `);
}); });
@ -380,7 +380,7 @@ export default class InlineComponentWrapper extends Wrapper {
#component.$$root._beforecreate.push(() => { #component.$$root._beforecreate.push(() => {
const changed = {}; const changed = {};
${this.node.bindings.map(binding => deindent` ${this.node.bindings.map(binding => deindent`
if (${binding.value.snippet} === void 0) changed.${binding.name} = 1;`)} if (${binding.expression.snippet} === void 0) changed.${binding.name} = 1;`)}
${name}._bind(changed, ${name}.get()); ${name}._bind(changed, ${name}.get());
});`} });`}
${name}.$$fragment.c(); ${name}.$$fragment.c();

Loading…
Cancel
Save