diff --git a/src/generators/dom/Block.js b/src/generators/dom/Block.js index d375676fb3..f2d507c035 100644 --- a/src/generators/dom/Block.js +++ b/src/generators/dom/Block.js @@ -24,6 +24,7 @@ export default class Block { create: new CodeBuilder(), mount: new CodeBuilder(), update: new CodeBuilder(), + intro: new CodeBuilder(), outro: new CodeBuilder(), detach: new CodeBuilder(), detachRaw: new CodeBuilder(), @@ -105,6 +106,12 @@ export default class Block { } render () { + let introing; + if ( this.hasIntroTransitions ) { + introing = this.getUniqueName( 'introing' ); + this.addVariable( introing ); + } + let outroing; if ( this.hasOutroTransitions ) { outroing = this.getUniqueName( 'outroing' ); @@ -168,6 +175,24 @@ export default class Block { } } + if ( this.hasIntroTransitions ) { + if ( this.builders.outro.isEmpty() ) { + properties.addBlock( `intro: ${this.generator.helper( 'noop' )},` ); + } else { + properties.addBlock( deindent` + intro: function ( ${this.target}, anchor ) { + if ( ${introing} ) return; + ${introing} = true; + ${this.hasOutroTransitions && `${outroing} = false;`} + + ${this.builders.intro} + + this.mount( ${this.target}, anchor ); + }, + ` ); + } + } + if ( this.hasOutroTransitions ) { if ( this.builders.outro.isEmpty() ) { properties.addBlock( `outro: ${this.generator.helper( 'noop' )},` ); @@ -176,6 +201,7 @@ export default class Block { outro: function ( ${this.alias( 'outrocallback' )} ) { if ( ${outroing} ) return; ${outroing} = true; + ${this.hasIntroTransitions && `${introing} = false;`} var ${this.alias( 'outros' )} = ${this.outros}; diff --git a/src/generators/dom/visitors/Element/addTransitions.js b/src/generators/dom/visitors/Element/addTransitions.js index b3e1a7e510..2483b4f12b 100644 --- a/src/generators/dom/visitors/Element/addTransitions.js +++ b/src/generators/dom/visitors/Element/addTransitions.js @@ -17,7 +17,11 @@ export default function addTransitions ( generator, block, state, node, intro, o const fn = `${generator.alias( 'template' )}.transitions.${intro.name}`; // TODO add built-in transitions? - block.builders.create.addBlock( deindent` + if ( outro ) { + block.builders.intro.addBlock( `if ( ${outroName} ) ${outroName}.abort();` ); + } + + block.builders.intro.addBlock( deindent` ${block.component}._renderHooks.push( function () { ${introName} = ${wrapTransition}( ${state.name}, ${fn}, ${introSnippet}, true, null, function () { ${block.component}.fire( 'intro.end', { node: ${state.name} }); diff --git a/src/generators/dom/visitors/IfBlock.js b/src/generators/dom/visitors/IfBlock.js index 8c6a8b2a7e..c4f1a435f8 100644 --- a/src/generators/dom/visitors/IfBlock.js +++ b/src/generators/dom/visitors/IfBlock.js @@ -10,6 +10,7 @@ function getBranches ( generator, block, state, node ) { condition: block.contextualise( node.expression ).snippet, block: node._block.name, dynamic: node._block.dependencies.size > 0, + hasIntroTransitions: node._block.hasIntroTransitions, hasOutroTransitions: node._block.hasOutroTransitions }]; @@ -24,6 +25,7 @@ function getBranches ( generator, block, state, node ) { condition: null, block: node.else ? node.else._block.name : null, dynamic: node.else ? node.else._block.dependencies.size > 0 : false, + hasIntroTransitions: node.else ? node.else._block.hasIntroTransitions : false, hasOutroTransitions: node.else ? node.else._block.hasOutroTransitions : false }); @@ -74,16 +76,17 @@ function simple ( generator, block, state, node, branch, dynamic, { name, anchor ` ); const isToplevel = !state.parentNode; + const mountOrIntro = branch.hasIntroTransitions ? 'intro' : 'mount'; if ( isToplevel ) { - block.builders.mount.addLine( `if ( ${name} ) ${name}.mount( ${block.target}, null );` ); + block.builders.mount.addLine( `if ( ${name} ) ${name}.${mountOrIntro}( ${block.target}, null );` ); } else { - block.builders.create.addLine( `if ( ${name} ) ${name}.mount( ${state.parentNode}, null );` ); + block.builders.create.addLine( `if ( ${name} ) ${name}.${mountOrIntro}( ${state.parentNode}, null );` ); } const parentNode = state.parentNode || `${anchor}.parentNode`; - const remove = branch.hasOutroTransitions ? + const exit = branch.hasOutroTransitions ? deindent` ${name}.outro( function () { ${name} = null; @@ -95,6 +98,10 @@ function simple ( generator, block, state, node, branch, dynamic, { name, anchor `; if ( dynamic ) { + if ( branch.hasIntroTransitions ) { + throw new Error( 'TODO simple dynamic if-block with intro transitions' ); + } + block.builders.update.addBlock( deindent` if ( ${branch.condition} ) { if ( ${name} ) { @@ -104,18 +111,27 @@ function simple ( generator, block, state, node, branch, dynamic, { name, anchor ${name}.mount( ${parentNode}, ${anchor} ); } } else if ( ${name} ) { - ${remove} + ${exit} } ` ); } else { - block.builders.update.addBlock( deindent` - if ( ${branch.condition} ) { + const enter = branch.hasIntroTransitions ? + deindent` + if ( !${name} ) ${name} = ${branch.block}( ${params}, ${block.component} ); + ${name}.intro( ${parentNode}, ${anchor} ); + ` : + deindent` if ( !${name} ) { ${name} = ${branch.block}( ${params}, ${block.component} ); ${name}.mount( ${parentNode}, ${anchor} ); } + `; + + block.builders.update.addBlock( deindent` + if ( ${branch.condition} ) { + ${enter} } else if ( ${name} ) { - ${remove} + ${exit} } ` ); } diff --git a/src/shared/transitions.js b/src/shared/transitions.js index 1b4cb1b90d..58406864f7 100644 --- a/src/shared/transitions.js +++ b/src/shared/transitions.js @@ -27,7 +27,10 @@ export function wrapTransition ( node, fn, params, intro, outgroup, callback ) { obj.tick( intro ? 1 : 0 ); callback(); }, - abort: noop + abort: function () { + if ( !intro ) obj.tick( 1 ); // reset styles for intro + this.aborted = true; + } }; } else { // CSS transition @@ -71,6 +74,7 @@ export function wrapTransition ( node, fn, params, intro, outgroup, callback ) { }, abort: function () { node.style.cssText = getComputedStyle( node ).cssText; + this.aborted = true; } }; } @@ -105,12 +109,13 @@ export var transitionManager = { while ( i-- ) { var transition = transitionManager.transitions[i]; - if ( now >= transition.end ) { - transition.done(); - transitionManager.transitions.splice( i, 1 ); - } else { + + if ( now < transition.end && !transition.aborted ) { if ( now > transition.start ) transition.update( now ); transitionManager.running = true; + } else { + if ( !transition.aborted ) transition.done(); + transitionManager.transitions.splice( i, 1 ); } }