Skip to content

Commit a9ac217

Browse files
committed
WIP
1 parent 7d977fa commit a9ac217

File tree

2 files changed

+107
-64
lines changed

2 files changed

+107
-64
lines changed
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
/** @import { Effect, TemplateNode } from '#client' */
2+
import { Batch, current_batch } from '../../reactivity/batch.js';
3+
import { branch, pause_effect, resume_effect } from '../../reactivity/effects.js';
4+
import { create_text, should_defer_append } from '../operations.js';
5+
6+
/**
7+
* @typedef {{ effect: Effect, fragment: DocumentFragment }} Branch
8+
*/
9+
10+
/**
11+
* @template Key
12+
*/
13+
export class BranchManager {
14+
/** @type {TemplateNode} */
15+
#anchor;
16+
17+
/** @type {Map<Batch, Key>} */
18+
#batches = new Map();
19+
20+
/** @type {Map<Key, Effect>} */
21+
#onscreen = new Map();
22+
23+
/** @type {Map<Key, Branch>} */
24+
#offscreen = new Map();
25+
26+
/**
27+
*
28+
* @param {TemplateNode} anchor
29+
*/
30+
constructor(anchor) {
31+
this.#anchor = anchor;
32+
}
33+
34+
#commit = () => {
35+
var batch = /** @type {Batch} */ (current_batch);
36+
var key = /** @type {Key} */ (this.#batches.get(batch));
37+
38+
var onscreen = this.#onscreen.get(key);
39+
40+
if (onscreen) {
41+
// effect is already in the DOM — abort any current outro
42+
resume_effect(onscreen);
43+
} else {
44+
// effect is currently offscreen. put it in the DOM
45+
var offscreen = this.#offscreen.get(key);
46+
if (!offscreen) throw new Error('This should never happen!');
47+
48+
this.#onscreen.set(key, offscreen.effect);
49+
this.#offscreen.delete(key);
50+
51+
// remove the anchor...
52+
/** @type {TemplateNode} */ (offscreen.fragment.lastChild).remove();
53+
54+
// ...and append the fragment
55+
this.#anchor.before(offscreen.fragment);
56+
}
57+
58+
this.#batches.delete(batch);
59+
60+
for (const [k, e] of this.#onscreen) {
61+
if (k === key) continue;
62+
63+
pause_effect(e, () => {
64+
// TODO if this needed by a pending batch, move the effect to
65+
// a fragment and put it on this.#offscreen
66+
67+
this.#onscreen.delete(k);
68+
});
69+
}
70+
};
71+
72+
/**
73+
*
74+
* @param {any} key
75+
* @param {(target: TemplateNode) => void} fn
76+
*/
77+
ensure(key, fn) {
78+
if (should_defer_append()) {
79+
var batch = /** @type {Batch} */ (current_batch);
80+
81+
if (!this.#onscreen.has(key) && !this.#offscreen.has(key)) {
82+
var fragment = document.createDocumentFragment();
83+
var target = create_text();
84+
85+
fragment.append(target);
86+
87+
this.#offscreen.set(key, {
88+
effect: branch(() => fn(target)),
89+
fragment
90+
});
91+
}
92+
93+
this.#batches.set(batch, key);
94+
95+
batch.add_callback(this.#commit);
96+
} else {
97+
this.#onscreen.set(
98+
key,
99+
branch(() => fn(this.#anchor))
100+
);
101+
}
102+
}
103+
}

packages/svelte/src/internal/client/dom/blocks/if.js

Lines changed: 4 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ import { block, branch, pause_effect, resume_effect } from '../../reactivity/eff
1414
import { HYDRATION_START_ELSE, UNINITIALIZED } from '../../../../constants.js';
1515
import { create_text, should_defer_append } from '../operations.js';
1616
import { current_batch } from '../../reactivity/batch.js';
17+
import { BranchManager } from './branches.js';
18+
import { noop } from '../../../shared/utils.js';
1719

1820
// TODO reinstate https://github.com/sveltejs/svelte/pull/15250
1921

@@ -30,12 +32,6 @@ export function if_block(node, fn, elseif = false) {
3032

3133
var anchor = node;
3234

33-
/** @type {Effect | null} */
34-
var consequent_effect = null;
35-
36-
/** @type {Effect | null} */
37-
var alternate_effect = null;
38-
3935
/** @type {typeof UNINITIALIZED | boolean | null} */
4036
var condition = UNINITIALIZED;
4137

@@ -48,42 +44,12 @@ export function if_block(node, fn, elseif = false) {
4844
update_branch(flag, fn);
4945
};
5046

51-
/** @type {DocumentFragment | null} */
52-
var offscreen_fragment = null;
53-
54-
function commit() {
55-
if (offscreen_fragment !== null) {
56-
// remove the anchor
57-
/** @type {Text} */ (offscreen_fragment.lastChild).remove();
58-
59-
anchor.before(offscreen_fragment);
60-
offscreen_fragment = null;
61-
}
62-
63-
var active = condition ? consequent_effect : alternate_effect;
64-
var inactive = condition ? alternate_effect : consequent_effect;
65-
66-
if (active) {
67-
resume_effect(active);
68-
}
69-
70-
if (inactive) {
71-
pause_effect(inactive, () => {
72-
if (condition) {
73-
alternate_effect = null;
74-
} else {
75-
consequent_effect = null;
76-
}
77-
});
78-
}
79-
}
47+
var branches = new BranchManager(anchor);
8048

8149
const update_branch = (
8250
/** @type {boolean | null} */ new_condition,
8351
/** @type {null | ((anchor: Node) => void)} */ fn
8452
) => {
85-
if (condition === (condition = new_condition)) return;
86-
8753
/** Whether or not there was a hydration mismatch. Needs to be a `let` or else it isn't treeshaken out */
8854
let mismatch = false;
8955

@@ -101,33 +67,7 @@ export function if_block(node, fn, elseif = false) {
10167
}
10268
}
10369

104-
var defer = should_defer_append();
105-
var target = anchor;
106-
107-
if (defer) {
108-
offscreen_fragment = document.createDocumentFragment();
109-
offscreen_fragment.append((target = create_text()));
110-
}
111-
112-
if (condition) {
113-
consequent_effect ??= fn && branch(() => fn(target));
114-
} else {
115-
alternate_effect ??= fn && branch(() => fn(target));
116-
}
117-
118-
if (defer) {
119-
var batch = /** @type {Batch} */ (current_batch);
120-
121-
var active = condition ? consequent_effect : alternate_effect;
122-
var inactive = condition ? alternate_effect : consequent_effect;
123-
124-
if (active) batch.skipped_effects.delete(active);
125-
if (inactive) batch.skipped_effects.add(inactive);
126-
127-
batch.add_callback(commit);
128-
} else {
129-
commit();
130-
}
70+
branches.ensure(new_condition, fn ?? noop);
13171

13272
if (mismatch) {
13373
// continue in hydration mode

0 commit comments

Comments
 (0)