@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.
- package/_adapters/selection.d.ts +13 -2
- package/_adapters/selection.js +39 -15
- package/button/index.jsx +5 -3
- package/collections/components.d.ts +36 -1
- package/collections/components.jsx +224 -16
- package/collections/nodes.d.ts +9 -1
- package/collections/nodes.js +26 -0
- package/collections/row-stamps.d.ts +17 -0
- package/collections/row-stamps.js +108 -0
- package/collections/server-dev.d.ts +1 -0
- package/collections/server-dev.js +2 -0
- package/collections/server-off.d.ts +1 -0
- package/collections/server-off.js +2 -0
- package/collections/server-on.d.ts +1 -0
- package/collections/server-on.jsx +10 -0
- package/drag-drop/directives/drag.d.ts +16 -0
- package/drag-drop/directives/drag.js +28 -0
- package/drag-drop/drag.jsx +6 -4
- package/drag-drop/lazy-refs.js +15 -4
- package/grid-list/behaviour.d.ts +6 -0
- package/grid-list/behaviour.js +25 -7
- package/grid-list/index.d.ts +3 -1
- package/grid-list/index.jsx +60 -24
- package/interactions/press.jsx +2 -1
- package/interactions/utils.d.ts +2 -3
- package/interactions/utils.js +8 -7
- package/listbox/behaviour.jsx +37 -14
- package/listbox/index.jsx +65 -29
- package/menu/index.jsx +38 -7
- package/package.json +4 -4
- package/portal/index.d.ts +8 -0
- package/portal/index.jsx +8 -1
- package/radio-group/index.jsx +4 -1
- package/selection/keyed.js +6 -16
- package/selection/selectable-collection.js +42 -17
- package/sidebar/index.jsx +5 -4
- package/state/menu.d.ts +3 -0
- package/state/menu.js +1 -0
- package/table/index.jsx +512 -168
- package/tree/index.jsx +26 -4
- package/utils/data-attrs.d.ts +4 -2
- package/utils/data-attrs.jsx +27 -3
- package/utils/index.d.ts +1 -15
- package/utils/index.js +5 -102
- package/utils/link-state.d.ts +6 -0
- package/utils/link-state.js +6 -0
- package/utils/links.d.ts +1 -6
- package/utils/links.jsx +2 -4
- package/utils/transitions.d.ts +15 -0
- package/utils/transitions.js +104 -0
- package/virtualizer/behaviour.d.ts +35 -4
- package/virtualizer/behaviour.js +131 -9
- package/virtualizer/collection-renderer.d.ts +62 -0
- package/virtualizer/collection-renderer.jsx +190 -0
- package/virtualizer/engine.d.ts +1 -1
- package/virtualizer/index.d.ts +1 -0
- package/virtualizer/index.jsx +1 -0
- package/virtualizer/list-layout.js +4 -2
- package/virtualizer/state.js +12 -2
- package/virtualizer/virtualizer.d.ts +8 -6
- 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
|
-
// -
|
|
29
|
-
// (
|
|
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
|
-
|
|
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
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
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
|
-
|
|
707
|
-
|
|
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(
|
|
714
|
-
const hasChildItems = !!
|
|
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
|
-
|
|
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: () =>
|
|
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
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
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
|
-
|
|
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
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
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
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
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,
|
|
804
|
-
|
|
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
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
}} onFocusOut={(e) =>
|
|
816
|
-
|
|
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
|
-
|
|
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
|
-
|
|
827
|
-
gridRowPointerCancel(e, ropts(v), v);
|
|
828
|
-
syncPressed(e.currentTarget);
|
|
1089
|
+
rowOn(r, e, gridRowPointerCancel, true);
|
|
829
1090
|
onPointerCancel?.(e);
|
|
830
|
-
}} onClick={(e) => {
|
|
831
|
-
|
|
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,
|
|
844
|
-
[CheckboxFieldContext,
|
|
845
|
-
[ButtonContext,
|
|
1096
|
+
[CheckboxContext, checkboxSlots],
|
|
1097
|
+
[CheckboxFieldContext, checkboxSlots],
|
|
1098
|
+
[ButtonContext, buttons],
|
|
846
1099
|
[SelectionIndicatorContext, { isSelected: values.isSelected }],
|
|
847
1100
|
[RowFocusContext, { isFocusVisibleWithinRow: values.isFocusVisibleWithin }],
|
|
848
1101
|
]}>
|
|
849
|
-
{
|
|
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
|
-
|
|
861
|
-
|
|
862
|
-
const
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
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:
|
|
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 =
|
|
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(
|
|
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:
|
|
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:
|
|
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
|
-
|
|
932
|
-
|
|
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
|
-
|
|
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
|
-
|
|
939
|
-
}} onPointerOver={(e) => hoverStateOver(interaction, e)} onPointerEnter={onPointerEnter ? (e) => onPointerEnter(e) : undefined} onPointerLeave={onPointerLeave ? (e) => onPointerLeave(e) : undefined} onPointerDown={(e) => {
|
|
940
|
-
|
|
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
|
-
|
|
1270
|
+
cellOn(c, e, gridCellPointerUp);
|
|
944
1271
|
onPointerUp?.(e);
|
|
945
1272
|
}} onClick={(e) => {
|
|
946
|
-
|
|
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>
|