@gtkx/components 2.0.0-beta.12 → 2.0.0-beta.13

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.
Files changed (100) hide show
  1. package/README.md +1 -1
  2. package/dist/column-view.d.ts.map +1 -1
  3. package/dist/column-view.js +7 -3
  4. package/dist/column-view.js.map +1 -1
  5. package/dist/combo-row.d.ts.map +1 -1
  6. package/dist/combo-row.js +3 -1
  7. package/dist/combo-row.js.map +1 -1
  8. package/dist/grid-view.d.ts.map +1 -1
  9. package/dist/grid-view.js +3 -2
  10. package/dist/grid-view.js.map +1 -1
  11. package/dist/index.d.ts +1 -2
  12. package/dist/index.d.ts.map +1 -1
  13. package/dist/index.js +0 -1
  14. package/dist/index.js.map +1 -1
  15. package/dist/internal/cells.d.ts +5 -2
  16. package/dist/internal/cells.d.ts.map +1 -1
  17. package/dist/internal/cells.js +86 -43
  18. package/dist/internal/cells.js.map +1 -1
  19. package/dist/internal/collection-index.d.ts +3 -5
  20. package/dist/internal/collection-index.d.ts.map +1 -1
  21. package/dist/internal/collection-index.js +3 -7
  22. package/dist/internal/collection-index.js.map +1 -1
  23. package/dist/internal/collection-model.d.ts +15 -4
  24. package/dist/internal/collection-model.d.ts.map +1 -1
  25. package/dist/internal/collection-model.js +154 -73
  26. package/dist/internal/collection-model.js.map +1 -1
  27. package/dist/internal/collection.d.ts +3 -3
  28. package/dist/internal/collection.d.ts.map +1 -1
  29. package/dist/internal/collection.js +11 -9
  30. package/dist/internal/collection.js.map +1 -1
  31. package/dist/internal/controlled-sync.d.ts +2 -1
  32. package/dist/internal/controlled-sync.d.ts.map +1 -1
  33. package/dist/internal/controlled-sync.js +6 -1
  34. package/dist/internal/controlled-sync.js.map +1 -1
  35. package/dist/internal/drop-down.d.ts +10 -4
  36. package/dist/internal/drop-down.d.ts.map +1 -1
  37. package/dist/internal/drop-down.js +60 -8
  38. package/dist/internal/drop-down.js.map +1 -1
  39. package/dist/internal/expansion.d.ts.map +1 -1
  40. package/dist/internal/expansion.js +46 -19
  41. package/dist/internal/expansion.js.map +1 -1
  42. package/dist/internal/first-header.d.ts +11 -0
  43. package/dist/internal/first-header.d.ts.map +1 -0
  44. package/dist/internal/first-header.js +21 -0
  45. package/dist/internal/first-header.js.map +1 -0
  46. package/dist/internal/level-splice.d.ts +27 -0
  47. package/dist/internal/level-splice.d.ts.map +1 -0
  48. package/dist/internal/level-splice.js +98 -0
  49. package/dist/internal/level-splice.js.map +1 -0
  50. package/dist/internal/selection.d.ts.map +1 -1
  51. package/dist/internal/selection.js +23 -16
  52. package/dist/internal/selection.js.map +1 -1
  53. package/dist/internal/tree-expansion.d.ts +3 -1
  54. package/dist/internal/tree-expansion.d.ts.map +1 -1
  55. package/dist/internal/tree-expansion.js +31 -2
  56. package/dist/internal/tree-expansion.js.map +1 -1
  57. package/dist/internal/tree-order.d.ts +1 -0
  58. package/dist/internal/tree-order.d.ts.map +1 -1
  59. package/dist/internal/tree-order.js +2 -1
  60. package/dist/internal/tree-order.js.map +1 -1
  61. package/dist/internal/use-collection.d.ts +1 -0
  62. package/dist/internal/use-collection.d.ts.map +1 -1
  63. package/dist/internal/use-collection.js +6 -2
  64. package/dist/internal/use-collection.js.map +1 -1
  65. package/dist/list-item-factory.d.ts +1 -1
  66. package/dist/list-item-factory.d.ts.map +1 -1
  67. package/dist/list-item-factory.js +2 -2
  68. package/dist/list-item-factory.js.map +1 -1
  69. package/dist/list-view.d.ts.map +1 -1
  70. package/dist/list-view.js +8 -3
  71. package/dist/list-view.js.map +1 -1
  72. package/dist/types.d.ts +37 -47
  73. package/dist/types.d.ts.map +1 -1
  74. package/dist/types.js.map +1 -1
  75. package/package.json +8 -8
  76. package/src/column-view.tsx +8 -4
  77. package/src/combo-row.tsx +4 -1
  78. package/src/grid-view.tsx +3 -2
  79. package/src/index.ts +0 -4
  80. package/src/internal/cells.tsx +141 -53
  81. package/src/internal/collection-index.ts +7 -14
  82. package/src/internal/collection-model.ts +211 -76
  83. package/src/internal/collection.ts +14 -13
  84. package/src/internal/controlled-sync.ts +8 -1
  85. package/src/internal/drop-down.tsx +111 -14
  86. package/src/internal/expansion.ts +52 -20
  87. package/src/internal/first-header.ts +23 -0
  88. package/src/internal/level-splice.ts +157 -0
  89. package/src/internal/selection.tsx +24 -19
  90. package/src/internal/tree-expansion.ts +52 -2
  91. package/src/internal/tree-order.ts +3 -1
  92. package/src/internal/use-collection.tsx +8 -2
  93. package/src/list-item-factory.tsx +2 -1
  94. package/src/list-view.tsx +10 -2
  95. package/src/types.ts +39 -51
  96. package/dist/tab-collection.d.ts +0 -9
  97. package/dist/tab-collection.d.ts.map +0 -1
  98. package/dist/tab-collection.js +0 -114
  99. package/dist/tab-collection.js.map +0 -1
  100. package/src/tab-collection.tsx +0 -124
@@ -15,7 +15,7 @@ import type {
15
15
  ListSectionRenderer,
16
16
  } from "../types.js";
17
17
  import type { Collection } from "./collection.js";
18
- import { slotPathAt, type SlotRef, slotRefFor } from "./collection-model.js";
18
+ import { type LazyLevelStore, levelStoreFor, slotRefFor } from "./collection-model.js";
19
19
  import { joinParts } from "./keys.js";
20
20
 
21
21
  type CellSize = {
@@ -87,6 +87,7 @@ type PositionedHost = Gtk.ListItem | Gtk.ColumnViewRow;
87
87
  type ItemSlot = {
88
88
  itemKey: string;
89
89
  item: ListItem;
90
+ position: number | undefined;
90
91
  row: Gtk.TreeListRow | null;
91
92
  args: ListItemRenderArgs<unknown>;
92
93
  };
@@ -100,12 +101,14 @@ type ItemIdentity = {
100
101
  itemKey: string;
101
102
  item: ListItem | undefined;
102
103
  position: number;
104
+ index: number;
103
105
  isExpanded: boolean;
104
106
  };
105
107
 
106
108
  type ItemCellProps = {
107
109
  entry: CellEntry<Gtk.ListItem>;
108
110
  render: ListItemRenderer<never>;
111
+ rowProps: ListRowPropsResolver<never> | null | undefined;
109
112
  state: RefObject<CollectionState>;
110
113
  hasExpander: boolean;
111
114
  expanderDescriptions: ExpanderDescriptions | null | undefined;
@@ -114,12 +117,22 @@ type ItemCellProps = {
114
117
  type ItemPortalsProps = {
115
118
  registry: CellRegistry<Gtk.ListItem>;
116
119
  render: ListItemRenderer<never>;
120
+ rowProps?: ListRowPropsResolver<never> | null | undefined;
117
121
  collection: Collection;
118
122
  hasExpander?: boolean | undefined;
119
123
  expandedIds?: string[] | null | undefined;
120
124
  expanderDescriptions?: ExpanderDescriptions | null | undefined;
121
125
  };
122
126
 
127
+ type PendingList<H extends FactoryHost> = {
128
+ entries: CellEntry<H>[];
129
+ isPending: boolean;
130
+ };
131
+
132
+ type ItemCellListProps = Omit<ItemPortalsProps, "registry"> & PendingList<Gtk.ListItem>;
133
+
134
+ type ItemRowListProps = Omit<RowPortalsProps, "registry"> & PendingList<Gtk.ColumnViewRow>;
135
+
123
136
  type ItemRowProps = {
124
137
  entry: CellEntry<Gtk.ColumnViewRow>;
125
138
  rowProps: ListRowPropsResolver<never>;
@@ -136,7 +149,7 @@ type RowPortalsProps = {
136
149
  type HeaderCellProps = {
137
150
  entry: CellEntry<Gtk.ListHeader>;
138
151
  render: ListSectionRenderer<never>;
139
- collection: Collection;
152
+ version: number;
140
153
  };
141
154
 
142
155
  type HeaderPortalsProps = {
@@ -152,6 +165,7 @@ type NativeItemKeyStore = {
152
165
  type NativeItemCellProps<T extends GObject.Object> = {
153
166
  entry: CellEntry<Gtk.ListItem>;
154
167
  render: ListItemRenderer<T>;
168
+ rowProps: ListRowPropsResolver<T> | null | undefined;
155
169
  keys: NativeItemKeyStore;
156
170
  size: CellSize;
157
171
  };
@@ -159,6 +173,7 @@ type NativeItemCellProps<T extends GObject.Object> = {
159
173
  type NativeItemPortalsProps<T extends GObject.Object> = {
160
174
  registry: CellRegistry<Gtk.ListItem>;
161
175
  render: ListItemRenderer<T>;
176
+ rowProps: ListRowPropsResolver<T> | null | undefined;
162
177
  size: CellSize;
163
178
  };
164
179
 
@@ -175,11 +190,14 @@ type RowSlot = {
175
190
  const ItemCell = memo(ItemCellImpl, isSameItemCell);
176
191
  const ItemRow = memo(ItemRowImpl, isSameItemRow);
177
192
  const HeaderCell = memo(HeaderCellImpl);
193
+ const ItemCellList = memo(ItemCellListImpl, isWaitingForSync);
194
+ const ItemRowList = memo(ItemRowListImpl, isWaitingForSync);
178
195
  const CELL_OPTIONS: CellRegistryOptions<Gtk.ListItem> = { isHost: isListItem, prepare: prepareCell };
179
196
  const HEADER_OPTIONS: CellRegistryOptions<Gtk.ListHeader> = { isHost: isListHeader, prepare: prepareCell };
180
197
  const ROW_OPTIONS: CellRegistryOptions<Gtk.ColumnViewRow> = { isHost: isColumnViewRow };
181
198
  const NO_SIZE: CellSize = { width: -1, height: -1 };
182
199
  const NO_ROW_PROPS: ListRowProps = {};
200
+ const DEFAULT_ROW_PROPS = resolveRowProps(NO_ROW_PROPS);
183
201
  const placeholders: WeakSet<Gtk.Widget> = new WeakSet();
184
202
 
185
203
  const placeholder = (size: CellSize): Gtk.Widget => {
@@ -329,13 +347,27 @@ function isRowWanted(options: ItemSlotOptions, item: ListItem): boolean {
329
347
  return options.expandedIds?.includes(item.id) ?? false;
330
348
  }
331
349
 
350
+ function collectionIndex(collection: Collection, item: ListItem, position: number | undefined): number {
351
+ if (position === undefined) {
352
+ return 0;
353
+ }
354
+
355
+ if (collection.idAt(position) === item.id) {
356
+ return position;
357
+ }
358
+
359
+ const index = collection.positionFor(item.id);
360
+
361
+ return index === -1 ? position : index;
362
+ }
363
+
332
364
  function itemArgs(
333
365
  item: ListItem,
334
- position: number | undefined,
366
+ index: number,
335
367
  row: Gtk.TreeListRow | null,
336
368
  isExpanded: boolean,
337
369
  ): ListItemRenderArgs<unknown> {
338
- const args: ListItemRenderArgs<unknown> = { item: item.value, index: position ?? 0 };
370
+ const args: ListItemRenderArgs<unknown> = { item: item.value, index };
339
371
 
340
372
  if (row === null) {
341
373
  return args;
@@ -350,10 +382,6 @@ function itemArgs(
350
382
  return args;
351
383
  }
352
384
 
353
- function keyForItem(ref: SlotRef, item: ListItem): string {
354
- return joinParts([slotPathAt(ref.store, ref.slot), item.id]);
355
- }
356
-
357
385
  function slotFor(options: ItemSlotOptions): ItemSlot | null {
358
386
  const ref = slotRefFor(options.item);
359
387
 
@@ -368,10 +396,16 @@ function slotFor(options: ItemSlotOptions): ItemSlot | null {
368
396
  }
369
397
 
370
398
  return {
371
- itemKey: keyForItem(ref, item),
399
+ itemKey: ref.key,
372
400
  item,
401
+ position: options.position,
373
402
  row: options.row,
374
- args: itemArgs(item, options.position, options.row, isRowWanted(options, item)),
403
+ args: itemArgs(
404
+ item,
405
+ collectionIndex(options.collection, item, options.position),
406
+ options.row,
407
+ isRowWanted(options, item),
408
+ ),
375
409
  };
376
410
  }
377
411
 
@@ -383,6 +417,7 @@ function useItemSlot(
383
417
  const position = useProperty(entry.host, "position");
384
418
  const item = useProperty(entry.host, "item") ?? null;
385
419
  const row = item instanceof Gtk.TreeListRow ? item : null;
420
+ const { collection, expandedIds } = state.current;
386
421
 
387
422
  if (position === identity.position) {
388
423
  return identity.item === undefined
@@ -390,13 +425,12 @@ function useItemSlot(
390
425
  : {
391
426
  itemKey: identity.itemKey,
392
427
  item: identity.item,
428
+ position,
393
429
  row,
394
- args: itemArgs(identity.item, position, row, identity.isExpanded),
430
+ args: itemArgs(identity.item, identity.index, row, identity.isExpanded),
395
431
  };
396
432
  }
397
433
 
398
- const { collection, expandedIds } = state.current;
399
-
400
434
  return slotFor({ item, position, row, collection, expandedIds });
401
435
  }
402
436
 
@@ -407,11 +441,13 @@ function itemIdentity(
407
441
  ): ItemIdentity {
408
442
  const ref = slotRefFor(entry.host.getItem());
409
443
  const item = ref === null ? undefined : collection.itemAt(ref);
444
+ const position = entry.host.getPosition();
410
445
 
411
446
  return {
412
- itemKey: ref === null || item === undefined ? "" : keyForItem(ref, item),
447
+ itemKey: ref === null || item === undefined ? "" : ref.key,
413
448
  item,
414
- position: entry.host.getPosition(),
449
+ position,
450
+ index: item === undefined ? 0 : collectionIndex(collection, item, position),
415
451
  isExpanded: item !== undefined && (expandedIds?.includes(item.id) ?? false),
416
452
  };
417
453
  }
@@ -421,6 +457,7 @@ function isSameItemIdentity(previous: ItemIdentity, next: ItemIdentity): boolean
421
457
  previous.item === next.item &&
422
458
  previous.itemKey === next.itemKey &&
423
459
  previous.position === next.position &&
460
+ previous.index === next.index &&
424
461
  previous.isExpanded === next.isExpanded
425
462
  );
426
463
  }
@@ -431,12 +468,17 @@ function isSameItemCell(previous: ItemCellProps, next: ItemCellProps): boolean {
431
468
  return (
432
469
  previous.entry === next.entry &&
433
470
  previous.render === next.render &&
471
+ previous.rowProps === next.rowProps &&
434
472
  previous.hasExpander === next.hasExpander &&
435
473
  previous.expanderDescriptions === next.expanderDescriptions &&
436
474
  isSameItemIdentity(identity, next)
437
475
  );
438
476
  }
439
477
 
478
+ function isWaitingForSync<H extends FactoryHost>(_previous: PendingList<H>, next: PendingList<H>): boolean {
479
+ return next.isPending;
480
+ }
481
+
440
482
  function isSameItemRow(previous: ItemRowProps, next: ItemRowProps): boolean {
441
483
  const identity = previous.entry.identity.current ?? previous;
442
484
 
@@ -468,7 +510,8 @@ function renderedIdentity(entry: CellEntry<PositionedHost>, slot: ItemSlot | nul
468
510
  return {
469
511
  itemKey: slot?.itemKey ?? "",
470
512
  item: slot?.item,
471
- position: slot?.args.index ?? entry.host.getPosition(),
513
+ position: slot?.position ?? entry.host.getPosition(),
514
+ index: slot?.args.index ?? 0,
472
515
  isExpanded: slot?.args.isExpanded ?? false,
473
516
  };
474
517
  }
@@ -542,10 +585,7 @@ function rowFlag(value: boolean | undefined): boolean {
542
585
  return value ?? true;
543
586
  }
544
587
 
545
- function rowPropsFor(slot: ItemSlot | null, rowProps: ListRowPropsResolver<never>): ResolvedRowProps {
546
- const resolve = rowProps as ListRowPropsResolver<unknown>;
547
- const props = slot === null ? NO_ROW_PROPS : resolve(slot.args);
548
-
588
+ function resolveRowProps(props: ListRowProps): ResolvedRowProps {
549
589
  return {
550
590
  accessibleLabel: rowText(props.accessibleLabel),
551
591
  accessibleDescription: rowText(props.accessibleDescription),
@@ -555,7 +595,13 @@ function rowPropsFor(slot: ItemSlot | null, rowProps: ListRowPropsResolver<never
555
595
  };
556
596
  }
557
597
 
558
- function applyRowProps(host: Gtk.ColumnViewRow, props: ResolvedRowProps, previous: ResolvedRowProps | null): void {
598
+ function rowPropsFor(slot: ItemSlot | null, rowProps: ListRowPropsResolver<never>): ResolvedRowProps {
599
+ const resolve = rowProps as ListRowPropsResolver<unknown>;
600
+
601
+ return resolveRowProps(slot === null ? NO_ROW_PROPS : resolve(slot.args));
602
+ }
603
+
604
+ function applyRowProps(host: PositionedHost, props: ResolvedRowProps, previous: ResolvedRowProps | null): void {
559
605
  if (previous?.accessibleLabel !== props.accessibleLabel) {
560
606
  host.accessibleLabel = props.accessibleLabel;
561
607
  }
@@ -577,9 +623,24 @@ function applyRowProps(host: Gtk.ColumnViewRow, props: ResolvedRowProps, previou
577
623
  }
578
624
  }
579
625
 
626
+ function useAppliedRowProps(host: PositionedHost, props: ResolvedRowProps | null): void {
627
+ const previous = useRef<ResolvedRowProps | null>(null);
628
+
629
+ useLayoutEffect(() => {
630
+ if (props === null && previous.current === null) {
631
+ return;
632
+ }
633
+
634
+ const next = props ?? DEFAULT_ROW_PROPS;
635
+ applyRowProps(host, next, previous.current);
636
+ previous.current = props === null ? null : next;
637
+ }, [host, props]);
638
+ }
639
+
580
640
  function ItemCellImpl({
581
641
  entry,
582
642
  render,
643
+ rowProps,
583
644
  state,
584
645
  hasExpander,
585
646
  expanderDescriptions,
@@ -587,6 +648,7 @@ function ItemCellImpl({
587
648
  }: ItemCellProps): ReactNode {
588
649
  const slot = useItemSlot(entry, state, identity);
589
650
  useRememberIdentity(entry, slot);
651
+ useAppliedRowProps(entry.host, rowProps == null ? null : rowPropsFor(slot, rowProps));
590
652
  const body = itemBody(slot, render, hasExpander, expanderDescriptions);
591
653
 
592
654
  return createPortal(body, entry.host, entry.key);
@@ -595,30 +657,35 @@ function ItemCellImpl({
595
657
  function ItemRowImpl({ entry, rowProps, state, ...identity }: ItemRowProps): ReactNode {
596
658
  const slot = useItemSlot(entry, state, identity);
597
659
  useRememberIdentity(entry, slot);
598
- const props = rowPropsFor(slot, rowProps);
599
- const previous = useRef<ResolvedRowProps | null>(null);
600
-
601
- useLayoutEffect(() => {
602
- applyRowProps(entry.host, props, previous.current);
603
- previous.current = props;
604
- }, [entry.host, props]);
660
+ useAppliedRowProps(entry.host, rowPropsFor(slot, rowProps));
605
661
 
606
662
  return null;
607
663
  }
608
664
 
609
- function HeaderCellImpl({ entry, render, collection }: HeaderCellProps): ReactNode {
665
+ function HeaderCellImpl({ entry, render }: HeaderCellProps): ReactNode {
610
666
  const item = useProperty(entry.host, "item") ?? null;
611
667
 
612
- return createPortal(headerBody(slotRefFor(item), render, collection), entry.host, entry.key);
668
+ return createPortal(headerBody(levelStoreFor(item), render), entry.host, entry.key);
613
669
  }
614
670
 
615
- function NativeItemCell<T extends GObject.Object>({ entry, render, keys, size }: NativeItemCellProps<T>): ReactNode {
671
+ function NativeItemCell<T extends GObject.Object>({
672
+ entry,
673
+ render,
674
+ rowProps,
675
+ keys,
676
+ size,
677
+ }: NativeItemCellProps<T>): ReactNode {
616
678
  const position = useProperty(entry.host, "position");
617
679
  const item = useProperty(entry.host, "item") ?? null;
680
+ const args = item !== null && position !== undefined ? { item: item as T, index: position } : null;
681
+ useAppliedRowProps(
682
+ entry.host,
683
+ rowProps == null ? null : resolveRowProps(args === null ? NO_ROW_PROPS : rowProps(args)),
684
+ );
618
685
  let body: ReactNode = null;
619
686
 
620
- if (item !== null && position !== undefined) {
621
- const content = render({ item: item as T, index: position });
687
+ if (item !== null && args !== null) {
688
+ const content = render(args);
622
689
  const child =
623
690
  content == null || typeof content === "boolean" ? (
624
691
  <GtkBox widthRequest={size.width} heightRequest={size.height} />
@@ -631,19 +698,15 @@ function NativeItemCell<T extends GObject.Object>({ entry, render, keys, size }:
631
698
  return createPortal(body, entry.host, entry.key);
632
699
  }
633
700
 
634
- function headerBody(ref: SlotRef | null, render: ListSectionRenderer<never>, collection: Collection): ReactNode {
635
- if (ref === null) {
636
- return null;
637
- }
638
-
639
- const section = collection.sectionFor(ref.store.level.path);
701
+ function headerBody(store: LazyLevelStore | null, render: ListSectionRenderer<never>): ReactNode {
702
+ const section = store?.level.section;
640
703
 
641
- if (section === undefined) {
704
+ if (store === null || section === undefined) {
642
705
  return null;
643
706
  }
644
707
 
645
708
  const renderHeader = render as ListSectionRenderer<unknown>;
646
- const key = joinParts([ref.store.level.path, section.id]);
709
+ const key = joinParts([store.key, section.id]);
647
710
 
648
711
  return <Fragment key={key}>{renderHeader({ section: section.value })}</Fragment>;
649
712
  }
@@ -652,15 +715,19 @@ function usePortalEntries<H extends FactoryHost>(registry: CellRegistry<H>): Cel
652
715
  return useSyncExternalStore(registry.subscribe, registry.getEntries);
653
716
  }
654
717
 
655
- const ItemPortals = ({
656
- registry,
718
+ function useCollectionVersion(collection: Collection): number {
719
+ return useSyncExternalStore(collection.subscribe, collection.getVersion);
720
+ }
721
+
722
+ function ItemCellListImpl({
723
+ entries,
657
724
  render,
725
+ rowProps,
658
726
  collection,
659
727
  hasExpander,
660
728
  expandedIds,
661
729
  expanderDescriptions,
662
- }: ItemPortalsProps): ReactNode => {
663
- const entries = usePortalEntries(registry);
730
+ }: ItemCellListProps): ReactNode {
664
731
  const state = useLatestRef({ collection, expandedIds });
665
732
 
666
733
  return entries.map((entry) => (
@@ -668,16 +735,23 @@ const ItemPortals = ({
668
735
  key={entry.key}
669
736
  entry={entry}
670
737
  render={render}
738
+ rowProps={rowProps}
671
739
  state={state}
672
740
  hasExpander={hasExpander ?? true}
673
741
  expanderDescriptions={expanderDescriptions}
674
742
  {...itemIdentity(entry, collection, expandedIds)}
675
743
  />
676
744
  ));
677
- };
745
+ }
678
746
 
679
- const RowPortals = ({ registry, rowProps, collection, expandedIds }: RowPortalsProps): ReactNode => {
747
+ const ItemPortals = ({ registry, ...props }: ItemPortalsProps): ReactNode => {
680
748
  const entries = usePortalEntries(registry);
749
+ useCollectionVersion(props.collection);
750
+
751
+ return <ItemCellList {...props} entries={entries} isPending={props.collection.isPending()} />;
752
+ };
753
+
754
+ function ItemRowListImpl({ entries, rowProps, collection, expandedIds }: ItemRowListProps): ReactNode {
681
755
  const state = useLatestRef({ collection, expandedIds });
682
756
 
683
757
  return entries.map((entry) => (
@@ -689,19 +763,33 @@ const RowPortals = ({ registry, rowProps, collection, expandedIds }: RowPortalsP
689
763
  {...itemIdentity(entry, collection, expandedIds)}
690
764
  />
691
765
  ));
766
+ }
767
+
768
+ const RowPortals = ({ registry, ...props }: RowPortalsProps): ReactNode => {
769
+ const entries = usePortalEntries(registry);
770
+ useCollectionVersion(props.collection);
771
+
772
+ return <ItemRowList {...props} entries={entries} isPending={props.collection.isPending()} />;
692
773
  };
693
774
 
694
- const HeaderPortals = ({ registry, render, collection }: HeaderPortalsProps): ReactNode =>
695
- usePortalEntries(registry).map((entry) => (
696
- <HeaderCell key={entry.key} entry={entry} render={render} collection={collection} />
697
- ));
775
+ const HeaderPortals = ({ registry, render, collection }: HeaderPortalsProps): ReactNode => {
776
+ const entries = usePortalEntries(registry);
777
+ const version = useCollectionVersion(collection);
698
778
 
699
- function NativeItemPortals<T extends GObject.Object>({ registry, render, size }: NativeItemPortalsProps<T>): ReactNode {
779
+ return entries.map((entry) => <HeaderCell key={entry.key} entry={entry} render={render} version={version} />);
780
+ };
781
+
782
+ function NativeItemPortals<T extends GObject.Object>({
783
+ registry,
784
+ render,
785
+ rowProps,
786
+ size,
787
+ }: NativeItemPortalsProps<T>): ReactNode {
700
788
  const entries = usePortalEntries(registry);
701
789
  const [keys] = useState(createNativeItemKeyStore);
702
790
 
703
791
  return entries.map((entry) => (
704
- <NativeItemCell key={entry.key} entry={entry} render={render} keys={keys} size={size} />
792
+ <NativeItemCell key={entry.key} entry={entry} render={render} rowProps={rowProps} keys={keys} size={size} />
705
793
  ));
706
794
  }
707
795
 
@@ -1,28 +1,25 @@
1
1
  import type { ListItem, ListSection } from "../types.js";
2
2
  import { decodePartAt, encodePart } from "./keys.js";
3
3
 
4
+ type SectionIdentity = Pick<ListSection, "id" | "value">;
5
+
4
6
  type Level = {
5
7
  path: string;
6
8
  items: ListItem[];
7
9
  expandableFlags: boolean[];
10
+ section?: SectionIdentity | undefined;
8
11
  };
9
12
 
10
- type SectionIdentity = Pick<ListSection, "id" | "value">;
11
-
12
13
  type CollectionIndex = {
13
14
  isTree: boolean;
14
15
  groups: Level[];
15
16
  childLevel: (level: Level, slot: number) => Level | undefined;
16
- levelFor: (levelPath: string) => Level | undefined;
17
- itemAt: (levelPath: string, slot: number) => ListItem | undefined;
18
- sectionFor: (levelPath: string) => SectionIdentity | undefined;
19
17
  };
20
18
 
21
19
  type IndexState = {
22
20
  isTree: boolean;
23
21
  groups: Level[];
24
22
  levels: Map<string, Level>;
25
- sections: Map<string, SectionIdentity>;
26
23
  };
27
24
 
28
25
  type ParentItem = ListItem & { children: ListItem[] };
@@ -97,10 +94,10 @@ function buildGroups(state: IndexState, source: ListItem[], sections: ListSectio
97
94
  }
98
95
 
99
96
  return sections.map((section, group) => {
100
- const path = encodePart(String(group));
101
- state.sections.set(path, { id: section.id, value: section.value });
97
+ const level = newLevel(state, encodePart(String(group)), section.data);
98
+ level.section = { id: section.id, value: section.value };
102
99
 
103
- return newLevel(state, path, section.data);
100
+ return level;
104
101
  });
105
102
  }
106
103
 
@@ -127,7 +124,6 @@ function createCollectionIndex(
127
124
  isTree: isTreeSource(source, sections, isFlat),
128
125
  groups: [],
129
126
  levels: new Map(),
130
- sections: new Map(),
131
127
  };
132
128
 
133
129
  state.groups = buildGroups(state, source, sections);
@@ -136,10 +132,7 @@ function createCollectionIndex(
136
132
  isTree: state.isTree,
137
133
  groups: state.groups,
138
134
  childLevel: (level, slot) => childLevel(state, level, slot),
139
- levelFor: (levelPath) => levelFor(state, levelPath),
140
- itemAt: (levelPath, slot) => levelFor(state, levelPath)?.items[slot],
141
- sectionFor: (levelPath) => state.sections.get(levelPath),
142
135
  };
143
136
  }
144
137
 
145
- export { createCollectionIndex, type CollectionIndex, type Level, type SectionIdentity };
138
+ export { createCollectionIndex, type CollectionIndex, type Level };