@mintjamsinc/ichigojs 0.1.68 → 0.1.69
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/README.md +293 -5
- package/dist/ichigo.cjs +67 -24
- package/dist/ichigo.cjs.map +1 -1
- package/dist/ichigo.esm.js +67 -24
- package/dist/ichigo.esm.js.map +1 -1
- package/dist/ichigo.esm.min.js +1 -1
- package/dist/ichigo.min.cjs +1 -1
- package/dist/ichigo.umd.js +67 -24
- package/dist/ichigo.umd.js.map +1 -1
- package/dist/ichigo.umd.min.js +1 -1
- package/package.json +1 -1
package/dist/ichigo.esm.js
CHANGED
|
@@ -10264,9 +10264,18 @@ class VForDirective {
|
|
|
10264
10264
|
#sourceName;
|
|
10265
10265
|
#useOfSyntax = false; // Track if 'of' syntax was used
|
|
10266
10266
|
/**
|
|
10267
|
-
*
|
|
10267
|
+
* Ordered list of currently rendered items.
|
|
10268
|
+
*
|
|
10269
|
+
* This is intentionally an ordered array of { key, vNode } entries rather
|
|
10270
|
+
* than a Map<key, VNode>. The :key attribute is a *reconciliation hint*
|
|
10271
|
+
* used to identify and reuse the same logical row across re-renders — it is
|
|
10272
|
+
* not the identity of the rendered set. Keying the rendered set by a Map
|
|
10273
|
+
* would make it structurally impossible to hold two rows that resolve to
|
|
10274
|
+
* the same key, which would silently drop application data. The most
|
|
10275
|
+
* fundamental invariant of a list directive is "N items in => N rows out",
|
|
10276
|
+
* so the rendered set must be a positional list that can carry duplicates.
|
|
10268
10277
|
*/
|
|
10269
|
-
#renderedItems =
|
|
10278
|
+
#renderedItems = [];
|
|
10270
10279
|
/**
|
|
10271
10280
|
* Previous iterations to detect changes
|
|
10272
10281
|
*/
|
|
@@ -10389,11 +10398,11 @@ class VForDirective {
|
|
|
10389
10398
|
destroy() {
|
|
10390
10399
|
// Clean up all rendered items
|
|
10391
10400
|
// First destroy all VNodes (calls @unmount hooks), then remove from DOM
|
|
10392
|
-
for (const vNode of this.#renderedItems
|
|
10401
|
+
for (const { vNode } of this.#renderedItems) {
|
|
10393
10402
|
vNode.destroy();
|
|
10394
10403
|
}
|
|
10395
10404
|
// Then remove DOM nodes
|
|
10396
|
-
for (const vNode of this.#renderedItems
|
|
10405
|
+
for (const { vNode } of this.#renderedItems) {
|
|
10397
10406
|
const range = vNode.fragmentRange;
|
|
10398
10407
|
if (range) {
|
|
10399
10408
|
range.remove();
|
|
@@ -10404,7 +10413,7 @@ class VForDirective {
|
|
|
10404
10413
|
vNode.node.parentNode.removeChild(vNode.node);
|
|
10405
10414
|
}
|
|
10406
10415
|
}
|
|
10407
|
-
this.#renderedItems
|
|
10416
|
+
this.#renderedItems = [];
|
|
10408
10417
|
this.#previousIterations = [];
|
|
10409
10418
|
}
|
|
10410
10419
|
/**
|
|
@@ -10445,7 +10454,24 @@ class VForDirective {
|
|
|
10445
10454
|
this.#previousIterations = iterations;
|
|
10446
10455
|
}
|
|
10447
10456
|
/**
|
|
10448
|
-
* Key-based diffing for efficient DOM updates
|
|
10457
|
+
* Key-based diffing for efficient DOM updates.
|
|
10458
|
+
*
|
|
10459
|
+
* Reconciliation model
|
|
10460
|
+
* --------------------
|
|
10461
|
+
* The :key attribute is treated as a *hint* for reusing the same logical
|
|
10462
|
+
* row across re-renders, not as the identity of the rendered set. The
|
|
10463
|
+
* previously rendered rows are placed into a pool keyed by :key (a queue
|
|
10464
|
+
* per key, so equal keys can hold more than one row). Each incoming
|
|
10465
|
+
* iteration then claims a row from its key's queue when one is available,
|
|
10466
|
+
* otherwise a fresh row is created. Whatever stays in the pool at the end
|
|
10467
|
+
* is genuinely gone and is destroyed and removed.
|
|
10468
|
+
*
|
|
10469
|
+
* This guarantees the directive's most fundamental invariant — "N items in
|
|
10470
|
+
* => N rows out" — even when the application supplies duplicate keys. We
|
|
10471
|
+
* still warn on duplicates because they make reuse ambiguous (reordering
|
|
10472
|
+
* becomes positional rather than identity-stable), but we never silently
|
|
10473
|
+
* drop the application's data, and no row is ever orphaned: every old row
|
|
10474
|
+
* is either reused or explicitly removed.
|
|
10449
10475
|
*/
|
|
10450
10476
|
#updateList(newIterations) {
|
|
10451
10477
|
const parent = this.#vNode.anchorNode?.parentNode;
|
|
@@ -10453,22 +10479,38 @@ class VForDirective {
|
|
|
10453
10479
|
if (!parent || !anchor) {
|
|
10454
10480
|
throw new Error('v-for element must have a parent and anchor');
|
|
10455
10481
|
}
|
|
10456
|
-
|
|
10457
|
-
//
|
|
10458
|
-
|
|
10482
|
+
// Build a reuse pool from the currently rendered rows. A queue per key
|
|
10483
|
+
// (FIFO) lets duplicate keys reuse multiple rows: the first incoming
|
|
10484
|
+
// occurrence claims the first existing row, the second claims the next,
|
|
10485
|
+
// and so on.
|
|
10486
|
+
const pool = new Map();
|
|
10487
|
+
for (const { key, vNode } of this.#renderedItems) {
|
|
10488
|
+
let queue = pool.get(key);
|
|
10489
|
+
if (!queue) {
|
|
10490
|
+
queue = [];
|
|
10491
|
+
pool.set(key, queue);
|
|
10492
|
+
}
|
|
10493
|
+
queue.push(vNode);
|
|
10494
|
+
}
|
|
10495
|
+
// Decide, for each incoming iteration in order, whether it reuses an
|
|
10496
|
+
// existing row or needs a new one. Reused rows are taken out of the
|
|
10497
|
+
// pool so that what remains afterwards is exactly the set to remove.
|
|
10459
10498
|
const seenKeys = new Set();
|
|
10460
|
-
|
|
10461
|
-
|
|
10462
|
-
|
|
10499
|
+
const plan = [];
|
|
10500
|
+
for (const context of newIterations) {
|
|
10501
|
+
if (seenKeys.has(context.key)) {
|
|
10502
|
+
console.warn(`[ichigo.js] Duplicate key detected in v-for: "${context.key}". All entries are still rendered, but reordering may be unstable. Keys should be unique.`);
|
|
10463
10503
|
}
|
|
10464
|
-
seenKeys.add(
|
|
10465
|
-
|
|
10504
|
+
seenKeys.add(context.key);
|
|
10505
|
+
const queue = pool.get(context.key);
|
|
10506
|
+
const reused = queue && queue.length ? queue.shift() : undefined;
|
|
10507
|
+
plan.push({ context, reused });
|
|
10466
10508
|
}
|
|
10467
|
-
// Remove
|
|
10509
|
+
// Remove rows that were not reused.
|
|
10468
10510
|
// First destroy VNodes (calls @unmount hooks while DOM is still accessible)
|
|
10469
10511
|
const nodesToRemove = [];
|
|
10470
|
-
for (const
|
|
10471
|
-
|
|
10512
|
+
for (const queue of pool.values()) {
|
|
10513
|
+
for (const vNode of queue) {
|
|
10472
10514
|
nodesToRemove.push(vNode);
|
|
10473
10515
|
vNode.destroy();
|
|
10474
10516
|
}
|
|
@@ -10487,11 +10529,12 @@ class VForDirective {
|
|
|
10487
10529
|
parentOfNode.removeChild(vNode.node);
|
|
10488
10530
|
}
|
|
10489
10531
|
}
|
|
10490
|
-
// Add or reorder
|
|
10532
|
+
// Add or reorder rows, building the new ordered rendered set.
|
|
10533
|
+
const newRenderedItems = [];
|
|
10491
10534
|
let prevNode = anchor;
|
|
10492
|
-
for (const context of
|
|
10535
|
+
for (const { context, reused } of plan) {
|
|
10493
10536
|
const { key } = context;
|
|
10494
|
-
let vNode =
|
|
10537
|
+
let vNode = reused;
|
|
10495
10538
|
if (!vNode) {
|
|
10496
10539
|
// Create new item
|
|
10497
10540
|
const clone = this.#cloneNode();
|
|
@@ -10523,7 +10566,7 @@ class VForDirective {
|
|
|
10523
10566
|
if (clone.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
|
|
10524
10567
|
const range = VFragmentRange.insert(parent, prevNode.nextSibling, 'vfor-fragment', clone);
|
|
10525
10568
|
vNode.fragmentRange = range;
|
|
10526
|
-
newRenderedItems.
|
|
10569
|
+
newRenderedItems.push({ key, vNode });
|
|
10527
10570
|
vNode.forceUpdate();
|
|
10528
10571
|
prevNode = range.lastNode;
|
|
10529
10572
|
continue;
|
|
@@ -10537,12 +10580,12 @@ class VForDirective {
|
|
|
10537
10580
|
else {
|
|
10538
10581
|
parent.appendChild(nodeToInsert);
|
|
10539
10582
|
}
|
|
10540
|
-
newRenderedItems.
|
|
10583
|
+
newRenderedItems.push({ key, vNode });
|
|
10541
10584
|
vNode.forceUpdate();
|
|
10542
10585
|
}
|
|
10543
10586
|
else {
|
|
10544
10587
|
// Reuse existing item
|
|
10545
|
-
newRenderedItems.
|
|
10588
|
+
newRenderedItems.push({ key, vNode });
|
|
10546
10589
|
// Update bindings
|
|
10547
10590
|
this.#updateItemBindings(vNode, context);
|
|
10548
10591
|
// For fragment-backed iterations, move the entire range atomically.
|
|
@@ -10568,7 +10611,7 @@ class VForDirective {
|
|
|
10568
10611
|
// Advance prevNode to this iteration's last DOM node
|
|
10569
10612
|
prevNode = vNode.fragmentRange?.lastNode ?? vNode.anchorNode ?? vNode.node;
|
|
10570
10613
|
}
|
|
10571
|
-
// Update rendered
|
|
10614
|
+
// Update the ordered rendered set
|
|
10572
10615
|
this.#renderedItems = newRenderedItems;
|
|
10573
10616
|
}
|
|
10574
10617
|
/**
|