Skip to content

Commit

Permalink
Abort outro if block is recreated — fixes #1425
Browse files Browse the repository at this point in the history
  • Loading branch information
Rich-Harris authored May 6, 2018
1 parent e1db827 commit e8a7806
Show file tree
Hide file tree
Showing 10 changed files with 132 additions and 16 deletions.
4 changes: 1 addition & 3 deletions src/compile/dom/Block.ts
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ export default class Block {
}
}

if (this.hasIntroMethod) {
if (this.hasIntroMethod || this.hasOutroMethod) {
if (hasIntros) {
properties.addBlock(deindent`
${dev ? 'i: function intro' : 'i'}(#target, anchor) {
Expand All @@ -252,9 +252,7 @@ export default class Block {
},
`);
}
}

if (this.hasOutroMethod) {
if (hasOutros) {
properties.addBlock(deindent`
${dev ? 'o: function outro' : 'o'}(#outrocallback) {
Expand Down
4 changes: 3 additions & 1 deletion src/compile/nodes/AwaitBlock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,8 +132,10 @@ export default class AwaitBlock extends Node {
const initialMountNode = parentNode || '#target';
const anchorNode = parentNode ? 'null' : 'anchor';

const hasTransitions = this.pending.block.hasIntroMethod || this.pending.block.hasOutroMethod;

block.builders.mount.addBlock(deindent`
${info}.block.${this.pending.block.hasIntroMethod ? 'i' : 'm'}(${initialMountNode}, ${info}.anchor = ${anchorNode});
${info}.block.${hasTransitions ? 'i' : 'm'}(${initialMountNode}, ${info}.anchor = ${anchorNode});
${info}.mount = () => ${updateMountNode};
`);

Expand Down
4 changes: 2 additions & 2 deletions src/compile/nodes/EachBlock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ export default class EachBlock extends Node {
compiler.code.overwrite(c, c + 4, 'length');
const length = `[✂${c}-${c+4}✂]`;

const mountOrIntro = this.block.hasIntroMethod ? 'i' : 'm';
const mountOrIntro = (this.block.hasIntroMethod || this.block.hasOutroMethod) ? 'i' : 'm';
const vars = {
each,
create_each_block,
Expand Down Expand Up @@ -379,7 +379,7 @@ export default class EachBlock extends Node {

if (condition !== '') {
const forLoopBody = this.block.hasUpdateMethod
? this.block.hasIntroMethod
? (this.block.hasIntroMethod || this.block.hasOutroMethod)
? deindent`
if (${iterations}[#i]) {
${iterations}[#i].p(changed, child_ctx);
Expand Down
6 changes: 6 additions & 0 deletions src/compile/nodes/Element.ts
Original file line number Diff line number Diff line change
Expand Up @@ -701,6 +701,8 @@ export default class Element extends Node {
const fn = `%transitions-${intro.name}`;

block.builders.intro.addBlock(deindent`
if (${name}) ${name}.invalidate();
#component.root._aftercreate.push(() => {
if (!${name}) ${name} = @wrapTransition(#component, ${this.var}, ${fn}, ${snippet}, true);
${name}.run(1);
Expand Down Expand Up @@ -748,6 +750,10 @@ export default class Element extends Node {

const fn = `%transitions-${outro.name}`;

block.builders.intro.addBlock(deindent`
if (${outroName}) ${outroName}.abort();
`);

// TODO hide elements that have outro'd (unless they belong to a still-outroing
// group) prior to their removal from the DOM
block.builders.outro.addBlock(deindent`
Expand Down
4 changes: 2 additions & 2 deletions src/compile/nodes/IfBlock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -367,7 +367,7 @@ export default class IfBlock extends Node {
const updateMountNode = this.getUpdateMountNode(anchor);

const enter = dynamic
? branch.hasIntroMethod
? (branch.hasIntroMethod || branch.hasOutroMethod)
? deindent`
if (${name}) {
${name}.p(changed, ctx);
Expand All @@ -387,7 +387,7 @@ export default class IfBlock extends Node {
${name}.m(${updateMountNode}, ${anchor});
}
`
: branch.hasIntroMethod
: (branch.hasIntroMethod || branch.hasOutroMethod)
? deindent`
if (!${name}) {
${name} = ${branch.block}(#component, ctx);
Expand Down
26 changes: 18 additions & 8 deletions src/shared/transitions.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ export function linear(t) {
}

export function generateRule({ a, b, delta, duration }, ease, fn) {
const step = 16.666 / duration;
let keyframes = '{\n';

for (let p = 0; p <= 1; p += 16.666 / duration) {
for (let p = 0; p <= 1; p += step) {
const t = a + delta * ease(p);
keyframes += p * 100 + `%{${fn(t)}}\n`;
}
Expand Down Expand Up @@ -112,7 +113,7 @@ export function wrapTransition(component, node, fn, params, intro) {

component.fire(`${program.b ? 'intro' : 'outro'}.end`, { node });

if (!program.b) {
if (!program.b && !program.invalidated) {
program.group.callbacks.push(() => {
program.callback();
if (obj.css) transitionManager.deleteRule(node, program.name);
Expand All @@ -123,17 +124,26 @@ export function wrapTransition(component, node, fn, params, intro) {
fn();
});
}
} else {
if (obj.css) transitionManager.deleteRule(node, program.name);
}

this.program = null;
this.running = !!this.pending;
},

abort() {
if (obj.tick) obj.tick(1);
if (obj.css) transitionManager.deleteRule(node, this.program.name);
this.program = this.pending = null;
this.running = false;
if (this.program) {
if (obj.tick) obj.tick(1);
if (obj.css) transitionManager.deleteRule(node, this.program.name);
this.program = this.pending = null;
this.running = false;
}
},

invalidate() {
if (this.program) {
this.program.invalidated = true;
}
}
};
}
Expand Down Expand Up @@ -204,7 +214,7 @@ export var transitionManager = {
deleteRule(node, name) {
node.style.animation = node.style.animation
.split(', ')
.filter(anim => anim.indexOf(name) === -1)
.filter(anim => anim && anim.indexOf(name) === -1)
.join(', ');
},

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
export default {
data: {
things: [
'one',
'two',
'three'
]
},

test(assert, component, target, window, raf) {
const { things } = component.get();

component.set({ things: [] });
const spans = target.querySelectorAll('span');

raf.tick(25);
assert.equal(spans[0].foo, 0.75);
assert.equal(spans[1].foo, undefined);
assert.equal(spans[2].foo, undefined);

raf.tick(125);
assert.equal(spans[0].foo, 0);
assert.equal(spans[1].foo, 0.25);
assert.equal(spans[2].foo, 0.75);

component.set({ things });
raf.tick(225);

assert.htmlEqual(target.innerHTML, `
<span>one</span>
<span>two</span>
<span>three</span>
`);

assert.equal(spans[0].foo, 1);
assert.equal(spans[1].foo, 1);
assert.equal(spans[2].foo, 1);
},
};
19 changes: 19 additions & 0 deletions test/runtime/samples/transition-js-aborted-outro-in-each/main.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{#each things as thing, i}
<span out:foo="{delay: i * 50}">{thing}</span>
{/each}

<script>
export default {
transitions: {
foo(node, params) {
return {
delay: params.delay,
duration: 100,
tick: t => {
node.foo = t;
}
};
}
}
};
</script>
24 changes: 24 additions & 0 deletions test/runtime/samples/transition-js-aborted-outro/_config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
export default {
data: {
visible: true,
},

test(assert, component, target, window, raf) {
component.set({ visible: false });
const span = target.querySelector('span');

raf.tick(50);
assert.equal(span.foo, 0.5);

component.set({ visible: true });
assert.equal(span.foo, 1);

raf.tick(75);
assert.equal(span.foo, 1);

raf.tick(100);
assert.htmlEqual(target.innerHTML, `
<span>hello</span>
`);
},
};
18 changes: 18 additions & 0 deletions test/runtime/samples/transition-js-aborted-outro/main.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{#if visible}
<span out:foo>hello</span>
{/if}

<script>
export default {
transitions: {
foo(node, params) {
return {
duration: 100,
tick: t => {
node.foo = t;
}
};
}
}
};
</script>

0 comments on commit e8a7806

Please sign in to comment.