diff --git a/src/compile/nodes/Transition.ts b/src/compile/nodes/Transition.ts index b77b57f21be6..a0e42d939eff 100644 --- a/src/compile/nodes/Transition.ts +++ b/src/compile/nodes/Transition.ts @@ -7,6 +7,7 @@ export default class Transition extends Node { name: string; directive: string; expression: Expression; + is_local: boolean; constructor(component: Component, parent, scope, info) { super(component, parent, scope, info); @@ -15,6 +16,7 @@ export default class Transition extends Node { this.name = info.name; this.directive = info.intro && info.outro ? 'transition' : info.intro ? 'in' : 'out'; + this.is_local = info.modifiers.includes('local'); if ((info.intro && parent.intro) || (info.outro && parent.outro)) { const parentTransition = (parent.intro || parent.outro); diff --git a/src/compile/render-dom/Block.ts b/src/compile/render-dom/Block.ts index bf4823f863b7..954110b368dd 100644 --- a/src/compile/render-dom/Block.ts +++ b/src/compile/render-dom/Block.ts @@ -174,13 +174,15 @@ export default class Block { } } - addIntro() { + addIntro(local?: boolean) { this.hasIntros = this.hasIntroMethod = this.renderer.hasIntroTransitions = true; + if (!local && this.parent) this.parent.addIntro(); } - addOutro() { + addOutro(local?: boolean) { this.hasOutros = this.hasOutroMethod = this.renderer.hasOutroTransitions = true; this.outros += 1; + if (!local && this.parent) this.parent.addOutro(); } addAnimation() { @@ -327,7 +329,7 @@ export default class Block { properties.addLine(`i: @noop,`); } else { properties.addBlock(deindent` - ${dev ? 'i: function intro' : 'i'}() { + ${dev ? 'i: function intro' : 'i'}(#local) { ${this.hasOutros && `if (#current) return;`} ${this.builders.intro} }, @@ -338,7 +340,7 @@ export default class Block { properties.addLine(`o: @noop,`); } else { properties.addBlock(deindent` - ${dev ? 'o: function outro' : 'o'}() { + ${dev ? 'o: function outro' : 'o'}(#local) { ${this.builders.outro} }, `); diff --git a/src/compile/render-dom/wrappers/EachBlock.ts b/src/compile/render-dom/wrappers/EachBlock.ts index 39f3407020a3..7ec9941d5e95 100644 --- a/src/compile/render-dom/wrappers/EachBlock.ts +++ b/src/compile/render-dom/wrappers/EachBlock.ts @@ -403,7 +403,7 @@ export default class EachBlockWrapper extends Wrapper { const outroBlock = this.block.hasOutros && block.getUniqueName('outroBlock') if (outroBlock) { block.builders.init.addBlock(deindent` - function ${outroBlock}(i, detach) { + function ${outroBlock}(i, detach, local) { if (${iterations}[i]) { if (detach) { @on_outro(() => { @@ -412,7 +412,7 @@ export default class EachBlockWrapper extends Wrapper { }); } - ${iterations}[i].o(); + ${iterations}[i].o(local); } } `); @@ -434,27 +434,27 @@ export default class EachBlockWrapper extends Wrapper { ${iterations}[#i].c(); ${iterations}[#i].m(${updateMountNode}, ${anchor}); } - ${has_transitions && `${iterations}[#i].i();`} + ${has_transitions && `${iterations}[#i].i(1);`} ` : deindent` ${iterations}[#i] = ${create_each_block}(child_ctx); ${iterations}[#i].c(); ${iterations}[#i].m(${updateMountNode}, ${anchor}); - ${has_transitions && `${iterations}[#i].i();`} + ${has_transitions && `${iterations}[#i].i(1);`} `; const start = this.block.hasUpdateMethod ? '0' : `${iterations}.length`; - let destroy; + let remove_old_blocks; if (this.block.hasOutros) { - destroy = deindent` + remove_old_blocks = deindent` @group_outros(); - for (; #i < ${iterations}.length; #i += 1) ${outroBlock}(#i, 1); + for (; #i < ${iterations}.length; #i += 1) ${outroBlock}(#i, 1, 1); @check_outros(); `; } else { - destroy = deindent` + remove_old_blocks = deindent` for (${this.block.hasUpdateMethod ? `` : `#i = ${this.vars.each_block_value}.${length}`}; #i < ${iterations}.length; #i += 1) { ${iterations}[#i].d(1); } @@ -471,7 +471,7 @@ export default class EachBlockWrapper extends Wrapper { ${forLoopBody} } - ${destroy} + ${remove_old_blocks} `; block.builders.update.addBlock(deindent` diff --git a/src/compile/render-dom/wrappers/Element/index.ts b/src/compile/render-dom/wrappers/Element/index.ts index 97654fd19ccc..c61e81c69ccf 100644 --- a/src/compile/render-dom/wrappers/Element/index.ts +++ b/src/compile/render-dom/wrappers/Element/index.ts @@ -162,8 +162,8 @@ export default class ElementWrapper extends Wrapper { this.bindings = this.node.bindings.map(binding => new Binding(block, binding, this)); if (node.intro || node.outro) { - if (node.intro) block.addIntro(); - if (node.outro) block.addOutro(); + if (node.intro) block.addIntro(node.intro.is_local); + if (node.outro) block.addOutro(node.outro.is_local); } if (node.animation) { @@ -622,17 +622,34 @@ export default class ElementWrapper extends Wrapper { const fn = component.qualify(intro.name); - block.builders.intro.addBlock(deindent` + const intro_block = deindent` @add_render_callback(() => { if (!${name}) ${name} = @create_bidirectional_transition(${this.var}, ${fn}, ${snippet}, true); ${name}.run(1); }); - `); + `; - block.builders.outro.addBlock(deindent` + const outro_block = deindent` if (!${name}) ${name} = @create_bidirectional_transition(${this.var}, ${fn}, ${snippet}, false); ${name}.run(0); - `); + `; + + if (intro.is_local) { + block.builders.intro.addBlock(deindent` + if (#local) { + ${intro_block} + } + `); + + block.builders.outro.addBlock(deindent` + if (#local) { + ${outro_block} + } + `); + } else { + block.builders.intro.addBlock(intro_block); + block.builders.outro.addBlock(outro_block); + } block.builders.destroy.addConditional('detach', `if (${name}) ${name}.end();`); } @@ -649,25 +666,37 @@ export default class ElementWrapper extends Wrapper { const fn = component.qualify(intro.name); + let intro_block; + if (outro) { - block.builders.intro.addBlock(deindent` + intro_block = deindent` @add_render_callback(() => { if (!${introName}) ${introName} = @create_in_transition(${this.var}, ${fn}, ${snippet}); ${introName}.start(); }); - `); + `; block.builders.outro.addLine(`if (${introName}) ${introName}.invalidate()`); } else { - block.builders.intro.addBlock(deindent` + intro_block = deindent` if (!${introName}) { @add_render_callback(() => { ${introName} = @create_in_transition(${this.var}, ${fn}, ${snippet}); ${introName}.start(); }); } - `); + `; + } + + if (intro.is_local) { + intro_block = deindent` + if (#local) { + ${intro_block} + } + `; } + + block.builders.intro.addBlock(intro_block); } if (outro) { @@ -684,9 +713,19 @@ export default class ElementWrapper extends Wrapper { // 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` + let outro_block = deindent` ${outroName} = @create_out_transition(${this.var}, ${fn}, ${snippet}); - `); + `; + + if (outro_block) { + outro_block = deindent` + if (#local) { + ${outro_block} + } + `; + } + + block.builders.outro.addBlock(outro_block); block.builders.destroy.addConditional('detach', `if (${outroName}) ${outroName}.end();`); } diff --git a/src/compile/render-dom/wrappers/IfBlock.ts b/src/compile/render-dom/wrappers/IfBlock.ts index 3d12c589ac45..46a935721529 100644 --- a/src/compile/render-dom/wrappers/IfBlock.ts +++ b/src/compile/render-dom/wrappers/IfBlock.ts @@ -125,9 +125,6 @@ export default class IfBlockWrapper extends Wrapper { createBranches(this.node); - if (hasIntros) block.addIntro(); - if (hasOutros) block.addOutro(); - blocks.forEach(block => { block.hasUpdateMethod = isDynamic; block.hasIntroMethod = hasIntros; @@ -239,7 +236,7 @@ export default class IfBlockWrapper extends Wrapper { if (${name}) { ${name}.c(); ${name}.m(${updateMountNode}, ${anchor}); - ${has_transitions && `${name}.i();`} + ${has_transitions && `${name}.i(1);`} } `; @@ -327,7 +324,7 @@ export default class IfBlockWrapper extends Wrapper { ${if_blocks}[${previous_block_index}].d(1); ${if_blocks}[${previous_block_index}] = null; }); - ${name}.o(); + ${name}.o(1); @check_outros(); `; @@ -338,7 +335,7 @@ export default class IfBlockWrapper extends Wrapper { ${name}.c(); } ${name}.m(${updateMountNode}, ${anchor}); - ${has_transitions && `${name}.i();`} + ${has_transitions && `${name}.i(1);`} `; const changeBlock = hasElse @@ -415,7 +412,7 @@ export default class IfBlockWrapper extends Wrapper { ${name}.c(); ${name}.m(${updateMountNode}, ${anchor}); } - ${has_transitions && `${name}.i();`} + ${has_transitions && `${name}.i(1);`} ` : deindent` if (!${name}) { @@ -423,7 +420,7 @@ export default class IfBlockWrapper extends Wrapper { ${name}.c(); ${name}.m(${updateMountNode}, ${anchor}); } - ${has_transitions && `${name}.i();`} + ${has_transitions && `${name}.i(1);`} `; // no `p()` here — we don't want to update outroing nodes, @@ -436,7 +433,7 @@ export default class IfBlockWrapper extends Wrapper { ${name} = null; }); - ${name}.o(); + ${name}.o(1); @check_outros(); ` : deindent` diff --git a/src/compile/render-dom/wrappers/InlineComponent/index.ts b/src/compile/render-dom/wrappers/InlineComponent/index.ts index 5a97be48ebef..67de8adf581f 100644 --- a/src/compile/render-dom/wrappers/InlineComponent/index.ts +++ b/src/compile/render-dom/wrappers/InlineComponent/index.ts @@ -397,7 +397,7 @@ export default class InlineComponentWrapper extends Wrapper { @on_outro(() => { old_component.$destroy(); }); - old_component.$$.fragment.o(); + old_component.$$.fragment.o(1); @check_outros(); } @@ -409,7 +409,7 @@ export default class InlineComponentWrapper extends Wrapper { ${name}.$$.fragment.c(); @mount_component(${name}, ${updateMountNode}, ${anchor}); - ${name}.$$.fragment.i(); + ${name}.$$.fragment.i(1); } else { ${name} = null; } @@ -417,7 +417,7 @@ export default class InlineComponentWrapper extends Wrapper { `); block.builders.intro.addBlock(deindent` - if (${name}) ${name}.$$.fragment.i(); + if (${name}) ${name}.$$.fragment.i(#local); `); if (updates.length) { @@ -430,6 +430,10 @@ export default class InlineComponentWrapper extends Wrapper { `); } + block.builders.outro.addLine( + `if (${name}) ${name}.$$.fragment.o(#local);` + ); + block.builders.destroy.addLine(`if (${name}) ${name}.$destroy(${parentNode ? '' : 'detach'});`); } else { const expression = this.node.name === 'svelte:self' @@ -459,7 +463,7 @@ export default class InlineComponentWrapper extends Wrapper { ); block.builders.intro.addBlock(deindent` - ${name}.$$.fragment.i(); + ${name}.$$.fragment.i(#local); `); if (updates.length) { @@ -473,11 +477,11 @@ export default class InlineComponentWrapper extends Wrapper { block.builders.destroy.addBlock(deindent` ${name}.$destroy(${parentNode ? '' : 'detach'}); `); - } - block.builders.outro.addLine( - `if (${name}) ${name}.$$.fragment.o();` - ); + block.builders.outro.addLine( + `${name}.$$.fragment.o(#local);` + ); + } } } diff --git a/src/internal/await-block.js b/src/internal/await-block.js index 2e10250b4cb2..143694b04a0a 100644 --- a/src/internal/await-block.js +++ b/src/internal/await-block.js @@ -22,7 +22,7 @@ export function handlePromise(promise, info) { block.d(1); info.blocks[i] = null; }); - block.o(); + block.o(1); check_outros(); } }); @@ -32,7 +32,7 @@ export function handlePromise(promise, info) { block.c(); block.m(info.mount(), info.anchor); - if (block.i) block.i(); + if (block.i) block.i(1); flush(); } diff --git a/src/internal/keyed-each.js b/src/internal/keyed-each.js index 76e233f2eda8..1ef9e2743732 100644 --- a/src/internal/keyed-each.js +++ b/src/internal/keyed-each.js @@ -10,7 +10,7 @@ export function outroAndDestroyBlock(block, lookup) { destroyBlock(block, lookup); }); - block.o(); + block.o(1); } export function fixAndOutroAndDestroyBlock(block, lookup) { @@ -53,7 +53,7 @@ export function updateKeyedEach(old_blocks, changed, get_key, dynamic, ctx, list function insert(block) { block.m(node, next); - if (block.i) block.i(); + if (block.i) block.i(1); lookup[block.key] = block; next = block.first; n--; diff --git a/test/js/samples/component-static-array/expected.js b/test/js/samples/component-static-array/expected.js index 7fbcc273822c..7e8b1d8581c1 100644 --- a/test/js/samples/component-static-array/expected.js +++ b/test/js/samples/component-static-array/expected.js @@ -17,15 +17,15 @@ function create_fragment(ctx) { p: noop, - i() { + i(local) { if (current) return; - nested.$$.fragment.i(); + nested.$$.fragment.i(local); current = true; }, - o() { - if (nested) nested.$$.fragment.o(); + o(local) { + nested.$$.fragment.o(local); current = false; }, diff --git a/test/js/samples/component-static-immutable/expected.js b/test/js/samples/component-static-immutable/expected.js index e3dc6181153f..aa5f88b86ffe 100644 --- a/test/js/samples/component-static-immutable/expected.js +++ b/test/js/samples/component-static-immutable/expected.js @@ -17,15 +17,15 @@ function create_fragment(ctx) { p: noop, - i() { + i(local) { if (current) return; - nested.$$.fragment.i(); + nested.$$.fragment.i(local); current = true; }, - o() { - if (nested) nested.$$.fragment.o(); + o(local) { + nested.$$.fragment.o(local); current = false; }, diff --git a/test/js/samples/component-static-immutable2/expected.js b/test/js/samples/component-static-immutable2/expected.js index e3dc6181153f..aa5f88b86ffe 100644 --- a/test/js/samples/component-static-immutable2/expected.js +++ b/test/js/samples/component-static-immutable2/expected.js @@ -17,15 +17,15 @@ function create_fragment(ctx) { p: noop, - i() { + i(local) { if (current) return; - nested.$$.fragment.i(); + nested.$$.fragment.i(local); current = true; }, - o() { - if (nested) nested.$$.fragment.o(); + o(local) { + nested.$$.fragment.o(local); current = false; }, diff --git a/test/js/samples/component-static/expected.js b/test/js/samples/component-static/expected.js index 014e1caefb6a..113a85825733 100644 --- a/test/js/samples/component-static/expected.js +++ b/test/js/samples/component-static/expected.js @@ -17,15 +17,15 @@ function create_fragment(ctx) { p: noop, - i() { + i(local) { if (current) return; - nested.$$.fragment.i(); + nested.$$.fragment.i(local); current = true; }, - o() { - if (nested) nested.$$.fragment.o(); + o(local) { + nested.$$.fragment.o(local); current = false; }, diff --git a/test/js/samples/dynamic-import/expected.js b/test/js/samples/dynamic-import/expected.js index 8ccacdd398e6..7f7f8e39fde8 100644 --- a/test/js/samples/dynamic-import/expected.js +++ b/test/js/samples/dynamic-import/expected.js @@ -18,15 +18,15 @@ function create_fragment(ctx) { p: noop, - i() { + i(local) { if (current) return; - lazyload.$$.fragment.i(); + lazyload.$$.fragment.i(local); current = true; }, - o() { - if (lazyload) lazyload.$$.fragment.o(); + o(local) { + lazyload.$$.fragment.o(local); current = false; }, diff --git a/test/js/samples/non-imported-component/expected.js b/test/js/samples/non-imported-component/expected.js index a917990426f4..0da9a66b98a0 100644 --- a/test/js/samples/non-imported-component/expected.js +++ b/test/js/samples/non-imported-component/expected.js @@ -24,18 +24,18 @@ function create_fragment(ctx) { p: noop, - i() { + i(local) { if (current) return; - imported.$$.fragment.i(); + imported.$$.fragment.i(local); - nonimported.$$.fragment.i(); + nonimported.$$.fragment.i(local); current = true; }, - o() { - if (imported) imported.$$.fragment.o(); - if (nonimported) nonimported.$$.fragment.o(); + o(local) { + imported.$$.fragment.o(local); + nonimported.$$.fragment.o(local); current = false; }, diff --git a/test/js/samples/transition-local/expected.js b/test/js/samples/transition-local/expected.js new file mode 100644 index 000000000000..1e96247f1928 --- /dev/null +++ b/test/js/samples/transition-local/expected.js @@ -0,0 +1,162 @@ +/* generated by Svelte vX.Y.Z */ +import { SvelteComponent as SvelteComponent_1, add_render_callback, createComment, createElement, create_in_transition, detachNode, flush, init, insert, noop, safe_not_equal } from "svelte/internal"; + +// (8:0) {#if x} +function create_if_block(ctx) { + var if_block_anchor; + + var if_block = (ctx.y) && create_if_block_1(ctx); + + return { + c() { + if (if_block) if_block.c(); + if_block_anchor = createComment(); + }, + + m(target, anchor) { + if (if_block) if_block.m(target, anchor); + insert(target, if_block_anchor, anchor); + }, + + p(changed, ctx) { + if (ctx.y) { + if (!if_block) { + if_block = create_if_block_1(ctx); + if_block.c(); + if_block.m(if_block_anchor.parentNode, if_block_anchor); + } + if_block.i(1); + } else if (if_block) { + if_block.d(1); + if_block = null; + } + }, + + d(detach) { + if (if_block) if_block.d(detach); + + if (detach) { + detachNode(if_block_anchor); + } + } + }; +} + +// (9:1) {#if y} +function create_if_block_1(ctx) { + var div, div_intro; + + return { + c() { + div = createElement("div"); + div.textContent = "..."; + }, + + m(target, anchor) { + insert(target, div, anchor); + }, + + i(local) { + if (local) { + if (!div_intro) { + add_render_callback(() => { + div_intro = create_in_transition(div, foo, {}); + div_intro.start(); + }); + } + } + }, + + o: noop, + + d(detach) { + if (detach) { + detachNode(div); + } + } + }; +} + +function create_fragment(ctx) { + var if_block_anchor; + + var if_block = (ctx.x) && create_if_block(ctx); + + return { + c() { + if (if_block) if_block.c(); + if_block_anchor = createComment(); + }, + + m(target, anchor) { + if (if_block) if_block.m(target, anchor); + insert(target, if_block_anchor, anchor); + }, + + p(changed, ctx) { + if (ctx.x) { + if (if_block) { + if_block.p(changed, ctx); + } else { + if_block = create_if_block(ctx); + if_block.c(); + if_block.m(if_block_anchor.parentNode, if_block_anchor); + } + } else if (if_block) { + if_block.d(1); + if_block = null; + } + }, + + i: noop, + o: noop, + + d(detach) { + if (if_block) if_block.d(detach); + + if (detach) { + detachNode(if_block_anchor); + } + } + }; +} + +function foo() {} + +function instance($$self, $$props, $$invalidate) { + let { x, y } = $$props; + + $$self.$set = $$props => { + if ('x' in $$props) $$invalidate('x', x = $$props.x); + if ('y' in $$props) $$invalidate('y', y = $$props.y); + }; + + return { x, y }; +} + +class SvelteComponent extends SvelteComponent_1 { + constructor(options) { + super(); + init(this, options, instance, create_fragment, safe_not_equal); + } + + get x() { + return this.$$.ctx.x; + } + + set x(x) { + this.$set({ x }); + flush(); + } + + get y() { + return this.$$.ctx.y; + } + + set y(y) { + this.$set({ y }); + flush(); + } +} + +export default SvelteComponent; \ No newline at end of file diff --git a/test/js/samples/transition-local/input.html b/test/js/samples/transition-local/input.html new file mode 100644 index 000000000000..3f87627c6219 --- /dev/null +++ b/test/js/samples/transition-local/input.html @@ -0,0 +1,12 @@ + + +{#if x} + {#if y} +