@rivetjs/cdk 0.1.0-alpha.0 → 0.1.0-alpha.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.
Files changed (61) hide show
  1. package/_adapters/selection.d.ts +13 -2
  2. package/_adapters/selection.js +39 -15
  3. package/button/index.jsx +5 -3
  4. package/collections/components.d.ts +36 -1
  5. package/collections/components.jsx +224 -16
  6. package/collections/nodes.d.ts +9 -1
  7. package/collections/nodes.js +26 -0
  8. package/collections/row-stamps.d.ts +17 -0
  9. package/collections/row-stamps.js +108 -0
  10. package/collections/server-dev.d.ts +1 -0
  11. package/collections/server-dev.js +2 -0
  12. package/collections/server-off.d.ts +1 -0
  13. package/collections/server-off.js +2 -0
  14. package/collections/server-on.d.ts +1 -0
  15. package/collections/server-on.jsx +10 -0
  16. package/drag-drop/directives/drag.d.ts +16 -0
  17. package/drag-drop/directives/drag.js +28 -0
  18. package/drag-drop/drag.jsx +6 -4
  19. package/drag-drop/lazy-refs.js +15 -4
  20. package/grid-list/behaviour.d.ts +6 -0
  21. package/grid-list/behaviour.js +25 -7
  22. package/grid-list/index.d.ts +3 -1
  23. package/grid-list/index.jsx +60 -24
  24. package/interactions/press.jsx +2 -1
  25. package/interactions/utils.d.ts +2 -3
  26. package/interactions/utils.js +8 -7
  27. package/listbox/behaviour.jsx +37 -14
  28. package/listbox/index.jsx +65 -29
  29. package/menu/index.jsx +38 -7
  30. package/package.json +4 -4
  31. package/portal/index.d.ts +8 -0
  32. package/portal/index.jsx +8 -1
  33. package/radio-group/index.jsx +4 -1
  34. package/selection/keyed.js +6 -16
  35. package/selection/selectable-collection.js +42 -17
  36. package/sidebar/index.jsx +5 -4
  37. package/state/menu.d.ts +3 -0
  38. package/state/menu.js +1 -0
  39. package/table/index.jsx +512 -168
  40. package/tree/index.jsx +26 -4
  41. package/utils/data-attrs.d.ts +4 -2
  42. package/utils/data-attrs.jsx +27 -3
  43. package/utils/index.d.ts +1 -15
  44. package/utils/index.js +5 -102
  45. package/utils/link-state.d.ts +6 -0
  46. package/utils/link-state.js +6 -0
  47. package/utils/links.d.ts +1 -6
  48. package/utils/links.jsx +2 -4
  49. package/utils/transitions.d.ts +15 -0
  50. package/utils/transitions.js +104 -0
  51. package/virtualizer/behaviour.d.ts +35 -4
  52. package/virtualizer/behaviour.js +131 -9
  53. package/virtualizer/collection-renderer.d.ts +62 -0
  54. package/virtualizer/collection-renderer.jsx +190 -0
  55. package/virtualizer/engine.d.ts +1 -1
  56. package/virtualizer/index.d.ts +1 -0
  57. package/virtualizer/index.jsx +1 -0
  58. package/virtualizer/list-layout.js +4 -2
  59. package/virtualizer/state.js +12 -2
  60. package/virtualizer/virtualizer.d.ts +8 -6
  61. package/virtualizer/virtualizer.jsx +4 -1
package/table/index.jsx CHANGED
@@ -25,14 +25,15 @@
25
25
  // inside `<table>` would be foster-parented by the HTML parser).
26
26
  // - Selection checkbox / expand button ids derive from the table id + row key (byte-stable server HTML).
27
27
  // Deviations:
28
- // - Native table elements only (`table` / `thead` / `tr` / `th` / `td`): the Virtualizer table layout
29
- // (`isVirtualized` → `div` elements) is not wired yet.
28
+ // - Inside a `<CollectionVirtualizer layout={TableLayout}>` (RAC useElementType) every part renders a `div` with its role
29
+ // (the JSX of each part is duplicated per element type: one compiled template per tag, the native path untouched);
30
+ // the grid element is the scroll container (it forwards `scroll` to the renderer) and the focused key is persisted.
30
31
  // - Drag and drop (`dragAndDropHooks`, drop indicators, `slot="drag"` button) not wired yet (DnD lane).
31
32
  // - No `FocusScope` wrapper (nothing here reads its context); the SharedElementTransition scope is a `SharedElementContext` provider entry.
32
33
  // - Header placeholder cells (nested columns, react-stately level) render nothing — RAC renders
33
34
  // `node.render` of a placeholder (no render: throws); RAC `Column` is a leaf, so RAC tables never
34
35
  // have them.
35
- import { computed, createContext, provide, signal, untrack, useContext } from "@rivetjs/reactivity";
36
+ import { computed, createContext, provide, runWithOwner, signal, untrack, useContext } from "@rivetjs/reactivity";
36
37
  import { For, Portal, Show, clientEffect, ref as elementRef } from "@rivetjs/runtime/dom";
37
38
  import { isSlot, renderSlot } from "@rivetjs/runtime/slot";
38
39
  import { Collection, CollectionRendererContext, DefaultCollectionRenderer, collectionOf, createBranchComponent, createLeafComponent, renderNodeAt, } from "../collections/components.jsx";
@@ -55,7 +56,8 @@ import { SharedElementContext } from "../shared-element-transition/transition.js
55
56
  import { FieldInputContext, SelectableCollectionContext } from "../autocomplete/contexts.js";
56
57
  import { DEFAULT_SLOT, Provider, contextProps, mergeRefValues, renderProps, resumableContext, } from "../slots/index.jsx";
57
58
  import { dataBool, focusStateBlur, focusStateFocus, focusStateIn, hoverStateEnter, hoverStateLeave, hoverStateOver, } from "../utils/data-attrs.jsx";
58
- import { useFocusRing } from "../focus/focus-ring.jsx";
59
+ import { focusRingBlur, focusRingFocus, useFocusRing } from "../focus/focus-ring.jsx";
60
+ import { isFocusVisible } from "../interactions/focus-visible.jsx";
59
61
  import { focusSafely, getFocusableTreeWalker } from "../focus/index.js";
60
62
  import { getItemElement } from "../collections/dom.js";
61
63
  import { globalDOMProps } from "../utils/dom-props.js";
@@ -294,6 +296,61 @@ export function Table(inProps) {
294
296
  locale,
295
297
  });
296
298
  };
299
+ if (isVirtualized) {
300
+ // RAC useElementType: inside a Virtualizer the table is `div`s (absolutely positioned views; a `<table>` cannot
301
+ // hold the view wrappers). The grid element is the scroll container; its focused row / cell stays rendered.
302
+ const virtualScroll = customRenderer?.onScroll;
303
+ const focusedKey = data.selectionState.focusedKey;
304
+ return (<>
305
+ <div {...dom} {...attrs()} ref={rootRef} class={rp.class()} style={style()} slot={slot} data-focused={dataBool(focused())} data-focus-visible={dataBool(focusVisible())} onKeyDownCapture={(e) => {
306
+ const v = view();
307
+ gridKeyDownCapture(e, gridOf(v, e), v);
308
+ }} onKeyDown={(e) => {
309
+ const v = view();
310
+ gridKeyDown(e, gridOf(v, e), v);
311
+ }} onFocusIn={(e) => {
312
+ const v = view();
313
+ gridFocus(e, gridOf(v, e), v);
314
+ onFocusInFromContext?.(e);
315
+ }} onFocusOut={(e) => {
316
+ focusMemo.blurred = e.relatedTarget == null ? e.target : null;
317
+ const v = view();
318
+ gridBlur(e, gridOf(v, e), v);
319
+ }} onFocus={(e) => focusStateFocus(rootInteraction, e)} onBlur={(e) => focusStateBlur(rootInteraction, e)} onMouseDown={(e) => {
320
+ const v = view();
321
+ gridMouseDown(e, gridOf(v, e), v);
322
+ onMouseDown?.(e);
323
+ }} onScroll={(e) => {
324
+ virtualScroll?.(e);
325
+ onScroll?.(e);
326
+ }}>
327
+ <Provider values={[
328
+ [TableStateContext, view],
329
+ [TableColumnResizeStateContext, layout],
330
+ [TableOptionsContext, options],
331
+ [TableInternalContext, internal],
332
+ [SelectableCollectionContext, null],
333
+ [FieldInputContext, null],
334
+ // RAC: the rows share one SharedElementTransition scope (a SelectionIndicator animates between them).
335
+ [SharedElementContext, sharedElements],
336
+ ]}>
337
+ {(customRenderer ?? DefaultCollectionRenderer).CollectionRoot({ collection: collection, scrollRef: el, focusedKey })}
338
+ </Provider>
339
+ </div>
340
+ <div id={sortDescriptionId} hidden>
341
+ {sortDescription() ?? ""}
342
+ </div>
343
+ <div id={internal.sortableDescriptionId} hidden>
344
+ {formatMessage(tableStrings, locale, "sortable")}
345
+ </div>
346
+ <div id={descriptionId} hidden>
347
+ {longPressDescription() ?? ""}
348
+ </div>
349
+ {resizeData ? (<div id={internal.resizerDescriptionId} hidden>
350
+ {formatMessage(tableStrings, locale, "resizerDescription")}
351
+ </div>) : null}
352
+ </>);
353
+ }
297
354
  return (<>
298
355
  <table {...dom} {...attrs()} ref={rootRef} class={rp.class()} style={style()} slot={slot} data-focused={dataBool(focused())} data-focus-visible={dataBool(focusVisible())} onKeyDownCapture={(e) => {
299
356
  const v = view();
@@ -357,6 +414,22 @@ export const TableHeader = /* @__PURE__ */ createBranchComponent(TableHeaderNode
357
414
  const { onPointerEnter, onPointerLeave } = props;
358
415
  const dom = globalDOMProps(props, HOVER_OWNED);
359
416
  const rowKeys = () => internal.collection().headerRows.map((row) => row.key);
417
+ if (internal.isVirtualized) {
418
+ // Virtualized (RAC useElementType): `div` row group inside the header view.
419
+ return (<div {...dom} role="rowgroup" ref={ref} class={rp.class()} style={rp.style()} onPointerEnter={(e) => {
420
+ hoverStateEnter(interaction, e);
421
+ onPointerEnter?.(e);
422
+ }} onPointerLeave={(e) => {
423
+ hoverStateLeave(interaction, e);
424
+ onPointerLeave?.(e);
425
+ }}>
426
+ {/* Not lazy (LAZY-T6): a header row holds the virtualizer's column views (a nested collection); a lazy
427
+ header row materialised mid-layout lost them. Header rows are few: nothing to save. */}
428
+ <For each={rowKeys} by={(k) => k} lazy={false}>
429
+ {(k) => <TableHeaderRow rowKey={untrack(k)}/>}
430
+ </For>
431
+ </div>);
432
+ }
360
433
  return (<thead {...dom} role="rowgroup" ref={ref} class={rp.class()} style={rp.style()} onPointerEnter={(e) => {
361
434
  hoverStateEnter(interaction, e);
362
435
  onPointerEnter?.(e);
@@ -364,7 +437,8 @@ export const TableHeader = /* @__PURE__ */ createBranchComponent(TableHeaderNode
364
437
  hoverStateLeave(interaction, e);
365
438
  onPointerLeave?.(e);
366
439
  }}>
367
- <For each={rowKeys} by={(k) => k}>
440
+ {/* Not lazy (LAZY-T6): header rows hold the column cells (a nested collection) and are few. */}
441
+ <For each={rowKeys} by={(k) => k} lazy={false}>
368
442
  {(k) => <TableHeaderRow rowKey={untrack(k)}/>}
369
443
  </For>
370
444
  </thead>);
@@ -394,6 +468,23 @@ function TableHeaderRow(props) {
394
468
  keys.push(n.key);
395
469
  return keys;
396
470
  };
471
+ if (isVirtualized) {
472
+ // Virtualized: `div` row; the virtualizer renders the column views.
473
+ const branchRenderer = useContext(CollectionRendererContext);
474
+ const customBranch = branchRenderer === DefaultCollectionRenderer ? undefined : branchRenderer;
475
+ return (<div {...attrs()}>
476
+ <Provider values={[
477
+ [CheckboxContext, { slots: { selection: checkboxProps } }],
478
+ [CheckboxFieldContext, { slots: { selection: checkboxProps } }],
479
+ ]}>
480
+ {/* The virtualizer renders the header row's column views (positioned by the layout). */}
481
+ {(customBranch ?? DefaultCollectionRenderer).CollectionBranch({
482
+ collection: internal.collection,
483
+ parent: { key: rowKey },
484
+ })}
485
+ </Provider>
486
+ </div>);
487
+ }
397
488
  return (<tr {...attrs()}>
398
489
  <Provider values={[
399
490
  [CheckboxContext, { slots: { selection: checkboxProps } }],
@@ -480,6 +571,55 @@ export const Column = /* @__PURE__ */ createLeafComponent(TableColumnNode, funct
480
571
  };
481
572
  const { onPointerDown, onPointerUp, onPointerEnter, onPointerLeave, onClick } = props;
482
573
  const dom = globalDOMProps(props, COLUMN_OWNED);
574
+ if (isVirtualized) {
575
+ // Virtualized (RAC useElementType): `div` column header inside its view.
576
+ return (<div {...dom} {...attrs()} ref={thRef} class={rp.class()} style={style()} data-resizing={dataBool(isResizing())} data-allows-sorting={dataBool(allowsSorting())} data-sort-direction={sortDirection()} onKeyDownCapture={(e) => {
577
+ const v = view();
578
+ gridCellKeyDownCapture(e, hopts(v), v);
579
+ }} onKeyDown={(e) => {
580
+ const v = view();
581
+ gridCellKeyDown(e, hopts(v), v);
582
+ pressKeyDown(press(v), e);
583
+ }} onKeyUp={(e) => {
584
+ const v = view();
585
+ gridCellKeyUp(e, hopts(v), v);
586
+ }} onFocusIn={(e) => {
587
+ const v = view();
588
+ gridCellFocus(e, hopts(v), v);
589
+ }} onFocus={(e) => {
590
+ focusStateFocus(interaction, e);
591
+ }} onBlur={(e) => {
592
+ focusStateBlur(interaction, e);
593
+ }} onPointerEnter={(e) => {
594
+ hoverStateEnter(interaction, e);
595
+ pressPointerEnter(press(view()), e);
596
+ onPointerEnter?.(e);
597
+ }} onPointerLeave={(e) => {
598
+ hoverStateLeave(interaction, e);
599
+ pressPointerLeave(press(view()), e);
600
+ onPointerLeave?.(e);
601
+ }} onPointerDown={(e) => {
602
+ const v = view();
603
+ gridCellPointerDown(e, hopts(v), v);
604
+ pressPointerDown(press(v), e);
605
+ onPointerDown?.(e);
606
+ }} onPointerUp={(e) => {
607
+ const v = view();
608
+ gridCellPointerUp(e, hopts(v), v);
609
+ pressPointerUp(press(v), e);
610
+ onPointerUp?.(e);
611
+ }} onMouseDown={(e) => pressMouseDown(press(view()), e)} onClick={(e) => {
612
+ const v = view();
613
+ gridCellClick(e, hopts(v), v);
614
+ pressClick(press(v), e);
615
+ onClick?.(e);
616
+ }} onDragStart={(e) => pressDragStart(press(view()), e)}>
617
+ {
618
+ // A binding (not a Provider child slot): a render function that reads render-prop accessors
619
+ // and returns text re-runs on change, as RAC re-renders.
620
+ provide(ColumnResizerContext, { columnKey: key, triggerRef: th }, () => provide(CollectionRendererContext, DefaultCollectionRenderer, () => rp.children()))}
621
+ </div>);
622
+ }
483
623
  return (<th {...dom} {...attrs()} ref={thRef} class={rp.class()} style={style()} data-resizing={dataBool(isResizing())} data-allows-sorting={dataBool(allowsSorting())} data-sort-direction={sortDirection()} onKeyDownCapture={(e) => {
484
624
  const v = view();
485
625
  gridCellKeyDownCapture(e, hopts(v), v);
@@ -643,6 +783,19 @@ export const TableBody = /* @__PURE__ */ createBranchComponent(TableBodyNode, fu
643
783
  const numColumns = () => internal.collection().columnCount;
644
784
  const dom = globalDOMProps(props, ["style", "id"]);
645
785
  // TODO (upstream): TableBody doesn't support being the scrollable body of the table yet.
786
+ if (internal.isVirtualized) {
787
+ // Virtualized (RAC useElementType): `div` row group; the virtualizer renders the row views.
788
+ return (<div {...dom} role="rowgroup" ref={ref} class={rp.class()} style={rp.style()} data-empty={dataBool(isEmpty())}>
789
+ {(customBranch ?? DefaultCollectionRenderer).CollectionBranch({ collection: internal.collection, parent: branchParent })}
790
+ {renderEmptyState ? (<Show when={isEmpty}>
791
+ {() => (<div role="row" style={{ height: "100%" }}>
792
+ <div role="rowheader" aria-colspan={numColumns()} style={{ height: "100%" }}>
793
+ {renderEmptyState(values)}
794
+ </div>
795
+ </div>)}
796
+ </Show>) : null}
797
+ </div>);
798
+ }
646
799
  return (<tbody {...dom} role="rowgroup" ref={ref} class={rp.class()} style={rp.style()} data-empty={dataBool(isEmpty())}>
647
800
  {(customBranch ?? DefaultCollectionRenderer).CollectionBranch({ collection: internal.collection, parent: branchParent })}
648
801
  {renderEmptyState ? (<Show when={isEmpty}>
@@ -664,6 +817,12 @@ export const TableFooter = /* @__PURE__ */ createBranchComponent(TableFooterNode
664
817
  const dom = globalDOMProps(props, ["style", "id"]);
665
818
  const className = props.class || "rv-TableFooter";
666
819
  const style = props.style;
820
+ if (internal.isVirtualized) {
821
+ // Virtualized (RAC useElementType): `div` row group.
822
+ return (<div {...dom} role="rowgroup" ref={ref} class={className} style={style}>
823
+ {(customBranch ?? DefaultCollectionRenderer).CollectionBranch({ collection: internal.collection, parent: branchParent })}
824
+ </div>);
825
+ }
667
826
  return (<tfoot {...dom} role="rowgroup" ref={ref} class={className} style={style}>
668
827
  {(customBranch ?? DefaultCollectionRenderer).CollectionBranch({ collection: internal.collection, parent: branchParent })}
669
828
  </tfoot>);
@@ -681,37 +840,43 @@ const ROW_OWNED = [
681
840
  "style",
682
841
  "id",
683
842
  ];
684
- /** A row within a `<Table>`. */
685
- export const Row = /* @__PURE__ */ createBranchComponent(TableRowNode, function Row(props, forwardedRef, item) {
686
- const view = useContext(TableStateContext);
687
- const internal = useContext(TableInternalContext);
688
- const key = item.key;
689
- const isVirtualized = internal.isVirtualized;
690
- const locale = internal.locale;
691
- const router = useRouter();
692
- const rowBase = `${internal.gridId}-${normalizeKey(key)}`;
693
- const expandButtonId = rowBase + "-expand";
694
- const checkboxId = rowBase + "-checkbox";
695
- const isTreeGrid = internal.data.treeColumn != null;
696
- const { hasChildItems: hasChildItemsProp, onHoverStart, onHoverChange, onHoverEnd } = props;
697
- const ropts = (v, el) => ({
698
- node: v.collection.getItem(key),
699
- isVirtualized,
843
+ const rowDerived = /* @__PURE__ */ new WeakMap();
844
+ // Row slot values shared by every row (one resume entry each): context consumers copy them, never write.
845
+ const NO_SLOT_PROPS = {};
846
+ const DRAG_SLOT_PROPS = { style: { pointerEvents: "none" } };
847
+ const ROW_BUTTONS = { slots: { [DEFAULT_SLOT]: NO_SLOT_PROPS, chevron: NO_SLOT_PROPS, drag: DRAG_SLOT_PROPS } };
848
+ function rowOptions(r, v, el) {
849
+ return {
850
+ node: v.collection.getItem(r.key),
851
+ isVirtualized: r.isVirtualized,
700
852
  shouldSelectOnPressUp: false,
701
- expandButtonId,
702
- router,
703
- locale,
853
+ expandButtonId: r.expandButtonId,
854
+ router: r.router,
855
+ locale: r.locale,
704
856
  ref: el ?? undefined,
705
- });
706
- const row = elementRef();
707
- const rowRef = mergeRefValues(row, forwardedRef);
857
+ };
858
+ }
859
+ /** The derived parts of a row (built once per `RowData`; owner as `cellOf`). */
860
+ function rowOf(r) {
861
+ let d = rowDerived.get(r);
862
+ if (!d)
863
+ rowDerived.set(r, (d = untrack(() => runWithOwner(null, () => deriveRow(r)))));
864
+ return d;
865
+ }
866
+ function initRow(r) {
867
+ const d = deriveRow(r);
868
+ rowDerived.set(r, d);
869
+ return d;
870
+ }
871
+ function deriveRow(r) {
872
+ const { view, key, locale } = r;
708
873
  const st = computed(() => {
709
874
  const v = view();
710
875
  const n = v.collection.getItem(key);
711
876
  if (!n)
712
877
  return null;
713
- const aria = tableRowProps(ropts(v), v, locale);
714
- const hasChildItems = !!hasChildItemsProp || v.collection.getItem(n.lastChildKey)?.type !== "cell";
878
+ const aria = tableRowProps(rowOptions(r, v), v, locale);
879
+ const hasChildItems = !!r.hasChildItems || v.collection.getItem(n.lastChildKey)?.type !== "cell";
715
880
  return {
716
881
  aria,
717
882
  hasChildItems,
@@ -721,132 +886,221 @@ export const Row = /* @__PURE__ */ createBranchComponent(TableRowNode, function
721
886
  selectionBehavior: v.selectionManager.selectionBehavior,
722
887
  };
723
888
  });
724
- const hovered = signal(false);
725
- const pressed = signal(false);
726
- const focusVisible = signal(false);
727
- const within = useFocusRing({ within: true });
728
889
  const interaction = {
729
890
  // because of https://bugs.webkit.org/show_bug.cgi?id=214609, hover only when selectable or with an action.
730
891
  isDisabled: () => !st()?.aria.allowsSelection && !st()?.aria.hasAction,
731
- onHoverStart,
732
- onHoverChange,
733
- onHoverEnd,
734
- states: { isHovered: hovered, isFocusVisible: focusVisible },
892
+ onHoverStart: r.onHoverStart,
893
+ onHoverChange: r.onHoverChange,
894
+ onHoverEnd: r.onHoverEnd,
895
+ states: { isHovered: r.hovered, isFocusVisible: r.focusVisible },
735
896
  };
736
- // Row `isPressed` lives in the press memo of the row element: re-read it after the row's press handlers.
737
- const syncPressed = (target) => {
738
- const v = view();
739
- if (v.collection.getItem(key))
740
- pressed.set(tableRowProps(ropts(v, target), v, locale).isPressed);
741
- };
742
- const selectionMode = () => st()?.selectionMode ?? "none";
743
897
  const values = {
744
- isHovered: () => hovered(),
745
- isPressed: () => pressed(),
746
- isSelected: () => !!st()?.aria.isSelected,
898
+ isHovered: () => r.hovered(),
899
+ isPressed: () => r.pressed(),
900
+ // Provided to the row's content (SelectionIndicatorContext, the selection checkbox): captures only `r`.
901
+ isSelected: () => !!rowOf(r).st()?.aria.isSelected,
747
902
  isFocused: () => !!st()?.aria.isFocused,
748
- isFocusVisible: () => focusVisible(),
903
+ isFocusVisible: () => r.focusVisible(),
749
904
  isDisabled: () => !!st()?.aria.isDisabled,
750
- selectionMode,
905
+ selectionMode: () => st()?.selectionMode ?? "none",
751
906
  selectionBehavior: () => st()?.selectionBehavior ?? "toggle",
752
907
  allowsDragging: () => false,
753
908
  isDragging: () => false,
754
909
  isDropTarget: () => false,
755
- isFocusVisibleWithin: () => within.isFocusVisible(),
910
+ isFocusVisibleWithin: () => r.withinVisible(),
756
911
  id: key,
757
912
  hasChildItems: () => !!st()?.hasChildItems,
758
913
  isExpanded: () => !!st()?.isExpanded,
759
914
  level: () => st()?.level ?? 1,
760
915
  state: view,
761
916
  };
762
- const rp = untrack(() => renderProps({
763
- class: props.class,
764
- style: props.style,
765
- values,
766
- defaultClassName: "rv-Row",
767
- defaultStyle: { "--table-row-level": untrack(values.level) },
768
- }, hasLive(props) ? liveRenderProps(props) : undefined));
769
- // useTableSelectionCheckbox (Rivet: the id derives from the table id + row key, see header).
770
- const checkboxProps = {
771
- id: checkboxId,
772
- "aria-label": untrack(() => tableSelectionCheckboxProps({ key, id: checkboxId, locale }, view()).checkboxProps["aria-label"]),
773
- "aria-labelledby": untrack(() => `${checkboxId} ${getRowLabelledBy(view(), key)}`),
774
- isSelected: () => !!st()?.aria.isSelected,
775
- isDisabled: () => !view().selectionManager.canSelectItem(key),
776
- onChange: (isSelected) => tableSelectionCheckboxChange(isSelected, { key }, view()),
917
+ const rp = untrack(() => renderProps({ class: r.class, style: r.style, values, defaultClassName: "rv-Row", defaultStyle: { "--table-row-level": untrack(values.level) } }, r.live));
918
+ // useFocusRing({ within: true }) over the row's own signals (the binding of `data-focus-visible-within` reads them).
919
+ const ring = {
920
+ props: { within: true },
921
+ focused: r.withinFocused,
922
+ visible: r.withinVisible,
923
+ state: { isFocused: false, isFocusVisible: isFocusVisible(), unsubscribe: null },
924
+ };
925
+ return { st, values, rp, interaction, ring };
926
+ }
927
+ /** Row `isPressed` lives in the press memo of the row element: re-read it after the row's press handlers. */
928
+ function rowSyncPressed(r, target) {
929
+ const v = r.view();
930
+ if (v.collection.getItem(r.key))
931
+ r.pressed.set(tableRowProps(rowOptions(r, v, target), v, r.locale).isPressed);
932
+ }
933
+ /** A row event through a grid row handler (+ the pressed re-read). */
934
+ function rowOn(r, e, fn, sync) {
935
+ const v = r.view();
936
+ fn(e, rowOptions(r, v), v);
937
+ if (sync)
938
+ rowSyncPressed(r, e.currentTarget);
939
+ }
940
+ /** The row's own attributes after the user's DOM props (one spread binding). */
941
+ function rowAttrs(r) {
942
+ const { st, values, rp } = rowOf(r);
943
+ const selectionMode = values.selectionMode();
944
+ return {
945
+ ...(st()?.aria.rowProps ?? { role: "row" }),
946
+ class: rp.class(),
947
+ style: rp.style(),
948
+ "data-disabled": dataBool(values.isDisabled()),
949
+ "data-selected": dataBool(values.isSelected()),
950
+ "data-focused": dataBool(values.isFocused()),
951
+ "data-pressed": dataBool(r.pressed()),
952
+ "data-selection-mode": selectionMode === "none" ? undefined : selectionMode,
953
+ "data-expanded": dataBool(values.isExpanded()),
954
+ "data-has-child-items": dataBool(values.hasChildItems()),
955
+ "data-level": values.level(),
956
+ "data-focus-visible-within": dataBool(values.isFocusVisibleWithin()),
777
957
  };
778
- const chevron = isTreeGrid
958
+ }
959
+ /** A row within a `<Table>`. */
960
+ export const Row = /* @__PURE__ */ createBranchComponent(TableRowNode, function Row(props, forwardedRef, item) {
961
+ const view = useContext(TableStateContext);
962
+ const internal = useContext(TableInternalContext);
963
+ const key = item.key;
964
+ const locale = internal.locale;
965
+ const rowBase = `${internal.gridId}-${normalizeKey(key)}`;
966
+ const expandButtonId = rowBase + "-expand";
967
+ const checkboxId = rowBase + "-checkbox";
968
+ const isTreeGrid = internal.data.treeColumn != null;
969
+ const live = hasLive(props);
970
+ const r = {
971
+ key,
972
+ view,
973
+ isVirtualized: internal.isVirtualized,
974
+ locale,
975
+ router: useRouter(),
976
+ expandButtonId,
977
+ hasChildItems: props.hasChildItems,
978
+ onHoverStart: props.onHoverStart,
979
+ onHoverChange: props.onHoverChange,
980
+ onHoverEnd: props.onHoverEnd,
981
+ hovered: signal(false),
982
+ pressed: signal(false),
983
+ focusVisible: signal(false),
984
+ withinFocused: signal(false),
985
+ withinVisible: signal(false),
986
+ class: live ? undefined : props.class,
987
+ style: live ? undefined : props.style,
988
+ live: live ? liveRenderProps(props, ["class", "style"]) : undefined,
989
+ };
990
+ const { values } = initRow(r);
991
+ // useTableSelectionCheckbox (Rivet: the id derives from the table id + row key, see header). One slots value for
992
+ // CheckboxContext + CheckboxFieldContext; closures capture `r` (the row region's providers are in its resume state).
993
+ const checkboxSlots = {
994
+ slots: {
995
+ [DEFAULT_SLOT]: NO_SLOT_PROPS,
996
+ selection: {
997
+ id: checkboxId,
998
+ "aria-label": untrack(() => tableSelectionCheckboxProps({ key, id: checkboxId, locale }, view()).checkboxProps["aria-label"]),
999
+ "aria-labelledby": untrack(() => `${checkboxId} ${getRowLabelledBy(view(), key)}`),
1000
+ isSelected: values.isSelected,
1001
+ isDisabled: () => !r.view().selectionManager.canSelectItem(r.key),
1002
+ onChange: (isSelected) => tableSelectionCheckboxChange(isSelected, { key: r.key }, r.view()),
1003
+ },
1004
+ },
1005
+ };
1006
+ const buttons = isTreeGrid
779
1007
  ? {
780
- id: expandButtonId,
781
- "aria-label": () => st()?.aria.expandButtonProps["aria-label"],
782
- "aria-labelledby": untrack(() => st()?.aria.expandButtonProps["aria-labelledby"]),
783
- isDisabled: () => !!st()?.aria.expandButtonProps.isDisabled,
784
- excludeFromTabOrder: true,
785
- preventFocusOnPress: true,
786
- "data-react-aria-prevent-focus": "true",
787
- onPress: () => {
788
- const v = view();
789
- tableRowExpandPress(ropts(v), v);
1008
+ slots: {
1009
+ [DEFAULT_SLOT]: NO_SLOT_PROPS,
1010
+ chevron: {
1011
+ id: expandButtonId,
1012
+ "aria-label": () => rowOf(r).st()?.aria.expandButtonProps["aria-label"],
1013
+ "aria-labelledby": untrack(() => rowOf(r).st()?.aria.expandButtonProps["aria-labelledby"]),
1014
+ isDisabled: () => !!rowOf(r).st()?.aria.expandButtonProps.isDisabled,
1015
+ excludeFromTabOrder: true,
1016
+ preventFocusOnPress: true,
1017
+ "data-react-aria-prevent-focus": "true",
1018
+ onPress: () => {
1019
+ const v = r.view();
1020
+ tableRowExpandPress(rowOptions(r, v), v);
1021
+ },
1022
+ },
1023
+ drag: DRAG_SLOT_PROPS,
790
1024
  },
791
1025
  }
792
- : {};
1026
+ : ROW_BUTTONS;
793
1027
  const { onPointerDown, onPointerUp, onPointerCancel, onPointerEnter, onPointerLeave, onDblClick } = props;
794
1028
  const dom = globalDOMProps(props, ROW_OWNED);
795
1029
  const branchRenderer = useContext(CollectionRendererContext);
796
1030
  const customBranch = branchRenderer === DefaultCollectionRenderer ? undefined : branchRenderer;
797
- // The built renderer only reads the parent key: a plain object, not the node class instance (resumable capture).
798
- const branchParent = { key: key };
799
- return (<tr {...dom} {...(st()?.aria.rowProps ?? { role: "row" })} ref={rowRef} class={rp.class()} style={rp.style()} data-disabled={dataBool(values.isDisabled())} data-selected={dataBool(values.isSelected())} data-focused={dataBool(values.isFocused())} data-pressed={dataBool(pressed())} data-selection-mode={selectionMode() === "none" ? undefined : selectionMode()} data-expanded={dataBool(values.isExpanded())} data-has-child-items={dataBool(values.hasChildItems())} data-level={values.level()} data-focus-visible-within={dataBool(values.isFocusVisibleWithin())} onKeyDown={(e) => {
800
- const v = view();
801
- gridRowKeyDown(e, ropts(v), v);
1031
+ if (r.isVirtualized) {
1032
+ // Virtualized (RAC useElementType): `div` row inside its view; the virtualizer renders the cell views.
1033
+ return (<div {...dom} {...rowAttrs(r)} ref={forwardedRef} onKeyDown={(e) => {
1034
+ const v = r.view();
1035
+ gridRowKeyDown(e, rowOptions(r, v), v);
1036
+ if (!e.cancelBubble)
1037
+ tableRowKeyDown(e, rowOptions(r, v), v);
1038
+ rowSyncPressed(r, e.currentTarget);
1039
+ }} onKeyUp={(e) => rowOn(r, e, gridRowKeyUp, true)} onFocusIn={(e) => {
1040
+ // The row's own focus (was a native `focus` listener per row; it ran before this focusin): its `blur` binds while focused.
1041
+ const d = rowOf(r);
1042
+ focusStateIn(d.interaction, e);
1043
+ rowOn(r, e, gridRowFocus);
1044
+ focusRingFocus(d.ring, e);
1045
+ }} onFocusOut={(e) => focusRingBlur(rowOf(r).ring, e)} onPointerOver={(e) => hoverStateOver(rowOf(r).interaction, e)} onPointerEnter={onPointerEnter ? (e) => onPointerEnter(e) : undefined} onPointerLeave={onPointerLeave ? (e) => onPointerLeave(e) : undefined} onPointerDown={(e) => {
1046
+ rowOn(r, e, gridRowPointerDown, true);
1047
+ onPointerDown?.(e);
1048
+ }} onPointerUp={(e) => {
1049
+ rowOn(r, e, gridRowPointerUp, true);
1050
+ onPointerUp?.(e);
1051
+ }} onPointerCancel={(e) => {
1052
+ rowOn(r, e, gridRowPointerCancel, true);
1053
+ onPointerCancel?.(e);
1054
+ }} onClick={(e) => rowOn(r, e, gridRowClick, true)} onDblClick={(e) => {
1055
+ rowOn(r, e, gridRowDoubleClick);
1056
+ onDblClick?.(e);
1057
+ }} onDragStart={(e) => rowOn(r, e, gridRowDragStart)}>
1058
+ <Provider values={[
1059
+ [CheckboxContext, checkboxSlots],
1060
+ [CheckboxFieldContext, checkboxSlots],
1061
+ [ButtonContext, buttons],
1062
+ [SelectionIndicatorContext, { isSelected: values.isSelected }],
1063
+ [RowFocusContext, { isFocusVisibleWithinRow: values.isFocusVisibleWithin }],
1064
+ ]}>
1065
+ {/* The built renderer only reads the parent key: the row data (`key`), not the node class instance (resumable capture). */}
1066
+ {(customBranch ?? DefaultCollectionRenderer).CollectionBranch({ collection: internal.collection, parent: r })}
1067
+ </Provider>
1068
+ </div>);
1069
+ }
1070
+ return (<tr {...dom} {...rowAttrs(r)} ref={forwardedRef} onKeyDown={(e) => {
1071
+ const v = r.view();
1072
+ gridRowKeyDown(e, rowOptions(r, v), v);
802
1073
  if (!e.cancelBubble)
803
- tableRowKeyDown(e, ropts(v), v);
804
- syncPressed(e.currentTarget);
805
- }} onKeyUp={(e) => {
806
- const v = view();
807
- gridRowKeyUp(e, ropts(v), v);
808
- syncPressed(e.currentTarget);
809
- }} onFocusIn={(e) => {
1074
+ tableRowKeyDown(e, rowOptions(r, v), v);
1075
+ rowSyncPressed(r, e.currentTarget);
1076
+ }} onKeyUp={(e) => rowOn(r, e, gridRowKeyUp, true)} onFocusIn={(e) => {
810
1077
  // The row's own focus (was a native `focus` listener per row; it ran before this focusin): its `blur` binds while focused.
811
- focusStateIn(interaction, e);
812
- const v = view();
813
- gridRowFocus(e, ropts(v), v);
814
- within.focusProps.onFocusIn(e);
815
- }} onFocusOut={(e) => within.focusProps.onFocusOut(e)} onPointerOver={(e) => hoverStateOver(interaction, e)} onPointerEnter={onPointerEnter ? (e) => onPointerEnter(e) : undefined} onPointerLeave={onPointerLeave ? (e) => onPointerLeave(e) : undefined} onPointerDown={(e) => {
816
- const v = view();
817
- gridRowPointerDown(e, ropts(v), v);
818
- syncPressed(e.currentTarget);
1078
+ const d = rowOf(r);
1079
+ focusStateIn(d.interaction, e);
1080
+ rowOn(r, e, gridRowFocus);
1081
+ focusRingFocus(d.ring, e);
1082
+ }} onFocusOut={(e) => focusRingBlur(rowOf(r).ring, e)} onPointerOver={(e) => hoverStateOver(rowOf(r).interaction, e)} onPointerEnter={onPointerEnter ? (e) => onPointerEnter(e) : undefined} onPointerLeave={onPointerLeave ? (e) => onPointerLeave(e) : undefined} onPointerDown={(e) => {
1083
+ rowOn(r, e, gridRowPointerDown, true);
819
1084
  onPointerDown?.(e);
820
1085
  }} onPointerUp={(e) => {
821
- const v = view();
822
- gridRowPointerUp(e, ropts(v), v);
823
- syncPressed(e.currentTarget);
1086
+ rowOn(r, e, gridRowPointerUp, true);
824
1087
  onPointerUp?.(e);
825
1088
  }} onPointerCancel={(e) => {
826
- const v = view();
827
- gridRowPointerCancel(e, ropts(v), v);
828
- syncPressed(e.currentTarget);
1089
+ rowOn(r, e, gridRowPointerCancel, true);
829
1090
  onPointerCancel?.(e);
830
- }} onClick={(e) => {
831
- const v = view();
832
- gridRowClick(e, ropts(v), v);
833
- syncPressed(e.currentTarget);
834
- }} onDblClick={(e) => {
835
- const v = view();
836
- gridRowDoubleClick(e, ropts(v), v);
1091
+ }} onClick={(e) => rowOn(r, e, gridRowClick, true)} onDblClick={(e) => {
1092
+ rowOn(r, e, gridRowDoubleClick);
837
1093
  onDblClick?.(e);
838
- }} onDragStart={(e) => {
839
- const v = view();
840
- gridRowDragStart(e, ropts(v), v);
841
- }}>
1094
+ }} onDragStart={(e) => rowOn(r, e, gridRowDragStart)}>
842
1095
  <Provider values={[
843
- [CheckboxContext, { slots: { [DEFAULT_SLOT]: {}, selection: checkboxProps } }],
844
- [CheckboxFieldContext, { slots: { [DEFAULT_SLOT]: {}, selection: checkboxProps } }],
845
- [ButtonContext, { slots: { [DEFAULT_SLOT]: {}, chevron, drag: { style: { pointerEvents: "none" } } } }],
1096
+ [CheckboxContext, checkboxSlots],
1097
+ [CheckboxFieldContext, checkboxSlots],
1098
+ [ButtonContext, buttons],
846
1099
  [SelectionIndicatorContext, { isSelected: values.isSelected }],
847
1100
  [RowFocusContext, { isFocusVisibleWithinRow: values.isFocusVisibleWithin }],
848
1101
  ]}>
849
- {(customBranch ?? DefaultCollectionRenderer).CollectionBranch({ collection: internal.collection, parent: branchParent })}
1102
+ {/* The built renderer only reads the parent key: the row data (`key`), not the node class instance (resumable capture). */}
1103
+ {(customBranch ?? DefaultCollectionRenderer).CollectionBranch({ collection: internal.collection, parent: r })}
850
1104
  </Provider>
851
1105
  </tr>);
852
1106
  }, (props) => {
@@ -857,38 +1111,48 @@ export const Row = /* @__PURE__ */ createBranchComponent(TableRowNode, function
857
1111
  return Collection({ items: props.columns, children, idScope: props.id });
858
1112
  });
859
1113
  const CELL_OWNED = ["onPointerDown", "onPointerUp", "onPointerEnter", "onPointerLeave", "onClick", "style", "id"];
860
- /** A cell within a table row. */
861
- export const Cell = /* @__PURE__ */ createLeafComponent(TableCellNode, function Cell(props, forwardedRef, cell) {
862
- const view = useContext(TableStateContext);
863
- const internal = useContext(TableInternalContext);
864
- const rowFocus = useContext(RowFocusContext);
865
- const key = cell.key;
866
- const rowKey = cell.parentKey;
867
- const isVirtualized = internal.isVirtualized;
868
- const { focusMode, allowsArrowNavigation } = props;
869
- const nodeOf = (v) => {
870
- const n = v.collection.getItem(key);
871
- if (n)
872
- n.column = v.collection.columns[n.index];
873
- return n;
874
- };
875
- const copts = (v, n, el) => ({
1114
+ const cellDerived = /* @__PURE__ */ new WeakMap();
1115
+ function cellNode(c, v) {
1116
+ const n = v.collection.getItem(c.key);
1117
+ if (n)
1118
+ n.column = v.collection.columns[n.index];
1119
+ return n;
1120
+ }
1121
+ function cellOptions(c, n) {
1122
+ return {
876
1123
  node: n,
877
- isVirtualized,
878
- focusMode,
879
- allowsArrowNavigation,
1124
+ isVirtualized: c.isVirtualized,
1125
+ focusMode: c.focusMode,
1126
+ allowsArrowNavigation: c.allowsArrowNavigation,
880
1127
  shouldSelectOnPressUp: false,
881
- ref: el ?? undefined,
882
- });
1128
+ ref: undefined,
1129
+ };
1130
+ }
1131
+ /**
1132
+ * The derived parts of a cell: built by the component (`initCell`, under its owner) or, on a resumed page, on first
1133
+ * use outside any owner (page lifetime, as the resume decoder creates captured computeds) — never under the binding /
1134
+ * handler that asked first (its re-run would dispose the memoized computed).
1135
+ */
1136
+ function cellOf(c) {
1137
+ let d = cellDerived.get(c);
1138
+ if (!d)
1139
+ cellDerived.set(c, (d = untrack(() => runWithOwner(null, () => deriveCell(c)))));
1140
+ return d;
1141
+ }
1142
+ function initCell(c) {
1143
+ cellDerived.set(c, deriveCell(c));
1144
+ }
1145
+ function deriveCell(c) {
1146
+ const { view, rowKey } = c;
883
1147
  const st = computed(() => {
884
1148
  const v = view();
885
- const n = nodeOf(v);
1149
+ const n = cellNode(c, v);
886
1150
  if (!n)
887
1151
  return null;
888
1152
  const row = v.collection.getItem(rowKey);
889
1153
  const hasChildItems = !!row && (!!row.props?.hasChildItems || v.collection.getItem(row.lastChildKey)?.type !== "cell");
890
1154
  return {
891
- ...tableCellProps(copts(v, n), v),
1155
+ ...tableCellProps(cellOptions(c, n), v),
892
1156
  isSelected: v.selectionManager.isSelected(rowKey),
893
1157
  isDisabled: v.selectionManager.isDisabled(rowKey),
894
1158
  hasChildItems,
@@ -899,54 +1163,117 @@ export const Cell = /* @__PURE__ */ createLeafComponent(TableCellNode, function
899
1163
  isTreeColumn: n.column?.key === v.treeColumn,
900
1164
  };
901
1165
  });
902
- // Cell events with the node for the event (skipped once the cell left the collection).
903
- const on = (e, fn) => {
904
- const v = view();
905
- const n = nodeOf(v);
906
- if (n)
907
- fn(e, copts(v, n), v);
908
- };
909
- const hovered = signal(false);
910
- const focused = signal(false);
911
- const focusVisible = signal(false);
912
- const interaction = { states: { isHovered: hovered, isFocused: focused, isFocusVisible: focusVisible } };
913
1166
  const values = {
914
1167
  isPressed: () => !!st()?.isPressed,
915
- isFocused: () => focused(),
916
- isFocusVisible: () => focusVisible(),
917
- isHovered: () => hovered(),
1168
+ isFocused: () => c.focused(),
1169
+ isFocusVisible: () => c.focusVisible(),
1170
+ isHovered: () => c.hovered(),
918
1171
  isSelected: () => !!st()?.isSelected,
919
1172
  isDisabled: () => !!st()?.isDisabled,
920
- isFocusVisibleWithinRow: rowFocus.isFocusVisibleWithinRow,
921
- id: key,
1173
+ isFocusVisibleWithinRow: c.within,
1174
+ id: c.key,
922
1175
  columnIndex: () => st()?.columnIndex,
923
1176
  isTreeColumn: () => !!st()?.isTreeColumn,
924
1177
  isExpanded: () => !!st()?.isExpanded,
925
1178
  hasChildItems: () => !!st()?.hasChildItems,
926
1179
  level: () => st()?.level ?? 1,
927
1180
  };
928
- const rp = untrack(() => renderProps({ class: props.class, style: props.style, children: props.children, values, defaultClassName: "rv-Cell" }, hasLive(props) ? liveRenderProps(props) : undefined));
1181
+ const rp = untrack(() => renderProps({ class: c.class, style: c.style, children: c.children, values, defaultClassName: "rv-Cell" }, c.live));
1182
+ const interaction = { states: { isHovered: c.hovered, isFocused: c.focused, isFocusVisible: c.focusVisible } };
1183
+ return { st: st, values, rp, interaction };
1184
+ }
1185
+ /** A cell event with the node for the event (skipped once the cell left the collection). */
1186
+ function cellOn(c, e, fn) {
1187
+ const v = c.view();
1188
+ const n = cellNode(c, v);
1189
+ if (n)
1190
+ fn(e, cellOptions(c, n), v);
1191
+ }
1192
+ /** The cell's own attributes after the user's DOM props (one spread binding). */
1193
+ function cellAttrs(c) {
1194
+ const { st, values, rp } = cellOf(c);
1195
+ return {
1196
+ ...(st()?.gridCellProps ?? { role: "gridcell" }),
1197
+ class: rp.class(),
1198
+ style: rp.style(),
1199
+ "data-focus-visible-within-row": dataBool(c.within()),
1200
+ "data-pressed": dataBool(values.isPressed()),
1201
+ "data-selected": dataBool(values.isSelected()),
1202
+ "data-column-index": values.columnIndex() ?? undefined,
1203
+ "data-expanded": dataBool(values.isExpanded()),
1204
+ "data-has-child-items": dataBool(values.hasChildItems()),
1205
+ "data-level": values.level(),
1206
+ "data-tree-column": dataBool(values.isTreeColumn()),
1207
+ "data-disabled": dataBool(values.isDisabled()),
1208
+ };
1209
+ }
1210
+ /** A cell within a table row. */
1211
+ export const Cell = /* @__PURE__ */ createLeafComponent(TableCellNode, function Cell(props, forwardedRef, cell) {
1212
+ const view = useContext(TableStateContext);
1213
+ const internal = useContext(TableInternalContext);
1214
+ const rowFocus = useContext(RowFocusContext);
1215
+ const live = hasLive(props);
1216
+ const c = {
1217
+ key: cell.key,
1218
+ rowKey: cell.parentKey,
1219
+ view,
1220
+ isVirtualized: internal.isVirtualized,
1221
+ focusMode: props.focusMode,
1222
+ allowsArrowNavigation: props.allowsArrowNavigation,
1223
+ within: rowFocus.isFocusVisibleWithinRow,
1224
+ hovered: signal(false),
1225
+ focused: signal(false),
1226
+ focusVisible: signal(false),
1227
+ class: live ? undefined : props.class,
1228
+ style: live ? undefined : props.style,
1229
+ children: live ? undefined : props.children,
1230
+ live: live ? liveRenderProps(props) : undefined,
1231
+ };
1232
+ initCell(c);
929
1233
  const { onPointerDown, onPointerUp, onPointerEnter, onPointerLeave, onClick } = props;
930
1234
  const dom = globalDOMProps(props, CELL_OWNED);
931
- return (<td {...dom} {...(st()?.gridCellProps ?? { role: "gridcell" })} ref={forwardedRef} class={rp.class()} style={rp.style()} data-focus-visible-within-row={dataBool(rowFocus.isFocusVisibleWithinRow())} data-pressed={dataBool(values.isPressed())} data-selected={dataBool(values.isSelected())} data-column-index={values.columnIndex() ?? undefined} data-expanded={dataBool(values.isExpanded())} data-has-child-items={dataBool(values.hasChildItems())} data-level={values.level()} data-tree-column={dataBool(values.isTreeColumn())} data-disabled={dataBool(values.isDisabled())} onKeyDownCapture={(e) => on(e, gridCellKeyDownCapture)} onKeyDown={(e) => {
932
- on(e, gridCellKeyDown);
1235
+ if (c.isVirtualized) {
1236
+ // Virtualized (RAC useElementType): `div` cell inside its view.
1237
+ return (<div {...dom} {...cellAttrs(c)} ref={forwardedRef} onKeyDownCapture={(e) => cellOn(c, e, gridCellKeyDownCapture)} onKeyDown={(e) => {
1238
+ cellOn(c, e, gridCellKeyDown);
1239
+ }} onKeyUp={(e) => {
1240
+ cellOn(c, e, gridCellKeyUp);
1241
+ }} onFocusIn={(e) => {
1242
+ // The cell's own focus (was a native `focus` listener per cell, before this focusin); its `blur` binds while focused.
1243
+ focusStateIn(cellOf(c).interaction, e);
1244
+ cellOn(c, e, gridCellFocus);
1245
+ }} onPointerOver={(e) => hoverStateOver(cellOf(c).interaction, e)} onPointerEnter={onPointerEnter ? (e) => onPointerEnter(e) : undefined} onPointerLeave={onPointerLeave ? (e) => onPointerLeave(e) : undefined} onPointerDown={(e) => {
1246
+ cellOn(c, e, gridCellPointerDown);
1247
+ onPointerDown?.(e);
1248
+ }} onPointerUp={(e) => {
1249
+ cellOn(c, e, gridCellPointerUp);
1250
+ onPointerUp?.(e);
1251
+ }} onClick={(e) => {
1252
+ cellOn(c, e, gridCellClick);
1253
+ onClick?.(e);
1254
+ }}>
1255
+ {provide(CollectionRendererContext, DefaultCollectionRenderer, () => cellOf(c).rp.children())}
1256
+ </div>);
1257
+ }
1258
+ return (<td {...dom} {...cellAttrs(c)} ref={forwardedRef} onKeyDownCapture={(e) => cellOn(c, e, gridCellKeyDownCapture)} onKeyDown={(e) => {
1259
+ cellOn(c, e, gridCellKeyDown);
933
1260
  }} onKeyUp={(e) => {
934
- on(e, gridCellKeyUp);
1261
+ cellOn(c, e, gridCellKeyUp);
935
1262
  }} onFocusIn={(e) => {
936
1263
  // The cell's own focus (was a native `focus` listener per cell, before this focusin); its `blur` binds while focused.
937
- focusStateIn(interaction, e);
938
- on(e, gridCellFocus);
939
- }} onPointerOver={(e) => hoverStateOver(interaction, e)} onPointerEnter={onPointerEnter ? (e) => onPointerEnter(e) : undefined} onPointerLeave={onPointerLeave ? (e) => onPointerLeave(e) : undefined} onPointerDown={(e) => {
940
- on(e, gridCellPointerDown);
1264
+ focusStateIn(cellOf(c).interaction, e);
1265
+ cellOn(c, e, gridCellFocus);
1266
+ }} onPointerOver={(e) => hoverStateOver(cellOf(c).interaction, e)} onPointerEnter={onPointerEnter ? (e) => onPointerEnter(e) : undefined} onPointerLeave={onPointerLeave ? (e) => onPointerLeave(e) : undefined} onPointerDown={(e) => {
1267
+ cellOn(c, e, gridCellPointerDown);
941
1268
  onPointerDown?.(e);
942
1269
  }} onPointerUp={(e) => {
943
- on(e, gridCellPointerUp);
1270
+ cellOn(c, e, gridCellPointerUp);
944
1271
  onPointerUp?.(e);
945
1272
  }} onClick={(e) => {
946
- on(e, gridCellClick);
1273
+ cellOn(c, e, gridCellClick);
947
1274
  onClick?.(e);
948
1275
  }}>
949
- {provide(CollectionRendererContext, DefaultCollectionRenderer, () => rp.children())}
1276
+ {provide(CollectionRendererContext, DefaultCollectionRenderer, () => cellOf(c).rp.children())}
950
1277
  </td>);
951
1278
  });
952
1279
  /** A sentinel row: calls `onLoadMore` when scrolled near, and renders its children while `isLoading`. */
@@ -962,6 +1289,23 @@ export const TableLoadMoreItem = /* @__PURE__ */ createLeafComponent(LoaderNode,
962
1289
  const className = props.class || "rv-TableLoadingIndicator";
963
1290
  const style = { "--table-row-level": level, ...userStyle };
964
1291
  const dom = globalDOMProps(props, ["style", "id"]);
1292
+ if (internal.isVirtualized) {
1293
+ // Virtualized (RAC useElementType): `div`s inside the loader view.
1294
+ return (<>
1295
+ <div style={{ height: "0" }} inert>
1296
+ <div style={{ padding: "0", border: "0" }}>
1297
+ <div data-testid="loadMoreSentinel" ref={sentinel} style={{ position: "relative", height: "1px", width: "1px" }}/>
1298
+ </div>
1299
+ </div>
1300
+ <Show when={() => !!read(isLoading) && children != null}>
1301
+ {() => (<div {...dom} ref={ref} role="row" class={className} style={style} aria-level={level} data-level={level}>
1302
+ <div role="rowheader" aria-colspan={numColumns()}>
1303
+ {children}
1304
+ </div>
1305
+ </div>)}
1306
+ </Show>
1307
+ </>);
1308
+ }
965
1309
  return (<>
966
1310
  {/* Always render the sentinel. For now onus is on the user for styling when using flex + gap. */}
967
1311
  <tr style={{ height: "0" }} inert>