@ixfx/components 0.17.0 → 0.17.1

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.
@@ -412,6 +412,8 @@ Parts: `part="icon"` and `part="badge"` on the wrappers this adds.
412
412
  | `setItems(items)` | Replace the collection. Selection / cursor keep any key still present; vanished keys are deselected silently and held pending (see [View state](#view-state)). |
413
413
  | `appendItems(items)` | Add to the end. Scroll, selection and cursor untouched; no reflow animation. |
414
414
  | `updateItem(key, item)` | Replace one item. A visible cell re-renders in place; an offscreen one updates when scrolled in. |
415
+ | `updateItems(entries)` | Replace many items at once — the batched form of `updateItem`. See [Batched updates](#batched-updates). |
416
+ | `refreshItem(key)` | Re-render one cell **without** changing `items`. See [Refreshing a cell](#refreshing-a-cell). |
415
417
  | `removeItems(keys)` | Remove items. Removed keys drop out of selection / cursor silently and are held pending. |
416
418
  | `clearItems()` | Drop everything. Resets selection / cursor and emits one empty `list-select`; the keys are held pending, so re-adding them restores the selection. |
417
419
 
@@ -424,6 +426,49 @@ key-based. Data-mode events carry the data value as `detail.item` and
424
426
  Duplicate keys are warned about once; the first occurrence wins for key-based
425
427
  operations.
426
428
 
429
+ #### Batched updates
430
+
431
+ `updateItems(entries)` applies many replacements with **one** array copy and
432
+ **one** source rebuild:
433
+
434
+ ```typescript
435
+ grid.updateItems([
436
+ { key: 'a', item: { ...fileA, name: 'Renamed' } },
437
+ { key: 'b', item: { ...fileB, thumb: newThumb } },
438
+ ]);
439
+
440
+ // A Map works too:
441
+ grid.updateItems(new Map([['a', newA], ['b', newB]]));
442
+ ```
443
+
444
+ It has the same semantics as the equivalent run of `updateItem` calls: unknown
445
+ keys are ignored, selection / cursor / scroll are untouched, and an entry whose
446
+ `item` is the identical object already at that key is skipped. If nothing
447
+ changes, `items` keeps its identity and nothing is emitted.
448
+
449
+ **Use `updateItems` rather than a loop of `updateItem`s** when you have more
450
+ than one row to change. A loop pays one array copy per call, so changing n rows
451
+ costs n copies; the batched form pays one. On a 20k-item collection, replacing
452
+ 500 rows measured ~22 ms as a loop versus ~0.7 ms as one batch (happy-dom).
453
+
454
+ #### Refreshing a cell
455
+
456
+ `updateItem(key, item)` with the **same object** that is already at `key` is a
457
+ no-op — the array is not copied and nothing is emitted. That is the right
458
+ behaviour when you only want to push the *data* through.
459
+
460
+ If instead you want to redraw a cell whose data is unchanged — because the
461
+ adapter renders something that lives outside the item, such as a thumbnail that
462
+ just finished loading — use `refreshItem(key)`:
463
+
464
+ ```typescript
465
+ grid.refreshItem('a'); // re-renders the cell for 'a'; `items` is untouched
466
+ ```
467
+
468
+ The adapter's `render` is called again for that cell, so it picks up the new
469
+ state. The `items` array keeps its identity, so nothing is copied and the
470
+ collection is not rebuilt.
471
+
427
472
  ---
428
473
 
429
474
  ## Grouping
@@ -608,6 +653,26 @@ Inserting is O(items): one `appendItems` costs ~5 ms at 5k items and ~21 ms at
608
653
  50k (the source and search index are rebuilt, and `items` is copied); the
609
654
  element-mode equivalents are ~8 ms at 2k and ~16 ms at 10k.
610
655
 
656
+ ### Mutation API
657
+
658
+ `src/grid-list/grid-list.perf.test.ts` guards the data-mode mutation API with
659
+ **work counters** — how many times the adapter's `key` is called per mutation.
660
+ That number must not scale with the item count. Run it with:
661
+
662
+ ```sh
663
+ pnpm test:perf # all perf suites, verbose
664
+ PERF_REPORT=1 pnpm vitest run src/grid-list/grid-list.perf.test.ts
665
+ ```
666
+
667
+ The mutation paths keep a key → raw index map, so a lookup is O(1) rather than
668
+ an O(n) scan of the array:
669
+
670
+ - `updateItem(key, item)` — one lookup and one array copy. Passing the
671
+ identical object is a no-op (no copy, no emit).
672
+ - `updateItems(entries)` — many replacements, one copy, one rebuild.
673
+ - `appendItems(items)` — the appended items are added to the map; the existing
674
+ ones are not re-scanned.
675
+
611
676
  Things that keep the per-frame paths flat, and that the benchmark guards:
612
677
 
613
678
  - **Cell recycling.** Cells are keyed by a stable cell id, not the item key, so
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@ixfx/components",
3
- "version": "0.17.0",
4
- "generated": "2026-11-09T22:52:59.151Z",
3
+ "version": "0.17.1",
4
+ "generated": "2026-11-10T11:14:57.413Z",
5
5
  "components": [
6
6
  {
7
7
  "name": "ac-text",
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@ixfx/components",
3
3
  "type": "module",
4
- "version": "0.17.0",
4
+ "version": "0.17.1",
5
5
  "packageManager": "pnpm@10.0.0",
6
6
  "description": "Web components for ixfx — Lit-based UI toolkit",
7
7
  "author": "Clint Heyer",