ripple 0.4.1 → 0.4.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +162 -0
- package/package.json +9 -3
- package/src/constants.js +4 -0
- package/src/jsx-runtime.d.ts +2 -1
- package/src/runtime/array.js +15 -9
- package/src/runtime/index-client.js +25 -7
- package/src/runtime/index-server.js +1 -0
- package/src/runtime/internal/client/attributes.js +313 -0
- package/src/runtime/internal/client/blocks.js +4 -14
- package/src/runtime/internal/client/component.js +2 -5
- package/src/runtime/internal/client/composite.js +14 -6
- package/src/runtime/internal/client/constants.js +12 -6
- package/src/runtime/internal/client/context.js +3 -2
- package/src/runtime/internal/client/css.js +11 -26
- package/src/runtime/internal/client/errors.js +188 -0
- package/src/runtime/internal/client/events.js +79 -34
- package/src/runtime/internal/client/expression.js +21 -116
- package/src/runtime/internal/client/for.js +272 -222
- package/src/runtime/internal/client/head.js +4 -3
- package/src/runtime/internal/client/html.js +5 -3
- package/src/runtime/internal/client/hydrate.js +484 -0
- package/src/runtime/internal/client/hydration-enabled.js +7 -0
- package/src/runtime/internal/client/hydration.js +16 -97
- package/src/runtime/internal/client/if.js +13 -9
- package/src/runtime/internal/client/index.js +16 -8
- package/src/runtime/internal/client/operations.js +12 -9
- package/src/runtime/internal/client/portal.js +3 -2
- package/src/runtime/internal/client/render.js +75 -321
- package/src/runtime/internal/client/root-boundary-enabled.js +7 -0
- package/src/runtime/internal/client/runtime.js +129 -362
- package/src/runtime/internal/client/template-ns.js +47 -0
- package/src/runtime/internal/client/template.js +63 -140
- package/src/runtime/internal/client/track-async.js +356 -0
- package/src/runtime/internal/client/try.js +133 -319
- package/src/runtime/internal/client/types.d.ts +48 -24
- package/src/runtime/internal/server/index.js +38 -33
- package/src/runtime/internal/server/prerender.js +27 -0
- package/src/server/index.js +1 -0
- package/src/utils/class-name.js +28 -0
- package/tests/client/basic/basic.errors.test.tsrx +3 -3
- package/tests/client/compiler/__snapshots__/compiler.typescript.test.tsrx.snap +0 -2
- package/tests/client/compiler/compiler.basic.test.tsrx +4 -4
- package/tests/client/compiler/compiler.tracked-access.test.tsrx +3 -3
- package/tests/client/composite/composite.props.test.tsrx +10 -10
- package/tests/client/derived-release.test.tsrx +3 -3
- package/tests/client/for-item.test.tsrx +443 -0
- package/tests/client/for-selector.test.tsrx +95 -0
- package/tests/hydration/compiled/client/basic.js +97 -123
- package/tests/hydration/compiled/client/composite.js +5 -5
- package/tests/hydration/compiled/client/events.js +84 -79
- package/tests/hydration/compiled/client/for.js +229 -233
- package/tests/hydration/compiled/client/fragment-cursor.js +781 -481
- package/tests/hydration/compiled/client/head.js +23 -26
- package/tests/hydration/compiled/client/hmr.js +4 -4
- package/tests/hydration/compiled/client/html-in-template.js +5 -5
- package/tests/hydration/compiled/client/html.js +174 -208
- package/tests/hydration/compiled/client/if-children.js +71 -70
- package/tests/hydration/compiled/client/if-fragment-controlflow.js +46 -46
- package/tests/hydration/compiled/client/if.js +26 -26
- package/tests/hydration/compiled/client/mixed-control-flow.js +63 -105
- package/tests/hydration/compiled/client/nested-control-flow.js +170 -390
- package/tests/hydration/compiled/client/portal.js +10 -10
- package/tests/hydration/compiled/client/reactivity.js +36 -50
- package/tests/hydration/compiled/client/return.js +2 -2
- package/tests/hydration/compiled/client/streaming.js +34 -40
- package/tests/hydration/compiled/client/switch.js +25 -25
- package/tests/hydration/compiled/client/track-async-serialization.js +41 -41
- package/tests/hydration/compiled/client/try.js +15 -15
- package/tests/hydration/compiled/client/typed-text.js +8 -19
- package/tests/hydration/compiled/server/basic.js +6 -6
- package/tests/hydration/compiled/server/events.js +13 -61
- package/tests/hydration/compiled/server/for.js +23 -31
- package/tests/hydration/compiled/server/fragment-cursor.js +214 -366
- package/tests/hydration/compiled/server/head.js +6 -6
- package/tests/hydration/compiled/server/hmr.js +1 -1
- package/tests/hydration/compiled/server/html.js +1 -1
- package/tests/hydration/compiled/server/if-children.js +9 -9
- package/tests/hydration/compiled/server/if.js +5 -5
- package/tests/hydration/compiled/server/mixed-control-flow.js +4 -4
- package/tests/hydration/compiled/server/portal.js +1 -1
- package/tests/hydration/compiled/server/reactivity.js +9 -9
- package/tests/hydration/compiled/server/streaming.js +9 -9
- package/tests/hydration/compiled/server/switch.js +4 -4
- package/tests/hydration/compiled/server/track-async-serialization.js +16 -16
- package/tests/hydration/compiled/server/try.js +4 -4
- package/tests/hydration/compiled/server/typed-text.js +1 -1
- package/tests/hydration/components/fragment-cursor.tsrx +60 -0
- package/tests/hydration/components/track-async-serialization.tsrx +2 -2
- package/tests/hydration/fragment-cursor.test.js +34 -1
- package/tests/hydration/track-async-serialization.test.js +1 -1
- package/tests/server/basic.components.test.tsrx +3 -3
- package/tests/server/basic.test.tsrx +23 -0
- package/tests/server/track-async-serialization.test.tsrx +6 -6
- package/tests/utils/tracked-types.test.js +3 -3
- package/types/index.d.ts +8 -8
- package/types/server.d.ts +17 -0
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
/** @import { AppendIntoAnchor, Block, Tracked } from '#client' */
|
|
2
2
|
|
|
3
|
-
import { IS_CONTROLLED, IS_INDEXED, ROOT_CONTROLLED } from '../../../constants.js';
|
|
3
|
+
import { IS_CONTROLLED, IS_INDEXED, LOCAL_ITEMS, ROOT_CONTROLLED } from '../../../constants.js';
|
|
4
4
|
import {
|
|
5
5
|
block as create_block,
|
|
6
|
+
create_block as allocate_block,
|
|
6
7
|
branch,
|
|
7
8
|
destroy_block,
|
|
8
9
|
destroy_block_children,
|
|
@@ -11,55 +12,97 @@ import {
|
|
|
11
12
|
own_anchor,
|
|
12
13
|
render,
|
|
13
14
|
} from './blocks.js';
|
|
14
|
-
import {
|
|
15
|
+
import {
|
|
16
|
+
BLOCK_HAS_RUN,
|
|
17
|
+
BRANCH_BLOCK,
|
|
18
|
+
DESTROYED,
|
|
19
|
+
FOR_BLOCK,
|
|
20
|
+
ITEM_BLOCK,
|
|
21
|
+
TRACKED_ARRAY,
|
|
22
|
+
} from './constants.js';
|
|
15
23
|
import { hydrate_first_child, hydrate_next, hydrate_node, hydrating } from './hydration.js';
|
|
16
24
|
import { get_last_child, next_sibling, resolve_anchor } from './operations.js';
|
|
17
25
|
import { append } from './template.js';
|
|
18
|
-
import { active_block, set, set_tracking, tracked } from './runtime.js';
|
|
26
|
+
import { active_block, run_block, run_branch, set, set_tracking, tracked } from './runtime.js';
|
|
19
27
|
import { array_from, is_array } from '@tsrx/core/runtime/language-helpers';
|
|
28
|
+
import { HYDRATION } from 'ripple/internal/client/hydration-enabled';
|
|
20
29
|
|
|
21
30
|
/**
|
|
22
31
|
* @template V
|
|
23
32
|
* @param {Node | AppendIntoAnchor} anchor
|
|
24
33
|
* @param {V} value
|
|
25
34
|
* @param {number} index
|
|
26
|
-
* @param {
|
|
27
|
-
* @param {
|
|
28
|
-
* @param {boolean} is_keyed
|
|
29
|
-
* @param {((item: V) => any) | undefined} [map_item] a keyed loop's destructuring of the item (see `for_block_keyed`)
|
|
35
|
+
* @param {any} key the key the item was matched by (keyed lists)
|
|
36
|
+
* @param {ListState} state the list's state: how items render
|
|
30
37
|
* @returns {Block}
|
|
31
38
|
*/
|
|
32
|
-
function create_item(anchor, value, index,
|
|
39
|
+
function create_item(anchor, value, index, key, state) {
|
|
33
40
|
var block = /** @type {Block} */ (active_block);
|
|
34
|
-
var
|
|
35
|
-
var
|
|
41
|
+
var render_fn = state.r;
|
|
42
|
+
var map_item = state.m;
|
|
43
|
+
var tracked_index = state.x ? tracked(index, block) : undefined;
|
|
44
|
+
var tracked_value = state.y
|
|
36
45
|
? tracked(map_item === undefined ? value : map_item(value), block)
|
|
37
46
|
: value;
|
|
38
|
-
var
|
|
47
|
+
var item_state = {
|
|
39
48
|
start: null,
|
|
40
49
|
end: null,
|
|
41
50
|
i: tracked_index,
|
|
42
51
|
v: tracked_value,
|
|
43
52
|
// The item as the collection holds it; a keyed diff compares against it.
|
|
44
53
|
r: value,
|
|
54
|
+
// The key the item was matched by: fixed for the block's whole life, so
|
|
55
|
+
// the body reads it as a plain value (a compiled `@if (outer === item.key)`
|
|
56
|
+
// depends on the selector alone, not on the item).
|
|
57
|
+
k: key,
|
|
58
|
+
// The render state of a body that carries its render block on the item
|
|
59
|
+
// block itself (see `item`), or null.
|
|
60
|
+
p: null,
|
|
45
61
|
};
|
|
46
62
|
|
|
63
|
+
// An item without a tracked of its own (a plain list's, or a local one)
|
|
64
|
+
// renders in place: allocated, linked and run through `run_branch`, with
|
|
65
|
+
// none of the first-run bookkeeping a block that may re-run needs.
|
|
66
|
+
if (tracked_value === value && tracked_index === undefined) {
|
|
67
|
+
var lean = allocate_block(BRANCH_BLOCK, run_item, item_state);
|
|
68
|
+
run_branch(lean, render_fn, anchor, value, key);
|
|
69
|
+
lean.f ^= BLOCK_HAS_RUN;
|
|
70
|
+
return lean;
|
|
71
|
+
}
|
|
72
|
+
|
|
47
73
|
// Passed through module state rather than a per-item closure; run_item
|
|
48
74
|
// reads them before rendering, so nested loops cannot observe a stale pair.
|
|
49
75
|
item_anchor = anchor;
|
|
50
76
|
item_render_fn = render_fn;
|
|
51
|
-
var b = create_block(BRANCH_BLOCK, run_item,
|
|
77
|
+
var b = create_block(BRANCH_BLOCK, run_item, item_state);
|
|
52
78
|
|
|
53
79
|
// The item's tracked value and index are owned by the item block itself.
|
|
54
|
-
if (
|
|
80
|
+
if (tracked_value !== value) {
|
|
55
81
|
/** @type {Tracked} */ (tracked_value).b = b;
|
|
56
82
|
}
|
|
57
|
-
if (
|
|
83
|
+
if (tracked_index !== undefined) {
|
|
58
84
|
/** @type {Tracked} */ (tracked_index).b = b;
|
|
59
85
|
}
|
|
60
86
|
return b;
|
|
61
87
|
}
|
|
62
88
|
|
|
89
|
+
/**
|
|
90
|
+
* A matched item at its new index: its tracked index and, for a computed key,
|
|
91
|
+
* its tracked value follow the collection.
|
|
92
|
+
* @param {ListState} state
|
|
93
|
+
* @param {Block} block
|
|
94
|
+
* @param {any} value
|
|
95
|
+
* @param {number} index
|
|
96
|
+
*/
|
|
97
|
+
function patch_item(state, block, value, index) {
|
|
98
|
+
if (state.x) {
|
|
99
|
+
update_index(block, index);
|
|
100
|
+
}
|
|
101
|
+
if (state.k !== undefined) {
|
|
102
|
+
update_value(block, value, state.m, state.l);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
63
106
|
/**
|
|
64
107
|
* The insertion point of a list: a node the items go before, or, for a
|
|
65
108
|
* controlled list (the sole content of its parent), an append-into sentinel so
|
|
@@ -72,23 +115,56 @@ function create_item(anchor, value, index, render_fn, is_indexed, is_keyed, map_
|
|
|
72
115
|
* @returns {AppendIntoAnchor}
|
|
73
116
|
*/
|
|
74
117
|
function controlled_anchor(parent) {
|
|
75
|
-
return { parent, into: true };
|
|
118
|
+
return { parent, into: true, tail: false };
|
|
76
119
|
}
|
|
77
120
|
|
|
78
121
|
/** @type {Node | AppendIntoAnchor | null} */
|
|
79
122
|
var item_anchor = null;
|
|
80
|
-
/** @type {((anchor: Node, value: any, index?: any) => Block) | null} */
|
|
123
|
+
/** @type {((anchor: Node, value: any, index?: any, key?: any) => Block) | null} */
|
|
81
124
|
var item_render_fn = null;
|
|
82
125
|
|
|
83
126
|
/**
|
|
84
|
-
* @param {{ i: Tracked | undefined, v: any }} state
|
|
127
|
+
* @param {{ i: Tracked | undefined, v: any, k: any, p: any }} state
|
|
85
128
|
*/
|
|
86
129
|
function run_item(state) {
|
|
87
|
-
var
|
|
130
|
+
var p = state.p;
|
|
131
|
+
if (p !== null) {
|
|
132
|
+
// A re-run of an item that carries its render block: only the body's
|
|
133
|
+
// update function, tracked (see `item`).
|
|
134
|
+
set_tracking(true);
|
|
135
|
+
/** @type {(prev: any) => void} */ (
|
|
136
|
+
/** @type {ListState} */ (/** @type {Block} */ (/** @type {Block} */ (active_block).p).s).u
|
|
137
|
+
)(p);
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
var render_fn = /** @type {(anchor: Node, value: any, index?: any, key?: any) => Block} */ (
|
|
141
|
+
item_render_fn
|
|
142
|
+
);
|
|
88
143
|
var anchor = /** @type {Node} */ (item_anchor);
|
|
89
144
|
item_render_fn = null;
|
|
90
145
|
item_anchor = null;
|
|
91
|
-
render_fn(anchor, state.v, state.i);
|
|
146
|
+
render_fn(anchor, state.v, state.i, state.k);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* The compiled body of a list item whose updates all live in one render block
|
|
151
|
+
* calls this where it would have created that block: the item block carries
|
|
152
|
+
* the render block instead. The update function is the list's (passed to
|
|
153
|
+
* `for`/`for_keyed`, the same for every item) and `prev` its state; it runs
|
|
154
|
+
* tracked here, so its dependencies are the item block's own, and a change
|
|
155
|
+
* re-runs it alone through `run_item`. One block, one run, and one state
|
|
156
|
+
* object less per item.
|
|
157
|
+
* @param {any} prev
|
|
158
|
+
*/
|
|
159
|
+
export function item(prev) {
|
|
160
|
+
var block = /** @type {Block} */ (active_block);
|
|
161
|
+
block.s.p = prev;
|
|
162
|
+
block.f |= ITEM_BLOCK;
|
|
163
|
+
set_tracking(true);
|
|
164
|
+
/** @type {(prev: any) => void} */ (
|
|
165
|
+
/** @type {ListState} */ (/** @type {Block} */ (block.p).s).u
|
|
166
|
+
)(prev);
|
|
167
|
+
set_tracking(false);
|
|
92
168
|
}
|
|
93
169
|
|
|
94
170
|
/**
|
|
@@ -236,7 +312,11 @@ class ListState {
|
|
|
236
312
|
* @param {boolean} is_indexed
|
|
237
313
|
* @param {((item: any) => any) | undefined} get_key
|
|
238
314
|
* @param {((anchor: Node) => void) | undefined} render_empty
|
|
239
|
-
* @param {((item: any) => any) | undefined}
|
|
315
|
+
* @param {((item: any) => any) | undefined} map_item
|
|
316
|
+
* @param {((prev: any) => void) | undefined} update_fn the update function
|
|
317
|
+
* of an item body that carries its render block (see `item`)
|
|
318
|
+
* @param {boolean} is_local items are held as they are (`LOCAL_ITEMS`)
|
|
319
|
+
* @param {boolean} is_keyed each item holds a tracked of its value
|
|
240
320
|
*/
|
|
241
321
|
constructor(
|
|
242
322
|
anchor,
|
|
@@ -247,6 +327,9 @@ class ListState {
|
|
|
247
327
|
get_key,
|
|
248
328
|
render_empty,
|
|
249
329
|
map_item,
|
|
330
|
+
update_fn,
|
|
331
|
+
is_local,
|
|
332
|
+
is_keyed,
|
|
250
333
|
) {
|
|
251
334
|
this.a = anchor;
|
|
252
335
|
this.g = get_collection;
|
|
@@ -256,6 +339,9 @@ class ListState {
|
|
|
256
339
|
this.k = get_key;
|
|
257
340
|
this.e = render_empty;
|
|
258
341
|
this.m = map_item;
|
|
342
|
+
this.u = update_fn;
|
|
343
|
+
this.l = is_local;
|
|
344
|
+
this.y = is_keyed;
|
|
259
345
|
}
|
|
260
346
|
}
|
|
261
347
|
|
|
@@ -268,13 +354,13 @@ function run_for(state) {
|
|
|
268
354
|
|
|
269
355
|
// Items render untracked, as in a branch; the list tracks only its collection.
|
|
270
356
|
set_tracking(false);
|
|
271
|
-
reconcile_by_ref(state.a, block, array
|
|
357
|
+
reconcile_by_ref(state.a, block, array);
|
|
272
358
|
set_tracking(true);
|
|
273
359
|
|
|
274
360
|
// Re-anchor a hydrated list: the hydrated anchor is the block's start
|
|
275
361
|
// marker; later inserts and end moves must go before the cursor, which now
|
|
276
362
|
// sits after the hydrated items.
|
|
277
|
-
if (hydrating) {
|
|
363
|
+
if (HYDRATION && hydrating) {
|
|
278
364
|
state.a = /** @type {Element | Text} */ (hydrate_node);
|
|
279
365
|
}
|
|
280
366
|
}
|
|
@@ -287,10 +373,10 @@ function run_for_keyed(state) {
|
|
|
287
373
|
var array = collection_to_array(state.g());
|
|
288
374
|
|
|
289
375
|
set_tracking(false);
|
|
290
|
-
reconcile_by_key(state.a, block, array
|
|
376
|
+
reconcile_by_key(state.a, block, array);
|
|
291
377
|
set_tracking(true);
|
|
292
378
|
|
|
293
|
-
if (hydrating) {
|
|
379
|
+
if (HYDRATION && hydrating) {
|
|
294
380
|
state.a = /** @type {Element | Text} */ (hydrate_node);
|
|
295
381
|
}
|
|
296
382
|
}
|
|
@@ -302,9 +388,11 @@ function run_for_keyed(state) {
|
|
|
302
388
|
* @param {(anchor: Node, value: V | Tracked, index?: any) => Block} render_fn
|
|
303
389
|
* @param {number} flags
|
|
304
390
|
* @param {(anchor: Node) => void} [render_empty]
|
|
391
|
+
* @param {(prev: any) => void} [update_fn] for an item body that carries its
|
|
392
|
+
* render block: its update function (see `item`)
|
|
305
393
|
* @returns {void}
|
|
306
394
|
*/
|
|
307
|
-
export function for_block(node, get_collection, render_fn, flags, render_empty) {
|
|
395
|
+
export function for_block(node, get_collection, render_fn, flags, render_empty, update_fn) {
|
|
308
396
|
var is_controlled = (flags & IS_CONTROLLED) !== 0;
|
|
309
397
|
var is_indexed = (flags & IS_INDEXED) !== 0;
|
|
310
398
|
var root_controlled = (flags & ROOT_CONTROLLED) !== 0;
|
|
@@ -315,7 +403,7 @@ export function for_block(node, get_collection, render_fn, flags, render_empty)
|
|
|
315
403
|
var boundary;
|
|
316
404
|
|
|
317
405
|
if (is_controlled) {
|
|
318
|
-
if (hydrating) {
|
|
406
|
+
if (HYDRATION && hydrating) {
|
|
319
407
|
// The cursor sits on the list's element; the items are its children.
|
|
320
408
|
hydrate_first_child();
|
|
321
409
|
} else {
|
|
@@ -323,7 +411,7 @@ export function for_block(node, get_collection, render_fn, flags, render_empty)
|
|
|
323
411
|
}
|
|
324
412
|
}
|
|
325
413
|
|
|
326
|
-
if (hydrating) {
|
|
414
|
+
if (HYDRATION && hydrating) {
|
|
327
415
|
if (root_controlled) {
|
|
328
416
|
boundary = /** @type {Node} */ (hydrate_node);
|
|
329
417
|
}
|
|
@@ -340,13 +428,17 @@ export function for_block(node, get_collection, render_fn, flags, render_empty)
|
|
|
340
428
|
is_indexed,
|
|
341
429
|
undefined,
|
|
342
430
|
render_empty,
|
|
431
|
+
undefined,
|
|
432
|
+
update_fn,
|
|
433
|
+
false,
|
|
434
|
+
false,
|
|
343
435
|
),
|
|
344
436
|
FOR_BLOCK,
|
|
345
437
|
);
|
|
346
438
|
|
|
347
439
|
if (!is_controlled) own_anchor(node, /** @type {Node} */ (anchor));
|
|
348
440
|
|
|
349
|
-
if (hydrating && root_controlled) {
|
|
441
|
+
if (HYDRATION && hydrating && root_controlled) {
|
|
350
442
|
// The original `node`: for a sentinel, `hydrate_append` performs the
|
|
351
443
|
// cursor advance that stands in for the eliminated sibling navigation.
|
|
352
444
|
append(/** @type {ChildNode} */ (node), /** @type {Node} */ (boundary));
|
|
@@ -365,6 +457,8 @@ export function for_block(node, get_collection, render_fn, flags, render_empty)
|
|
|
365
457
|
* @param {(item: V) => any} [map_item] for a destructuring pattern with a rest
|
|
366
458
|
* element or a default: destructures the item once, natively, into an object
|
|
367
459
|
* of the pattern's names, which is what the item's tracked then holds
|
|
460
|
+
* @param {(prev: any) => void} [update_fn] for an item body that carries its
|
|
461
|
+
* render block: its update function (see `item`)
|
|
368
462
|
* @returns {void}
|
|
369
463
|
*/
|
|
370
464
|
export function for_block_keyed(
|
|
@@ -375,6 +469,7 @@ export function for_block_keyed(
|
|
|
375
469
|
get_key,
|
|
376
470
|
render_empty,
|
|
377
471
|
map_item,
|
|
472
|
+
update_fn,
|
|
378
473
|
) {
|
|
379
474
|
var is_controlled = (flags & IS_CONTROLLED) !== 0;
|
|
380
475
|
var is_indexed = (flags & IS_INDEXED) !== 0;
|
|
@@ -386,7 +481,7 @@ export function for_block_keyed(
|
|
|
386
481
|
if (is_controlled) {
|
|
387
482
|
var parent_node = /** @type {Element} */ (node);
|
|
388
483
|
|
|
389
|
-
if (hydrating) {
|
|
484
|
+
if (HYDRATION && hydrating) {
|
|
390
485
|
hydrate_first_child();
|
|
391
486
|
anchor = /** @type {Element | Text} */ (get_last_child(parent_node));
|
|
392
487
|
} else {
|
|
@@ -394,7 +489,7 @@ export function for_block_keyed(
|
|
|
394
489
|
}
|
|
395
490
|
}
|
|
396
491
|
|
|
397
|
-
if (hydrating) {
|
|
492
|
+
if (HYDRATION && hydrating) {
|
|
398
493
|
if (root_controlled) {
|
|
399
494
|
boundary = /** @type {Node} */ (hydrate_node);
|
|
400
495
|
}
|
|
@@ -412,13 +507,16 @@ export function for_block_keyed(
|
|
|
412
507
|
get_key,
|
|
413
508
|
render_empty,
|
|
414
509
|
map_item,
|
|
510
|
+
update_fn,
|
|
511
|
+
(flags & LOCAL_ITEMS) !== 0,
|
|
512
|
+
(flags & LOCAL_ITEMS) === 0,
|
|
415
513
|
),
|
|
416
514
|
FOR_BLOCK,
|
|
417
515
|
);
|
|
418
516
|
|
|
419
517
|
if (!is_controlled) own_anchor(node, /** @type {Node} */ (anchor));
|
|
420
518
|
|
|
421
|
-
if (hydrating && root_controlled) {
|
|
519
|
+
if (HYDRATION && hydrating && root_controlled) {
|
|
422
520
|
// The original `node`: for a sentinel, `hydrate_append` performs the
|
|
423
521
|
// cursor advance that stands in for the eliminated sibling navigation.
|
|
424
522
|
append(/** @type {ChildNode} */ (node), /** @type {Node} */ (boundary));
|
|
@@ -459,46 +557,40 @@ function update_index(block, index) {
|
|
|
459
557
|
* @param {Block} block
|
|
460
558
|
* @param {any} value
|
|
461
559
|
* @param {((item: any) => any) | undefined} map_item
|
|
560
|
+
* @param {boolean} is_local
|
|
462
561
|
* @returns {void}
|
|
463
562
|
*/
|
|
464
|
-
function update_value(block, value, map_item) {
|
|
563
|
+
function update_value(block, value, map_item, is_local) {
|
|
465
564
|
var state = block.s;
|
|
466
565
|
if (state.r !== value) {
|
|
467
566
|
state.r = value;
|
|
468
|
-
|
|
567
|
+
var next = map_item === undefined ? value : map_item(value);
|
|
568
|
+
if (is_local) {
|
|
569
|
+
// Held as it is, read by the item block's update function alone
|
|
570
|
+
// (`LOCAL_ITEMS`) off its state's `$item` slot: the block re-runs here.
|
|
571
|
+
state.v = next;
|
|
572
|
+
state.p.$item = next;
|
|
573
|
+
run_block(block);
|
|
574
|
+
} else {
|
|
575
|
+
set(state.v, next);
|
|
576
|
+
}
|
|
469
577
|
}
|
|
470
578
|
}
|
|
471
579
|
|
|
472
580
|
/**
|
|
473
581
|
* @template V
|
|
474
|
-
* @template K
|
|
475
582
|
* @param {ListAnchor} anchor
|
|
476
583
|
* @param {Block} block
|
|
477
584
|
* @param {V[]} b
|
|
478
|
-
* @param {(anchor: Node, value: V | Tracked, index?: any) => Block} render_fn
|
|
479
|
-
* @param {boolean} is_controlled
|
|
480
|
-
* @param {boolean} is_indexed
|
|
481
|
-
* @param {((item: V) => K) | undefined} get_key identity keys when omitted
|
|
482
|
-
* @param {((anchor: Node) => void) | undefined} render_empty
|
|
483
|
-
* @param {((item: V) => any) | undefined} map_item
|
|
484
585
|
* @returns {void}
|
|
485
586
|
*
|
|
486
587
|
* The first run only creates items, so it lives in this small function and
|
|
487
588
|
* the diff below is compiled only once a list actually changes.
|
|
488
589
|
*/
|
|
489
|
-
function reconcile_by_key(
|
|
490
|
-
anchor,
|
|
491
|
-
block,
|
|
492
|
-
b,
|
|
493
|
-
render_fn,
|
|
494
|
-
is_controlled,
|
|
495
|
-
is_indexed,
|
|
496
|
-
get_key,
|
|
497
|
-
render_empty,
|
|
498
|
-
map_item,
|
|
499
|
-
) {
|
|
590
|
+
function reconcile_by_key(anchor, block, b) {
|
|
500
591
|
var b_length = b.length;
|
|
501
592
|
var state = /** @type {ListState} */ (block.s);
|
|
593
|
+
var get_key = state.k;
|
|
502
594
|
|
|
503
595
|
if (state.keys === null && b_length > 0) {
|
|
504
596
|
var b_blocks = Array(b_length);
|
|
@@ -512,7 +604,7 @@ function reconcile_by_key(
|
|
|
512
604
|
if (get_key !== undefined) {
|
|
513
605
|
b_keys[j] = get_key(value);
|
|
514
606
|
}
|
|
515
|
-
b_blocks[j] = create_item(anchor, value, j,
|
|
607
|
+
b_blocks[j] = create_item(anchor, value, j, b_keys[j], state);
|
|
516
608
|
}
|
|
517
609
|
|
|
518
610
|
state.array = b;
|
|
@@ -521,46 +613,24 @@ function reconcile_by_key(
|
|
|
521
613
|
return;
|
|
522
614
|
}
|
|
523
615
|
|
|
524
|
-
reconcile_by_key_diff(
|
|
525
|
-
anchor,
|
|
526
|
-
block,
|
|
527
|
-
b,
|
|
528
|
-
render_fn,
|
|
529
|
-
is_controlled,
|
|
530
|
-
is_indexed,
|
|
531
|
-
get_key,
|
|
532
|
-
render_empty,
|
|
533
|
-
map_item,
|
|
534
|
-
);
|
|
616
|
+
reconcile_by_key_diff(anchor, block, b);
|
|
535
617
|
}
|
|
536
618
|
|
|
537
619
|
/**
|
|
538
620
|
* Keyed diff for every run after the first (or a first run with an empty
|
|
539
621
|
* list). See {@link reconcile_by_key}.
|
|
540
|
-
* @template V
|
|
622
|
+
* @template V
|
|
541
623
|
* @param {ListAnchor} anchor
|
|
542
624
|
* @param {Block} block
|
|
543
625
|
* @param {V[]} b
|
|
544
|
-
* @param {(anchor: Node, value: V | Tracked, index?: any) => Block} render_fn
|
|
545
|
-
* @param {boolean} is_controlled
|
|
546
|
-
* @param {boolean} is_indexed
|
|
547
|
-
* @param {((item: V) => K) | undefined} get_key
|
|
548
|
-
* @param {((anchor: Node) => void) | undefined} render_empty
|
|
549
|
-
* @param {((item: V) => any) | undefined} map_item
|
|
550
626
|
* @returns {void}
|
|
551
627
|
*/
|
|
552
|
-
function reconcile_by_key_diff(
|
|
553
|
-
anchor,
|
|
554
|
-
block,
|
|
555
|
-
b,
|
|
556
|
-
render_fn,
|
|
557
|
-
is_controlled,
|
|
558
|
-
is_indexed,
|
|
559
|
-
get_key,
|
|
560
|
-
render_empty,
|
|
561
|
-
map_item,
|
|
562
|
-
) {
|
|
628
|
+
function reconcile_by_key_diff(anchor, block, b) {
|
|
563
629
|
var state = /** @type {ListState} */ (block.s);
|
|
630
|
+
var is_controlled = state.c;
|
|
631
|
+
var is_indexed = state.x;
|
|
632
|
+
var get_key = state.k;
|
|
633
|
+
var render_empty = state.e;
|
|
564
634
|
|
|
565
635
|
// Variables used in conditional branches - declare with initial values
|
|
566
636
|
/** @type {number} */
|
|
@@ -627,7 +697,7 @@ function reconcile_by_key_diff(
|
|
|
627
697
|
// Fast-path for create
|
|
628
698
|
if (a_length === 0) {
|
|
629
699
|
for (; j < b_length; j++) {
|
|
630
|
-
b_blocks[j] = create_item(anchor, b[j], j,
|
|
700
|
+
b_blocks[j] = create_item(anchor, b[j], j, b_keys[j], state);
|
|
631
701
|
}
|
|
632
702
|
state.array = b;
|
|
633
703
|
state.blocks = b_blocks;
|
|
@@ -669,27 +739,15 @@ function reconcile_by_key_diff(
|
|
|
669
739
|
// moves; only what is left afterwards needs the map and LIS below.
|
|
670
740
|
while (a_start <= a_end && b_start <= b_end) {
|
|
671
741
|
if (a_keys[a_start] === b_keys[b_start]) {
|
|
672
|
-
b_val = b[b_start];
|
|
673
742
|
b_block = b_blocks[b_start] = a_blocks[a_start];
|
|
674
|
-
|
|
675
|
-
update_index(b_block, b_start);
|
|
676
|
-
}
|
|
677
|
-
if (get_key !== undefined) {
|
|
678
|
-
update_value(b_block, b_val, map_item);
|
|
679
|
-
}
|
|
743
|
+
patch_item(state, b_block, b[b_start], b_start);
|
|
680
744
|
a_start++;
|
|
681
745
|
b_start++;
|
|
682
746
|
continue;
|
|
683
747
|
}
|
|
684
748
|
if (a_keys[a_end] === b_keys[b_end]) {
|
|
685
|
-
b_val = b[b_end];
|
|
686
749
|
b_block = b_blocks[b_end] = a_blocks[a_end];
|
|
687
|
-
|
|
688
|
-
update_index(b_block, b_end);
|
|
689
|
-
}
|
|
690
|
-
if (get_key !== undefined) {
|
|
691
|
-
update_value(b_block, b_val, map_item);
|
|
692
|
-
}
|
|
750
|
+
patch_item(state, b_block, b[b_end], b_end);
|
|
693
751
|
a_end--;
|
|
694
752
|
b_end--;
|
|
695
753
|
continue;
|
|
@@ -704,14 +762,8 @@ function reconcile_by_key_diff(
|
|
|
704
762
|
}
|
|
705
763
|
if (a_keys[a_end] === b_keys[b_start]) {
|
|
706
764
|
// Last old item is the next new one: move it in front of the old run.
|
|
707
|
-
b_val = b[b_start];
|
|
708
765
|
b_block = b_blocks[b_start] = a_blocks[a_end];
|
|
709
|
-
|
|
710
|
-
update_index(b_block, b_start);
|
|
711
|
-
}
|
|
712
|
-
if (get_key !== undefined) {
|
|
713
|
-
update_value(b_block, b_val, map_item);
|
|
714
|
-
}
|
|
766
|
+
patch_item(state, b_block, b[b_start], b_start);
|
|
715
767
|
move(b_block, /** @type {ChildNode} */ (a_blocks[a_start].s.start));
|
|
716
768
|
a_end--;
|
|
717
769
|
b_start++;
|
|
@@ -719,14 +771,8 @@ function reconcile_by_key_diff(
|
|
|
719
771
|
}
|
|
720
772
|
if (a_keys[a_start] === b_keys[b_end]) {
|
|
721
773
|
// First old item is the last new one: move it behind the old run.
|
|
722
|
-
b_val = b[b_end];
|
|
723
774
|
b_block = b_blocks[b_end] = a_blocks[a_start];
|
|
724
|
-
|
|
725
|
-
update_index(b_block, b_end);
|
|
726
|
-
}
|
|
727
|
-
if (get_key !== undefined) {
|
|
728
|
-
update_value(b_block, b_val, map_item);
|
|
729
|
-
}
|
|
775
|
+
patch_item(state, b_block, b[b_end], b_end);
|
|
730
776
|
move(b_block, block_start(b_blocks, b_end + 1, b_length, anchor));
|
|
731
777
|
a_start++;
|
|
732
778
|
b_end--;
|
|
@@ -741,15 +787,7 @@ function reconcile_by_key_diff(
|
|
|
741
787
|
if (b_start <= b_end) {
|
|
742
788
|
var target_node = block_start(b_blocks, b_end + 1, b_length, anchor);
|
|
743
789
|
while (b_start <= b_end) {
|
|
744
|
-
b_blocks[b_start] = create_item(
|
|
745
|
-
target_node,
|
|
746
|
-
b[b_start],
|
|
747
|
-
b_start,
|
|
748
|
-
render_fn,
|
|
749
|
-
is_indexed,
|
|
750
|
-
true,
|
|
751
|
-
map_item,
|
|
752
|
-
);
|
|
790
|
+
b_blocks[b_start] = create_item(target_node, b[b_start], b_start, b_keys[b_start], state);
|
|
753
791
|
b_start++;
|
|
754
792
|
}
|
|
755
793
|
}
|
|
@@ -790,14 +828,8 @@ function reconcile_by_key_diff(
|
|
|
790
828
|
} else {
|
|
791
829
|
pos = j;
|
|
792
830
|
}
|
|
793
|
-
b_val = b[j];
|
|
794
831
|
b_block = b_blocks[j] = a_blocks[i];
|
|
795
|
-
|
|
796
|
-
update_index(b_block, j);
|
|
797
|
-
}
|
|
798
|
-
if (get_key !== undefined) {
|
|
799
|
-
update_value(b_block, b_val, map_item);
|
|
800
|
-
}
|
|
832
|
+
patch_item(state, b_block, b[j], j);
|
|
801
833
|
++patched;
|
|
802
834
|
break;
|
|
803
835
|
}
|
|
@@ -833,14 +865,8 @@ function reconcile_by_key_diff(
|
|
|
833
865
|
} else {
|
|
834
866
|
pos = j;
|
|
835
867
|
}
|
|
836
|
-
b_val = b[j];
|
|
837
868
|
b_block = b_blocks[j] = a_blocks[i];
|
|
838
|
-
|
|
839
|
-
update_index(b_block, j);
|
|
840
|
-
}
|
|
841
|
-
if (get_key !== undefined) {
|
|
842
|
-
update_value(b_block, b_val, map_item);
|
|
843
|
-
}
|
|
869
|
+
patch_item(state, b_block, b[j], j);
|
|
844
870
|
++patched;
|
|
845
871
|
} else if (!fast_path_removal) {
|
|
846
872
|
destroy_block(a_blocks[i]);
|
|
@@ -854,17 +880,7 @@ function reconcile_by_key_diff(
|
|
|
854
880
|
|
|
855
881
|
if (fast_path_removal) {
|
|
856
882
|
reconcile_fast_clear(anchor, block, []);
|
|
857
|
-
reconcile_by_key(
|
|
858
|
-
anchor,
|
|
859
|
-
block,
|
|
860
|
-
b,
|
|
861
|
-
render_fn,
|
|
862
|
-
is_controlled,
|
|
863
|
-
is_indexed,
|
|
864
|
-
get_key,
|
|
865
|
-
render_empty,
|
|
866
|
-
map_item,
|
|
867
|
-
);
|
|
883
|
+
reconcile_by_key(anchor, block, b);
|
|
868
884
|
return;
|
|
869
885
|
} else if (moved) {
|
|
870
886
|
var next_pos = 0;
|
|
@@ -872,26 +888,9 @@ function reconcile_by_key_diff(
|
|
|
872
888
|
j = seq.length - 1;
|
|
873
889
|
|
|
874
890
|
// When most surviving items have to move anyway, re-lay the whole range
|
|
875
|
-
// in order
|
|
876
|
-
// noticeably cheaper per item than moving into arbitrary positions.
|
|
891
|
+
// in order (see `relay`).
|
|
877
892
|
if ((patched - seq.length) * 3 > b_left * 2) {
|
|
878
|
-
|
|
879
|
-
for (i = 0; i < b_left; i++) {
|
|
880
|
-
pos = i + b_start;
|
|
881
|
-
if (sources[i] === 0) {
|
|
882
|
-
b_blocks[pos] = create_item(
|
|
883
|
-
relay_target,
|
|
884
|
-
b[pos],
|
|
885
|
-
pos,
|
|
886
|
-
render_fn,
|
|
887
|
-
is_indexed,
|
|
888
|
-
true,
|
|
889
|
-
map_item,
|
|
890
|
-
);
|
|
891
|
-
} else {
|
|
892
|
-
move(b_blocks[pos], relay_target);
|
|
893
|
-
}
|
|
894
|
-
}
|
|
893
|
+
relay(anchor, a_blocks, a_start, a_end, b, b_blocks, b_start, b_end, sources, b_keys, state);
|
|
895
894
|
state.array = b;
|
|
896
895
|
state.blocks = b_blocks;
|
|
897
896
|
state.keys = b_keys;
|
|
@@ -905,7 +904,7 @@ function reconcile_by_key_diff(
|
|
|
905
904
|
next_pos = pos + 1;
|
|
906
905
|
|
|
907
906
|
var target = block_start(b_blocks, next_pos, b_length, anchor);
|
|
908
|
-
b_blocks[pos] = create_item(target, b_val, pos,
|
|
907
|
+
b_blocks[pos] = create_item(target, b_val, pos, b_keys[pos], state);
|
|
909
908
|
} else if (j < 0 || i !== seq[j]) {
|
|
910
909
|
pos = i + b_start;
|
|
911
910
|
next_pos = pos + 1;
|
|
@@ -924,7 +923,7 @@ function reconcile_by_key_diff(
|
|
|
924
923
|
next_pos = pos + 1;
|
|
925
924
|
|
|
926
925
|
var target = block_start(b_blocks, next_pos, b_length, anchor);
|
|
927
|
-
b_blocks[pos] = create_item(target, b_val, pos,
|
|
926
|
+
b_blocks[pos] = create_item(target, b_val, pos, b_keys[pos], state);
|
|
928
927
|
}
|
|
929
928
|
}
|
|
930
929
|
}
|
|
@@ -934,19 +933,94 @@ function reconcile_by_key_diff(
|
|
|
934
933
|
state.keys = b_keys;
|
|
935
934
|
}
|
|
936
935
|
|
|
936
|
+
/**
|
|
937
|
+
* Re-lays the unmatched range `b_start..b_end` in its new order, walking it
|
|
938
|
+
* front to back: `cursor` is the first node of the range that is not in place
|
|
939
|
+
* yet, so a block already there costs a compare and a sibling read, and every
|
|
940
|
+
* other block (or a new item) is inserted before `cursor`. Inserting each item
|
|
941
|
+
* toward the front is what the reverse fast path does too; appending every
|
|
942
|
+
* item at the end of the range instead cost about twice as much in Blink's
|
|
943
|
+
* reorder floor (500 rows: 0.55 ms against 0.29), and several times more
|
|
944
|
+
* right after a layout flush, so this holds even when most items move.
|
|
945
|
+
* @template V
|
|
946
|
+
* @param {ListAnchor} anchor
|
|
947
|
+
* @param {Block[]} a_blocks the old blocks; destroyed ones are skipped
|
|
948
|
+
* @param {number} a_start
|
|
949
|
+
* @param {number} a_end
|
|
950
|
+
* @param {V[]} b
|
|
951
|
+
* @param {Block[]} b_blocks matched blocks at their new index, holes for new items
|
|
952
|
+
* @param {number} b_start
|
|
953
|
+
* @param {number} b_end
|
|
954
|
+
* @param {Int32Array} sources zero at the index of a new item
|
|
955
|
+
* @param {any[] | undefined} b_keys the new items' keys (a keyed list)
|
|
956
|
+
* @param {ListState} state
|
|
957
|
+
*/
|
|
958
|
+
function relay(
|
|
959
|
+
anchor,
|
|
960
|
+
a_blocks,
|
|
961
|
+
a_start,
|
|
962
|
+
a_end,
|
|
963
|
+
b,
|
|
964
|
+
b_blocks,
|
|
965
|
+
b_start,
|
|
966
|
+
b_end,
|
|
967
|
+
sources,
|
|
968
|
+
b_keys,
|
|
969
|
+
state,
|
|
970
|
+
) {
|
|
971
|
+
var b_length = b.length;
|
|
972
|
+
var end_target = block_start(b_blocks, b_end + 1, b_length, anchor);
|
|
973
|
+
/** @type {ChildNode | AppendIntoAnchor | null} */
|
|
974
|
+
var cursor = null;
|
|
975
|
+
|
|
976
|
+
// The range starts at the first surviving old block.
|
|
977
|
+
for (var i = a_start; i <= a_end && cursor === null; i++) {
|
|
978
|
+
var old_block = a_blocks[i];
|
|
979
|
+
if ((old_block.f & DESTROYED) === 0) {
|
|
980
|
+
cursor = /** @type {ChildNode | null} */ (get_first_node(old_block));
|
|
981
|
+
}
|
|
982
|
+
}
|
|
983
|
+
if (cursor === null) {
|
|
984
|
+
cursor = end_target;
|
|
985
|
+
}
|
|
986
|
+
|
|
987
|
+
for (var pos = b_start; pos <= b_end; pos++) {
|
|
988
|
+
if (sources[pos - b_start] === 0) {
|
|
989
|
+
b_blocks[pos] = create_item(
|
|
990
|
+
cursor,
|
|
991
|
+
b[pos],
|
|
992
|
+
pos,
|
|
993
|
+
b_keys === undefined ? undefined : b_keys[pos],
|
|
994
|
+
state,
|
|
995
|
+
);
|
|
996
|
+
continue;
|
|
997
|
+
}
|
|
998
|
+
var block = b_blocks[pos];
|
|
999
|
+
var first = get_first_node(block);
|
|
1000
|
+
if (first === null) {
|
|
1001
|
+
// Renders nothing: it has no place to take.
|
|
1002
|
+
continue;
|
|
1003
|
+
}
|
|
1004
|
+
if (first !== cursor) {
|
|
1005
|
+
move(block, cursor);
|
|
1006
|
+
continue;
|
|
1007
|
+
}
|
|
1008
|
+
var after = next_sibling(/** @type {Node} */ (get_last_node(block)));
|
|
1009
|
+
cursor = after === null ? end_target : /** @type {ChildNode} */ (after);
|
|
1010
|
+
}
|
|
1011
|
+
}
|
|
1012
|
+
|
|
937
1013
|
/**
|
|
938
1014
|
* @template V
|
|
939
1015
|
* @param {ListAnchor} anchor
|
|
940
1016
|
* @param {Block} block
|
|
941
1017
|
* @param {V[]} b
|
|
942
|
-
* @param {(anchor: Node, value: V | Tracked, index?: any) => Block} render_fn
|
|
943
|
-
* @param {boolean} is_controlled
|
|
944
|
-
* @param {boolean} is_indexed
|
|
945
|
-
* @param {(anchor: Node) => void} [render_empty]
|
|
946
1018
|
* @returns {void}
|
|
947
1019
|
*/
|
|
948
|
-
function reconcile_by_ref(anchor, block, b
|
|
1020
|
+
function reconcile_by_ref(anchor, block, b) {
|
|
949
1021
|
var state = /** @type {ListState} */ (block.s);
|
|
1022
|
+
var is_controlled = state.c;
|
|
1023
|
+
var render_empty = state.e;
|
|
950
1024
|
var a_length = state.array.length;
|
|
951
1025
|
var b_length = b.length;
|
|
952
1026
|
var j = 0;
|
|
@@ -986,14 +1060,14 @@ function reconcile_by_ref(anchor, block, b, render_fn, is_controlled, is_indexed
|
|
|
986
1060
|
// Fast-path for create
|
|
987
1061
|
if (a_length === 0) {
|
|
988
1062
|
for (; j < b_length; j++) {
|
|
989
|
-
b_blocks[j] = create_item(anchor, b[j], j,
|
|
1063
|
+
b_blocks[j] = create_item(anchor, b[j], j, undefined, state);
|
|
990
1064
|
}
|
|
991
1065
|
state.array = b;
|
|
992
1066
|
state.blocks = b_blocks;
|
|
993
1067
|
return;
|
|
994
1068
|
}
|
|
995
1069
|
|
|
996
|
-
reconcile_by_ref_diff(anchor, block, b, b_blocks
|
|
1070
|
+
reconcile_by_ref_diff(anchor, block, b, b_blocks);
|
|
997
1071
|
}
|
|
998
1072
|
|
|
999
1073
|
/**
|
|
@@ -1004,13 +1078,11 @@ function reconcile_by_ref(anchor, block, b, render_fn, is_controlled, is_indexed
|
|
|
1004
1078
|
* @param {Block} block
|
|
1005
1079
|
* @param {V[]} b
|
|
1006
1080
|
* @param {Block[]} b_blocks
|
|
1007
|
-
* @param {(anchor: Node, value: V | Tracked, index?: any) => Block} render_fn
|
|
1008
|
-
* @param {boolean} is_controlled
|
|
1009
|
-
* @param {boolean} is_indexed
|
|
1010
1081
|
* @returns {void}
|
|
1011
1082
|
*/
|
|
1012
|
-
function reconcile_by_ref_diff(anchor, block, b, b_blocks
|
|
1083
|
+
function reconcile_by_ref_diff(anchor, block, b, b_blocks) {
|
|
1013
1084
|
var state = /** @type {ListState} */ (block.s);
|
|
1085
|
+
var is_controlled = state.c;
|
|
1014
1086
|
|
|
1015
1087
|
// Variables used in conditional branches - declare with initial values
|
|
1016
1088
|
/** @type {number} */
|
|
@@ -1046,21 +1118,15 @@ function reconcile_by_ref_diff(anchor, block, b, b_blocks, render_fn, is_control
|
|
|
1046
1118
|
// moves; only what is left afterwards needs the map and LIS below.
|
|
1047
1119
|
while (a_start <= a_end && b_start <= b_end) {
|
|
1048
1120
|
if (a[a_start] === b[b_start]) {
|
|
1049
|
-
b_val = b[b_start];
|
|
1050
1121
|
b_block = b_blocks[b_start] = a_blocks[a_start];
|
|
1051
|
-
|
|
1052
|
-
update_index(b_block, b_start);
|
|
1053
|
-
}
|
|
1122
|
+
patch_item(state, b_block, b[b_start], b_start);
|
|
1054
1123
|
a_start++;
|
|
1055
1124
|
b_start++;
|
|
1056
1125
|
continue;
|
|
1057
1126
|
}
|
|
1058
1127
|
if (a[a_end] === b[b_end]) {
|
|
1059
|
-
b_val = b[b_end];
|
|
1060
1128
|
b_block = b_blocks[b_end] = a_blocks[a_end];
|
|
1061
|
-
|
|
1062
|
-
update_index(b_block, b_end);
|
|
1063
|
-
}
|
|
1129
|
+
patch_item(state, b_block, b[b_end], b_end);
|
|
1064
1130
|
a_end--;
|
|
1065
1131
|
b_end--;
|
|
1066
1132
|
continue;
|
|
@@ -1070,11 +1136,8 @@ function reconcile_by_ref_diff(anchor, block, b, b_blocks, render_fn, is_control
|
|
|
1070
1136
|
}
|
|
1071
1137
|
if (a[a_end] === b[b_start]) {
|
|
1072
1138
|
// Last old item is the next new one: move it in front of the old run.
|
|
1073
|
-
b_val = b[b_start];
|
|
1074
1139
|
b_block = b_blocks[b_start] = a_blocks[a_end];
|
|
1075
|
-
|
|
1076
|
-
update_index(b_block, b_start);
|
|
1077
|
-
}
|
|
1140
|
+
patch_item(state, b_block, b[b_start], b_start);
|
|
1078
1141
|
move(b_block, /** @type {ChildNode} */ (a_blocks[a_start].s.start));
|
|
1079
1142
|
a_end--;
|
|
1080
1143
|
b_start++;
|
|
@@ -1082,11 +1145,8 @@ function reconcile_by_ref_diff(anchor, block, b, b_blocks, render_fn, is_control
|
|
|
1082
1145
|
}
|
|
1083
1146
|
if (a[a_start] === b[b_end]) {
|
|
1084
1147
|
// First old item is the last new one: move it behind the old run.
|
|
1085
|
-
b_val = b[b_end];
|
|
1086
1148
|
b_block = b_blocks[b_end] = a_blocks[a_start];
|
|
1087
|
-
|
|
1088
|
-
update_index(b_block, b_end);
|
|
1089
|
-
}
|
|
1149
|
+
patch_item(state, b_block, b[b_end], b_end);
|
|
1090
1150
|
move(b_block, block_start(b_blocks, b_end + 1, b_length, anchor));
|
|
1091
1151
|
a_start++;
|
|
1092
1152
|
b_end--;
|
|
@@ -1101,14 +1161,7 @@ function reconcile_by_ref_diff(anchor, block, b, b_blocks, render_fn, is_control
|
|
|
1101
1161
|
if (b_start <= b_end) {
|
|
1102
1162
|
var target_node = block_start(b_blocks, b_end + 1, b_length, anchor);
|
|
1103
1163
|
while (b_start <= b_end) {
|
|
1104
|
-
b_blocks[b_start] = create_item(
|
|
1105
|
-
target_node,
|
|
1106
|
-
b[b_start],
|
|
1107
|
-
b_start,
|
|
1108
|
-
render_fn,
|
|
1109
|
-
is_indexed,
|
|
1110
|
-
false,
|
|
1111
|
-
);
|
|
1164
|
+
b_blocks[b_start] = create_item(target_node, b[b_start], b_start, undefined, state);
|
|
1112
1165
|
b_start++;
|
|
1113
1166
|
}
|
|
1114
1167
|
}
|
|
@@ -1145,11 +1198,8 @@ function reconcile_by_ref_diff(anchor, block, b, b_blocks, render_fn, is_control
|
|
|
1145
1198
|
} else {
|
|
1146
1199
|
pos = j;
|
|
1147
1200
|
}
|
|
1148
|
-
b_val = b[j];
|
|
1149
1201
|
b_block = b_blocks[j] = a_blocks[i];
|
|
1150
|
-
|
|
1151
|
-
update_index(b_block, j);
|
|
1152
|
-
}
|
|
1202
|
+
patch_item(state, b_block, b[j], j);
|
|
1153
1203
|
++patched;
|
|
1154
1204
|
break;
|
|
1155
1205
|
}
|
|
@@ -1185,11 +1235,8 @@ function reconcile_by_ref_diff(anchor, block, b, b_blocks, render_fn, is_control
|
|
|
1185
1235
|
} else {
|
|
1186
1236
|
pos = j;
|
|
1187
1237
|
}
|
|
1188
|
-
b_val = b[j];
|
|
1189
1238
|
b_block = b_blocks[j] = a_blocks[i];
|
|
1190
|
-
|
|
1191
|
-
update_index(b_block, j);
|
|
1192
|
-
}
|
|
1239
|
+
patch_item(state, b_block, b[j], j);
|
|
1193
1240
|
++patched;
|
|
1194
1241
|
} else if (!fast_path_removal) {
|
|
1195
1242
|
destroy_block(a_blocks[i]);
|
|
@@ -1203,7 +1250,7 @@ function reconcile_by_ref_diff(anchor, block, b, b_blocks, render_fn, is_control
|
|
|
1203
1250
|
|
|
1204
1251
|
if (fast_path_removal) {
|
|
1205
1252
|
reconcile_fast_clear(anchor, block, []);
|
|
1206
|
-
reconcile_by_ref(anchor, block, b
|
|
1253
|
+
reconcile_by_ref(anchor, block, b);
|
|
1207
1254
|
return;
|
|
1208
1255
|
} else if (moved) {
|
|
1209
1256
|
var next_pos = 0;
|
|
@@ -1211,18 +1258,21 @@ function reconcile_by_ref_diff(anchor, block, b, b_blocks, render_fn, is_control
|
|
|
1211
1258
|
j = seq.length - 1;
|
|
1212
1259
|
|
|
1213
1260
|
// When most surviving items have to move anyway, re-lay the whole range
|
|
1214
|
-
// in order
|
|
1215
|
-
// noticeably cheaper per item than moving into arbitrary positions.
|
|
1261
|
+
// in order (see `relay`).
|
|
1216
1262
|
if ((patched - seq.length) * 3 > b_left * 2) {
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1263
|
+
relay(
|
|
1264
|
+
anchor,
|
|
1265
|
+
a_blocks,
|
|
1266
|
+
a_start,
|
|
1267
|
+
a_end,
|
|
1268
|
+
b,
|
|
1269
|
+
b_blocks,
|
|
1270
|
+
b_start,
|
|
1271
|
+
b_end,
|
|
1272
|
+
sources,
|
|
1273
|
+
undefined,
|
|
1274
|
+
state,
|
|
1275
|
+
);
|
|
1226
1276
|
state.array = b;
|
|
1227
1277
|
state.blocks = b_blocks;
|
|
1228
1278
|
return;
|
|
@@ -1235,7 +1285,7 @@ function reconcile_by_ref_diff(anchor, block, b, b_blocks, render_fn, is_control
|
|
|
1235
1285
|
next_pos = pos + 1;
|
|
1236
1286
|
|
|
1237
1287
|
var target = block_start(b_blocks, next_pos, b_length, anchor);
|
|
1238
|
-
b_blocks[pos] = create_item(target, b_val, pos,
|
|
1288
|
+
b_blocks[pos] = create_item(target, b_val, pos, undefined, state);
|
|
1239
1289
|
} else if (j < 0 || i !== seq[j]) {
|
|
1240
1290
|
pos = i + b_start;
|
|
1241
1291
|
next_pos = pos + 1;
|
|
@@ -1254,7 +1304,7 @@ function reconcile_by_ref_diff(anchor, block, b, b_blocks, render_fn, is_control
|
|
|
1254
1304
|
next_pos = pos + 1;
|
|
1255
1305
|
|
|
1256
1306
|
var target = block_start(b_blocks, next_pos, b_length, anchor);
|
|
1257
|
-
b_blocks[pos] = create_item(target, b_val, pos,
|
|
1307
|
+
b_blocks[pos] = create_item(target, b_val, pos, undefined, state);
|
|
1258
1308
|
}
|
|
1259
1309
|
}
|
|
1260
1310
|
}
|