Abort outro if block is recreated — fixes #1425

pull/1429/head
Rich Harris 7 years ago committed by GitHub
parent e1db82773d
commit e8a780676d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

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

@ -132,8 +132,10 @@ export default class AwaitBlock extends Node {
const initialMountNode = parentNode || '#target'; const initialMountNode = parentNode || '#target';
const anchorNode = parentNode ? 'null' : 'anchor'; const anchorNode = parentNode ? 'null' : 'anchor';
const hasTransitions = this.pending.block.hasIntroMethod || this.pending.block.hasOutroMethod;
block.builders.mount.addBlock(deindent` 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}; ${info}.mount = () => ${updateMountNode};
`); `);

@ -146,7 +146,7 @@ export default class EachBlock extends Node {
compiler.code.overwrite(c, c + 4, 'length'); compiler.code.overwrite(c, c + 4, 'length');
const length = `[✂${c}-${c+4}✂]`; const length = `[✂${c}-${c+4}✂]`;
const mountOrIntro = this.block.hasIntroMethod ? 'i' : 'm'; const mountOrIntro = (this.block.hasIntroMethod || this.block.hasOutroMethod) ? 'i' : 'm';
const vars = { const vars = {
each, each,
create_each_block, create_each_block,
@ -379,7 +379,7 @@ export default class EachBlock extends Node {
if (condition !== '') { if (condition !== '') {
const forLoopBody = this.block.hasUpdateMethod const forLoopBody = this.block.hasUpdateMethod
? this.block.hasIntroMethod ? (this.block.hasIntroMethod || this.block.hasOutroMethod)
? deindent` ? deindent`
if (${iterations}[#i]) { if (${iterations}[#i]) {
${iterations}[#i].p(changed, child_ctx); ${iterations}[#i].p(changed, child_ctx);

@ -701,6 +701,8 @@ export default class Element extends Node {
const fn = `%transitions-${intro.name}`; const fn = `%transitions-${intro.name}`;
block.builders.intro.addBlock(deindent` block.builders.intro.addBlock(deindent`
if (${name}) ${name}.invalidate();
#component.root._aftercreate.push(() => { #component.root._aftercreate.push(() => {
if (!${name}) ${name} = @wrapTransition(#component, ${this.var}, ${fn}, ${snippet}, true); if (!${name}) ${name} = @wrapTransition(#component, ${this.var}, ${fn}, ${snippet}, true);
${name}.run(1); ${name}.run(1);
@ -748,6 +750,10 @@ export default class Element extends Node {
const fn = `%transitions-${outro.name}`; 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 // TODO hide elements that have outro'd (unless they belong to a still-outroing
// group) prior to their removal from the DOM // group) prior to their removal from the DOM
block.builders.outro.addBlock(deindent` block.builders.outro.addBlock(deindent`

@ -367,7 +367,7 @@ export default class IfBlock extends Node {
const updateMountNode = this.getUpdateMountNode(anchor); const updateMountNode = this.getUpdateMountNode(anchor);
const enter = dynamic const enter = dynamic
? branch.hasIntroMethod ? (branch.hasIntroMethod || branch.hasOutroMethod)
? deindent` ? deindent`
if (${name}) { if (${name}) {
${name}.p(changed, ctx); ${name}.p(changed, ctx);
@ -387,7 +387,7 @@ export default class IfBlock extends Node {
${name}.m(${updateMountNode}, ${anchor}); ${name}.m(${updateMountNode}, ${anchor});
} }
` `
: branch.hasIntroMethod : (branch.hasIntroMethod || branch.hasOutroMethod)
? deindent` ? deindent`
if (!${name}) { if (!${name}) {
${name} = ${branch.block}(#component, ctx); ${name} = ${branch.block}(#component, ctx);

@ -6,9 +6,10 @@ export function linear(t) {
} }
export function generateRule({ a, b, delta, duration }, ease, fn) { export function generateRule({ a, b, delta, duration }, ease, fn) {
const step = 16.666 / duration;
let keyframes = '{\n'; 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); const t = a + delta * ease(p);
keyframes += p * 100 + `%{${fn(t)}}\n`; keyframes += p * 100 + `%{${fn(t)}}\n`;
} }
@ -112,7 +113,7 @@ export function wrapTransition(component, node, fn, params, intro) {
component.fire(`${program.b ? 'intro' : 'outro'}.end`, { node }); component.fire(`${program.b ? 'intro' : 'outro'}.end`, { node });
if (!program.b) { if (!program.b && !program.invalidated) {
program.group.callbacks.push(() => { program.group.callbacks.push(() => {
program.callback(); program.callback();
if (obj.css) transitionManager.deleteRule(node, program.name); if (obj.css) transitionManager.deleteRule(node, program.name);
@ -123,18 +124,27 @@ export function wrapTransition(component, node, fn, params, intro) {
fn(); fn();
}); });
} }
} else {
if (obj.css) transitionManager.deleteRule(node, program.name);
} }
this.program = null;
this.running = !!this.pending; this.running = !!this.pending;
}, },
abort() { abort() {
if (this.program) {
if (obj.tick) obj.tick(1); if (obj.tick) obj.tick(1);
if (obj.css) transitionManager.deleteRule(node, this.program.name); if (obj.css) transitionManager.deleteRule(node, this.program.name);
this.program = this.pending = null; this.program = this.pending = null;
this.running = false; this.running = false;
} }
},
invalidate() {
if (this.program) {
this.program.invalidated = true;
}
}
}; };
} }
@ -204,7 +214,7 @@ export var transitionManager = {
deleteRule(node, name) { deleteRule(node, name) {
node.style.animation = node.style.animation node.style.animation = node.style.animation
.split(', ') .split(', ')
.filter(anim => anim.indexOf(name) === -1) .filter(anim => anim && anim.indexOf(name) === -1)
.join(', '); .join(', ');
}, },

@ -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);
},
};

@ -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>

@ -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>
`);
},
};

@ -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>
Loading…
Cancel
Save