Skip to content

Commit c37e6a7

Browse files
authored
Merge pull request #1451 from sveltejs/nested-transitions
Nested transitions
2 parents 338c856 + d6bcdb1 commit c37e6a7

File tree

108 files changed

+470
-41
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

108 files changed

+470
-41
lines changed

src/compile/dom/Block.ts

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import Compiler from '../Compiler';
55
import { Node } from '../../interfaces';
66

77
export interface BlockOptions {
8+
parent?: Block;
89
name: string;
910
compiler?: Compiler;
1011
comment?: string;
@@ -14,6 +15,7 @@ export interface BlockOptions {
1415
}
1516

1617
export default class Block {
18+
parent?: Block;
1719
compiler: Compiler;
1820
name: string;
1921
comment?: string;
@@ -50,6 +52,7 @@ export default class Block {
5052
autofocus: string;
5153

5254
constructor(options: BlockOptions) {
55+
this.parent = options.parent;
5356
this.compiler = options.compiler;
5457
this.name = options.name;
5558
this.comment = options.comment;
@@ -115,6 +118,15 @@ export default class Block {
115118
}
116119
}
117120

121+
addIntro() {
122+
this.hasIntroMethod = this.compiler.target.hasIntroTransitions = true;
123+
}
124+
125+
addOutro() {
126+
this.hasOutroMethod = this.compiler.target.hasOutroTransitions = true;
127+
this.outros += 1;
128+
}
129+
118130
addVariable(name: string, init?: string) {
119131
if (this.variables.has(name) && this.variables.get(name) !== init) {
120132
throw new Error(
@@ -246,11 +258,15 @@ export default class Block {
246258
},
247259
`);
248260
} else {
249-
properties.addBlock(deindent`
250-
${dev ? 'i: function intro' : 'i'}(#target, anchor) {
251-
this.m(#target, anchor);
252-
},
253-
`);
261+
if (this.builders.mount.isEmpty()) {
262+
properties.addBlock(`i: @noop,`);
263+
} else {
264+
properties.addBlock(deindent`
265+
${dev ? 'i: function intro' : 'i'}(#target, anchor) {
266+
this.m(#target, anchor);
267+
},
268+
`);
269+
}
254270
}
255271

256272
if (hasOutros) {
@@ -260,7 +276,7 @@ export default class Block {
260276
${outroing} = true;
261277
${hasIntros && `${introing} = false;`}
262278
263-
${this.outros > 1 && `var #outros = ${this.outros};`}
279+
${this.outros > 1 && `#outrocallback = @callAfter(#outrocallback, ${this.outros});`}
264280
265281
${this.builders.outro}
266282
},

src/compile/dom/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,7 @@ export default function dom(
182182
})}
183183
${compiler.bindingGroups.length &&
184184
`this._bindingGroups = [${Array(compiler.bindingGroups.length).fill('[]').join(', ')}];`}
185+
this._intro = ${compiler.options.skipIntroByDefault ? 'options.intro' : 'true'};
185186
186187
${templateProperties.onstate && `this._handlers.state = [%onstate];`}
187188
${templateProperties.onupdate && `this._handlers.update = [%onupdate];`}
@@ -251,6 +252,8 @@ export default function dom(
251252
`}
252253
}
253254
`}
255+
256+
${compiler.options.skipIntroByDefault && `this._intro = true;`}
254257
`;
255258

256259
if (compiler.customElement) {

src/compile/nodes/AwaitBlock.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,8 @@ export default class AwaitBlock extends Node {
7676
this.pending.block.hasOutroMethod = hasOutros;
7777
this.then.block.hasOutroMethod = hasOutros;
7878
this.catch.block.hasOutroMethod = hasOutros;
79+
80+
if (hasOutros && this.compiler.options.nestedTransitions) block.addOutro();
7981
}
8082

8183
build(
@@ -169,6 +171,17 @@ export default class AwaitBlock extends Node {
169171
`);
170172
}
171173

174+
if (this.pending.block.hasOutroMethod && this.compiler.options.nestedTransitions) {
175+
block.builders.outro.addBlock(deindent`
176+
#outrocallback = @callAfter(#outrocallback, 3);
177+
for (let #i = 0; #i < 3; #i += 1) {
178+
const block = ${info}.blocks[#i];
179+
if (block) block.o(#outrocallback);
180+
else #outrocallback();
181+
}
182+
`);
183+
}
184+
172185
block.builders.destroy.addBlock(deindent`
173186
${info}.block.d(${parentNode ? '' : 'detach'});
174187
${info} = null;

src/compile/nodes/Component.ts

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,10 @@ export default class Component extends Node {
108108
child.init(block, stripWhitespace, nextSibling);
109109
});
110110
}
111+
112+
if (this.compiler.options.nestedTransitions) {
113+
block.addOutro();
114+
}
111115
}
112116

113117
build(
@@ -383,7 +387,7 @@ export default class Component extends Node {
383387

384388
block.builders.mount.addBlock(deindent`
385389
if (${name}) {
386-
${name}._mount(${parentNode || '#target'}, ${parentNode ? 'null' : 'anchor'}, ${compiler.options.skipIntroByDefault ? 'false' : 'true'});
390+
${name}._mount(${parentNode || '#target'}, ${parentNode ? 'null' : 'anchor'}, ${compiler.options.skipIntroByDefault ? '#component._intro' : 'true'});
387391
${this.ref && `#component.refs.${this.ref} = ${name};`}
388392
}
389393
`);
@@ -405,7 +409,7 @@ export default class Component extends Node {
405409
${name}._fragment.c();
406410
407411
${this.children.map(child => child.remount(name))}
408-
${name}._mount(${updateMountNode}, ${anchor}, ${compiler.options.skipIntroByDefault ? 'false' : 'true'});
412+
${name}._mount(${updateMountNode}, ${anchor}, true);
409413
410414
${this.handlers.map(handler => deindent`
411415
${name}.on("${handler.name}", ${handler.var});
@@ -464,7 +468,7 @@ export default class Component extends Node {
464468
}
465469

466470
block.builders.mount.addLine(
467-
`${name}._mount(${parentNode || '#target'}, ${parentNode ? 'null' : 'anchor'}, ${compiler.options.skipIntroByDefault ? 'false' : 'true'});`
471+
`${name}._mount(${parentNode || '#target'}, ${parentNode ? 'null' : 'anchor'}, ${compiler.options.skipIntroByDefault ? '#component._intro' : 'true'});`
468472
);
469473

470474
if (updates.length) {
@@ -480,10 +484,16 @@ export default class Component extends Node {
480484
${this.ref && `if (#component.refs.${this.ref} === ${name}) #component.refs.${this.ref} = null;`}
481485
`);
482486
}
487+
488+
if (this.compiler.options.nestedTransitions) {
489+
block.builders.outro.addLine(
490+
`${name}._fragment.o(#outrocallback);`
491+
);
492+
}
483493
}
484494

485495
remount(name: string) {
486-
return `${this.var}._mount(${name}._slotted.default, null, ${this.compiler.options.skipIntroByDefault ? 'false' : 'true'});`;
496+
return `${this.var}._mount(${name}._slotted.default, null, false);`;
487497
}
488498

489499
ssr() {

src/compile/nodes/EachBlock.ts

Lines changed: 42 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,10 @@ export default class EachBlock extends Node {
118118
);
119119
this.else.block.hasUpdateMethod = this.else.block.dependencies.size > 0;
120120
}
121+
122+
if (this.block.hasOutroMethod || (this.else && this.else.block.hasOutroMethod)) {
123+
block.addOutro();
124+
}
121125
}
122126

123127
build(
@@ -313,9 +317,17 @@ export default class EachBlock extends Node {
313317
block.builders.update.addBlock(deindent`
314318
var ${this.each_block_value} = ${snippet};
315319
320+
${this.block.hasOutroMethod && `@transitionManager.groupOutros();`}
316321
${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});
317322
`);
318323

324+
if (this.compiler.options.nestedTransitions) {
325+
block.builders.outro.addBlock(deindent`
326+
#outrocallback = @callAfter(#outrocallback, ${blocks}.length);
327+
for (#i = 0; #i < ${blocks}.length; #i += 1) ${blocks}[#i].o(#outrocallback);
328+
`);
329+
}
330+
319331
block.builders.destroy.addBlock(deindent`
320332
for (#i = 0; #i < ${blocks}.length; #i += 1) ${blocks}[#i].d(${parentNode ? '' : 'detach'});
321333
`);
@@ -372,6 +384,21 @@ export default class EachBlock extends Node {
372384
allDependencies.add(dependency);
373385
});
374386

387+
const outro = this.block.hasOutroMethod && block.getUniqueName('outro')
388+
if (outro) {
389+
block.builders.init.addBlock(deindent`
390+
function ${outro}(i, detach, fn) {
391+
if (${iterations}[i]) {
392+
${iterations}[i].o(() => {
393+
${iterations}[i].d(detach);
394+
if (detach) ${iterations}[i] = null;
395+
if (fn) fn();
396+
});
397+
}
398+
}
399+
`);
400+
}
401+
375402
// TODO do this for keyed blocks as well
376403
const condition = Array.from(allDependencies)
377404
.map(dependency => `changed.${dependency}`)
@@ -406,27 +433,21 @@ export default class EachBlock extends Node {
406433

407434
const start = this.block.hasUpdateMethod ? '0' : `${iterations}.length`;
408435

409-
const outro = block.getUniqueName('outro');
410-
const destroy = this.block.hasOutroMethod
411-
? deindent`
412-
function ${outro}(i) {
413-
if (${iterations}[i]) {
414-
${iterations}[i].o(function() {
415-
${iterations}[i].d(1);
416-
${iterations}[i] = null;
417-
});
418-
}
419-
}
436+
let destroy;
420437

438+
if (this.block.hasOutroMethod) {
439+
destroy = deindent`
421440
@transitionManager.groupOutros();
422-
for (; #i < ${iterations}.length; #i += 1) ${outro}(#i);
423-
`
424-
: deindent`
441+
for (; #i < ${iterations}.length; #i += 1) ${outro}(#i, 1);
442+
`;
443+
} else {
444+
destroy = deindent`
425445
for (; #i < ${iterations}.length; #i += 1) {
426446
${iterations}[#i].d(1);
427447
}
428448
${iterations}.length = ${this.each_block_value}.${length};
429449
`;
450+
}
430451

431452
block.builders.update.addBlock(deindent`
432453
if (${condition}) {
@@ -443,6 +464,13 @@ export default class EachBlock extends Node {
443464
`);
444465
}
445466

467+
if (outro && this.compiler.options.nestedTransitions) {
468+
block.builders.outro.addBlock(deindent`
469+
#outrocallback = @callAfter(#outrocallback, #i);
470+
for (let #i = 0; #i < ${iterations}.length; #i += 1) ${outro}(#i, 0, #outrocallback);`
471+
);
472+
}
473+
446474
block.builders.destroy.addBlock(`@destroyEach(${iterations}, detach);`);
447475
}
448476

src/compile/nodes/Element.ts

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -182,13 +182,12 @@ export default class Element extends Node {
182182

183183
if (this.intro) {
184184
this.parent.cannotUseInnerHTML();
185-
this.compiler.target.hasIntroTransitions = block.hasIntroMethod = true;
185+
block.addIntro();
186186
}
187187

188188
if (this.outro) {
189189
this.parent.cannotUseInnerHTML();
190-
this.compiler.target.hasOutroTransitions = block.hasOutroMethod = true;
191-
block.outros += 1;
190+
block.addOutro();
192191
}
193192

194193
if (this.ref) {
@@ -711,7 +710,7 @@ export default class Element extends Node {
711710

712711
block.builders.outro.addBlock(deindent`
713712
${name}.run(0, () => {
714-
${block.outros > 1 ? `if (--#outros === 0) #outrocallback();` : `#outrocallback();`}
713+
#outrocallback();
715714
${name} = null;
716715
});
717716
`);
@@ -758,9 +757,7 @@ export default class Element extends Node {
758757
// group) prior to their removal from the DOM
759758
block.builders.outro.addBlock(deindent`
760759
${outroName} = @wrapTransition(#component, ${this.var}, ${fn}, ${snippet}, false);
761-
${outroName}.run(0, () => {
762-
${block.outros > 1 ? `if (--#outros === 0) #outrocallback();` : `#outrocallback();`}
763-
});
760+
${outroName}.run(0, #outrocallback);
764761
`);
765762
}
766763
}

src/compile/nodes/IfBlock.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,11 @@ export default class IfBlock extends Node {
9595

9696
attachBlocks(this);
9797

98+
if (compiler.options.nestedTransitions) {
99+
if (hasIntros) block.addIntro();
100+
if (hasOutros) block.addOutro();
101+
}
102+
98103
blocks.forEach(block => {
99104
block.hasUpdateMethod = dynamic;
100105
block.hasIntroMethod = hasIntros;
@@ -129,11 +134,23 @@ export default class IfBlock extends Node {
129134
if (this.else) {
130135
if (hasOutros) {
131136
this.buildCompoundWithOutros(block, parentNode, parentNodes, branches, dynamic, vars);
137+
138+
if (this.compiler.options.nestedTransitions) {
139+
block.builders.outro.addLine(
140+
`${name}.o(#outrocallback);`
141+
);
142+
}
132143
} else {
133144
this.buildCompound(block, parentNode, parentNodes, branches, dynamic, vars);
134145
}
135146
} else {
136147
this.buildSimple(block, parentNode, parentNodes, branches[0], dynamic, vars);
148+
149+
if (hasOutros && this.compiler.options.nestedTransitions) {
150+
block.builders.outro.addLine(
151+
`if (${name}) ${name}.o(#outrocallback);`
152+
);
153+
}
137154
}
138155

139156
block.builders.create.addLine(`${if_name}${name}.c();`);

src/interfaces.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ export interface CompileOptions {
6767

6868
// to remove in v3
6969
skipIntroByDefault?: boolean;
70+
nestedTransitions: boolean;
7071
}
7172

7273
export interface GenerateOptions {

src/shared/keyed-each.js

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
import { transitionManager } from './transitions.js';
2-
31
export function destroyBlock(block, lookup) {
42
block.d(1);
53
lookup[block.key] = null;
@@ -45,7 +43,6 @@ export function updateKeyedEach(old_blocks, component, changed, get_key, dynamic
4543
var did_move = {};
4644

4745
var destroy = has_outro ? outroAndDestroyBlock : destroyBlock;
48-
if (has_outro) transitionManager.groupOutros();
4946

5047
function insert(block) {
5148
block[intro_method](node, next);

src/shared/utils.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,10 @@ export function assignTrue(tar, src) {
1212

1313
export function isPromise(value) {
1414
return value && typeof value.then === 'function';
15+
}
16+
17+
export function callAfter(fn, i) {
18+
return () => {
19+
if (!--i) fn();
20+
};
1521
}

test/cli/samples/basic/expected/Main.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ function create_main_fragment(component, ctx) {
2626
function Main(options) {
2727
init(this, options);
2828
this._state = assign({}, options.data);
29+
this._intro = true;
2930

3031
this._fragment = create_main_fragment(this, this._state);
3132

test/cli/samples/custom-element/expected/Main.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ class Main extends HTMLElement {
2929
super();
3030
init(this, options);
3131
this._state = assign({}, options.data);
32+
this._intro = true;
3233

3334
this.attachShadow({ mode: 'open' });
3435

0 commit comments

Comments
 (0)