From 98e63b37e9513f0f7a2ec3faede17d171b4d8132 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Sat, 12 May 2018 23:35:03 -0400 Subject: [PATCH 1/6] groundwork for animations (#1431) --- src/compile/Compiler.ts | 10 +++- src/compile/dom/Block.ts | 11 ++++ src/compile/nodes/Animation.ts | 18 ++++++ src/compile/nodes/EachBlock.ts | 6 +- src/compile/nodes/Element.ts | 35 ++++++----- src/parse/read/directives.ts | 11 +++- src/shared/keyed-each.js | 7 +++ src/validate/html/validateElement.ts | 35 +++++++++++ src/validate/index.ts | 4 ++ src/validate/js/index.ts | 2 +- src/validate/js/propValidators/animations.ts | 21 +++++++ src/validate/js/propValidators/index.ts | 2 + test/parser/samples/animation/input.html | 3 + test/parser/samples/animation/output.json | 59 +++++++++++++++++++ test/runtime/samples/animation/_config.js | 61 ++++++++++++++++++++ test/runtime/samples/animation/main.html | 22 +++++++ 16 files changed, 285 insertions(+), 22 deletions(-) create mode 100644 src/compile/nodes/Animation.ts create mode 100644 src/validate/js/propValidators/animations.ts create mode 100644 test/parser/samples/animation/input.html create mode 100644 test/parser/samples/animation/output.json create mode 100644 test/runtime/samples/animation/_config.js create mode 100644 test/runtime/samples/animation/main.html diff --git a/src/compile/Compiler.ts b/src/compile/Compiler.ts index ac584816b1..e022689aac 100644 --- a/src/compile/Compiler.ts +++ b/src/compile/Compiler.ts @@ -99,6 +99,7 @@ export default class Compiler { components: Set; events: Set; methods: Set; + animations: Set; transitions: Set; actions: Set; importedComponents: Map; @@ -149,6 +150,7 @@ export default class Compiler { this.components = new Set(); this.events = new Set(); this.methods = new Set(); + this.animations = new Set(); this.transitions = new Set(); this.actions = new Set(); this.importedComponents = new Map(); @@ -475,7 +477,7 @@ export default class Compiler { templateProperties[getName(prop.key)] = prop; }); - ['helpers', 'events', 'components', 'transitions', 'actions'].forEach(key => { + ['helpers', 'events', 'components', 'transitions', 'actions', 'animations'].forEach(key => { if (templateProperties[key]) { templateProperties[key].value.properties.forEach((prop: Node) => { this[key].add(getName(prop.key)); @@ -685,6 +687,12 @@ export default class Compiler { }); } + if (templateProperties.animations) { + templateProperties.animations.value.properties.forEach((property: Node) => { + addDeclaration(getName(property.key), property.value, false, 'animations'); + }); + } + if (templateProperties.actions) { templateProperties.actions.value.properties.forEach((property: Node) => { addDeclaration(getName(property.key), property.value, false, 'actions'); diff --git a/src/compile/dom/Block.ts b/src/compile/dom/Block.ts index 6730d416b0..cde0eb0f3f 100644 --- a/src/compile/dom/Block.ts +++ b/src/compile/dom/Block.ts @@ -40,6 +40,7 @@ export default class Block { }; maintainContext: boolean; + animation?: string; hasIntroMethod: boolean; hasOutroMethod: boolean; outros: number; @@ -77,6 +78,7 @@ export default class Block { destroy: new CodeBuilder(), }; + this.animation = null; this.hasIntroMethod = false; // a block could have an intro method but not intro transitions, e.g. if a sibling block has intros this.hasOutroMethod = false; this.outros = 0; @@ -127,6 +129,10 @@ export default class Block { this.outros += 1; } + addAnimation(name) { + this.animation = name; + } + addVariable(name: string, init?: string) { if (this.variables.has(name) && this.variables.get(name) !== init) { throw new Error( @@ -183,6 +189,11 @@ export default class Block { this.builders.hydrate.addLine(`this.first = ${this.first};`); } + if (this.animation) { + properties.addBlock(`node: null,`); + this.builders.hydrate.addLine(`this.node = ${this.animation};`); + } + if (this.builders.create.isEmpty() && this.builders.hydrate.isEmpty()) { properties.addBlock(`c: @noop,`); } else { diff --git a/src/compile/nodes/Animation.ts b/src/compile/nodes/Animation.ts new file mode 100644 index 0000000000..8041f26485 --- /dev/null +++ b/src/compile/nodes/Animation.ts @@ -0,0 +1,18 @@ +import Node from './shared/Node'; +import Expression from './shared/Expression'; + +export default class Animation extends Node { + type: 'Animation'; + name: string; + expression: Expression; + + constructor(compiler, parent, scope, info) { + super(compiler, parent, scope, info); + + this.name = info.name; + + this.expression = info.expression + ? new Expression(compiler, this, scope, info.expression) + : null; + } +} \ No newline at end of file diff --git a/src/compile/nodes/EachBlock.ts b/src/compile/nodes/EachBlock.ts index cee550d5fe..ea0dc9c551 100644 --- a/src/compile/nodes/EachBlock.ts +++ b/src/compile/nodes/EachBlock.ts @@ -315,10 +315,14 @@ export default class EachBlock extends Node { const dynamic = this.block.hasUpdateMethod; block.builders.update.addBlock(deindent` - var ${this.each_block_value} = ${snippet}; + const ${this.each_block_value} = ${snippet}; ${this.block.hasOutroMethod && `@transitionManager.groupOutros();`} + ${this.block.animation && `const before = @measure(${blocks});`} ${blocks} = @updateKeyedEach(${blocks}, #component, changed, ${get_key}, ${dynamic ? '1' : '0'}, ctx, ${this.each_block_value}, ${lookup}, ${updateMountNode}, ${String(this.block.hasOutroMethod)}, ${create_each_block}, "${mountOrIntro}", ${anchor}, ${this.get_each_context}); + ${this.block.animation && `const after = @measure(${blocks});`} + + console.log({ before, after }); `); if (this.compiler.options.nestedTransitions) { diff --git a/src/compile/nodes/Element.ts b/src/compile/nodes/Element.ts index 18dfb374da..9749aa09fb 100644 --- a/src/compile/nodes/Element.ts +++ b/src/compile/nodes/Element.ts @@ -13,6 +13,7 @@ import Attribute from './Attribute'; import Binding from './Binding'; import EventHandler from './EventHandler'; import Transition from './Transition'; +import Animation from './Animation'; import Action from './Action'; import Text from './Text'; import * as namespaces from '../../utils/namespaces'; @@ -30,8 +31,9 @@ export default class Element extends Node { actions: Action[]; bindings: Binding[]; handlers: EventHandler[]; - intro: Transition; - outro: Transition; + intro?: Transition; + outro?: Transition; + animation?: Animation; children: Node[]; ref: string; @@ -54,6 +56,7 @@ export default class Element extends Node { this.intro = null; this.outro = null; + this.animation = null; if (this.name === 'textarea') { // this is an egregious hack, but it's the easiest way to get