@proteos/ui 0.46.0 → 0.47.0
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/dist/index.cjs +115 -10
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +18 -2
- package/dist/index.d.ts +18 -2
- package/dist/index.js +115 -11
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/components/checkbox/checkbox.tsx +8 -1
- package/src/components/data-table/data-table.test.tsx +112 -0
- package/src/components/data-table/data-table.tsx +135 -11
- package/src/components/data-table/index.ts +1 -1
- package/src/test/setup.ts +30 -0
package/dist/index.cjs
CHANGED
|
@@ -1265,7 +1265,14 @@ function Checkbox({ children, className, ...props }) {
|
|
|
1265
1265
|
{
|
|
1266
1266
|
...props,
|
|
1267
1267
|
className: (renderProps) => chunkMWAXK66H_cjs.cn(
|
|
1268
|
-
|
|
1268
|
+
// `relative` so RAC's visually-hidden <input> — which is absolutely
|
|
1269
|
+
// positioned and is the element that actually receives focus — is
|
|
1270
|
+
// laid out against THIS checkbox. Without a positioned ancestor here
|
|
1271
|
+
// it resolves against a far-away container, ends up outside any
|
|
1272
|
+
// scroll clipping, and focusing it makes the browser scroll a
|
|
1273
|
+
// container the user cannot scroll back (the roles permission
|
|
1274
|
+
// matrix, where toggling a row threw the page off-screen).
|
|
1275
|
+
"group relative inline-flex items-center gap-2.5 cursor-pointer",
|
|
1269
1276
|
"font-sans text-[14px] text-ink-2 select-none",
|
|
1270
1277
|
"data-[disabled]:opacity-45 data-[disabled]:cursor-not-allowed",
|
|
1271
1278
|
typeof className === "function" ? className(renderProps) : className
|
|
@@ -2614,6 +2621,7 @@ function useInfiniteScrollSentinel({
|
|
|
2614
2621
|
return ref;
|
|
2615
2622
|
}
|
|
2616
2623
|
var DEFAULT_ROW_HEIGHT = 32;
|
|
2624
|
+
var SELECTION_COLUMN_ID = "__select__";
|
|
2617
2625
|
function DataTable({
|
|
2618
2626
|
query,
|
|
2619
2627
|
columns,
|
|
@@ -2638,6 +2646,9 @@ function DataTable({
|
|
|
2638
2646
|
extraRows,
|
|
2639
2647
|
bodyBefore,
|
|
2640
2648
|
bodyAfter,
|
|
2649
|
+
selectionMode = "none",
|
|
2650
|
+
selectedIds,
|
|
2651
|
+
onSelectionChange,
|
|
2641
2652
|
columnBorders = false
|
|
2642
2653
|
}) {
|
|
2643
2654
|
const rows = react.useMemo(() => {
|
|
@@ -2648,19 +2659,112 @@ function DataTable({
|
|
|
2648
2659
|
if (startExtras.length === 0 && endExtras.length === 0) return dataRows;
|
|
2649
2660
|
return [...startExtras, ...dataRows, ...endExtras];
|
|
2650
2661
|
}, [query.data, extraRows]);
|
|
2662
|
+
const resolveRowId = react.useMemo(
|
|
2663
|
+
() => getRowId ?? ((row, index) => {
|
|
2664
|
+
const r = row;
|
|
2665
|
+
if (typeof r.id === "string" || typeof r.id === "number") return String(r.id);
|
|
2666
|
+
if (typeof r.slug === "string") return r.slug;
|
|
2667
|
+
return String(index);
|
|
2668
|
+
}),
|
|
2669
|
+
[getRowId]
|
|
2670
|
+
);
|
|
2671
|
+
const selectionEnabled = selectionMode === "multiple";
|
|
2672
|
+
const selected = react.useMemo(() => selectedIds ?? /* @__PURE__ */ new Set(), [selectedIds]);
|
|
2673
|
+
const loadedIds = react.useMemo(
|
|
2674
|
+
() => selectionEnabled ? rows.map((row, index) => resolveRowId(row, index)) : [],
|
|
2675
|
+
[selectionEnabled, rows, resolveRowId]
|
|
2676
|
+
);
|
|
2677
|
+
const lastToggledIndexRef = react.useRef(null);
|
|
2678
|
+
const toggleRow = (id, index, isRange) => {
|
|
2679
|
+
if (!onSelectionChange) return;
|
|
2680
|
+
const next = new Set(selected);
|
|
2681
|
+
const anchor = lastToggledIndexRef.current;
|
|
2682
|
+
if (isRange && anchor !== null) {
|
|
2683
|
+
const [from, to] = anchor <= index ? [anchor, index] : [index, anchor];
|
|
2684
|
+
for (let i = from; i <= to; i += 1) {
|
|
2685
|
+
const rangeId = loadedIds[i];
|
|
2686
|
+
if (rangeId !== void 0) next.add(rangeId);
|
|
2687
|
+
}
|
|
2688
|
+
} else if (next.has(id)) {
|
|
2689
|
+
next.delete(id);
|
|
2690
|
+
} else {
|
|
2691
|
+
next.add(id);
|
|
2692
|
+
}
|
|
2693
|
+
lastToggledIndexRef.current = index;
|
|
2694
|
+
onSelectionChange(next);
|
|
2695
|
+
};
|
|
2696
|
+
const loadedSelectedCount = loadedIds.reduce((count, id) => count + (selected.has(id) ? 1 : 0), 0);
|
|
2697
|
+
const allLoadedSelected = loadedIds.length > 0 && loadedSelectedCount === loadedIds.length;
|
|
2698
|
+
const toggleAllLoaded = () => {
|
|
2699
|
+
if (!onSelectionChange) return;
|
|
2700
|
+
const next = new Set(selected);
|
|
2701
|
+
if (allLoadedSelected) {
|
|
2702
|
+
for (const id of loadedIds) next.delete(id);
|
|
2703
|
+
} else {
|
|
2704
|
+
for (const id of loadedIds) next.add(id);
|
|
2705
|
+
}
|
|
2706
|
+
lastToggledIndexRef.current = null;
|
|
2707
|
+
onSelectionChange(next);
|
|
2708
|
+
};
|
|
2709
|
+
const rangeModifierRef = react.useRef(false);
|
|
2710
|
+
const selectionColumn = react.useMemo(
|
|
2711
|
+
() => ({
|
|
2712
|
+
id: SELECTION_COLUMN_ID,
|
|
2713
|
+
size: 36,
|
|
2714
|
+
enableSorting: false,
|
|
2715
|
+
meta: { align: "center", sticky: "left", cellClassName: "px-0 justify-center" },
|
|
2716
|
+
header: () => /* @__PURE__ */ jsxRuntime.jsx("span", { className: "inline-flex items-center justify-center w-full", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
2717
|
+
Checkbox,
|
|
2718
|
+
{
|
|
2719
|
+
"aria-label": allLoadedSelected ? "Deselect all rows" : "Select all rows",
|
|
2720
|
+
isSelected: allLoadedSelected,
|
|
2721
|
+
isIndeterminate: loadedSelectedCount > 0 && !allLoadedSelected,
|
|
2722
|
+
isDisabled: loadedIds.length === 0,
|
|
2723
|
+
onChange: toggleAllLoaded
|
|
2724
|
+
}
|
|
2725
|
+
) }),
|
|
2726
|
+
cell: ({ row }) => (
|
|
2727
|
+
// Stop the click here: selecting a row must never also open it.
|
|
2728
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2729
|
+
"span",
|
|
2730
|
+
{
|
|
2731
|
+
className: "inline-flex items-center justify-center w-full",
|
|
2732
|
+
onClick: (event) => event.stopPropagation(),
|
|
2733
|
+
onKeyDown: (event) => {
|
|
2734
|
+
event.stopPropagation();
|
|
2735
|
+
rangeModifierRef.current = event.shiftKey;
|
|
2736
|
+
},
|
|
2737
|
+
onPointerDownCapture: (event) => {
|
|
2738
|
+
rangeModifierRef.current = event.shiftKey;
|
|
2739
|
+
},
|
|
2740
|
+
role: "presentation",
|
|
2741
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
2742
|
+
Checkbox,
|
|
2743
|
+
{
|
|
2744
|
+
"aria-label": `Select row ${row.index + 1}`,
|
|
2745
|
+
isSelected: selected.has(row.id),
|
|
2746
|
+
onChange: () => toggleRow(row.id, row.index, rangeModifierRef.current)
|
|
2747
|
+
}
|
|
2748
|
+
)
|
|
2749
|
+
}
|
|
2750
|
+
)
|
|
2751
|
+
)
|
|
2752
|
+
}),
|
|
2753
|
+
// biome-ignore lint/correctness/useExhaustiveDependencies: handlers read the latest selection via closure recreated on these inputs
|
|
2754
|
+
[selected, loadedIds, allLoadedSelected, loadedSelectedCount]
|
|
2755
|
+
);
|
|
2756
|
+
const tableColumns = react.useMemo(
|
|
2757
|
+
() => selectionEnabled ? [selectionColumn, ...columns] : columns,
|
|
2758
|
+
[selectionEnabled, selectionColumn, columns]
|
|
2759
|
+
);
|
|
2651
2760
|
const table = reactTable.useReactTable({
|
|
2652
2761
|
data: rows,
|
|
2653
|
-
columns,
|
|
2762
|
+
columns: tableColumns,
|
|
2654
2763
|
state: { sorting: sorting ?? [] },
|
|
2655
2764
|
...onSortingChange ? { onSortingChange } : {},
|
|
2656
2765
|
manualSorting: true,
|
|
2657
2766
|
getCoreRowModel: reactTable.getCoreRowModel(),
|
|
2658
|
-
getRowId:
|
|
2659
|
-
const r = row;
|
|
2660
|
-
if (typeof r.id === "string" || typeof r.id === "number") return String(r.id);
|
|
2661
|
-
if (typeof r.slug === "string") return r.slug;
|
|
2662
|
-
return String(index);
|
|
2663
|
-
}
|
|
2767
|
+
getRowId: (row, index) => resolveRowId(row, index)
|
|
2664
2768
|
});
|
|
2665
2769
|
const tableRows = table.getRowModel().rows;
|
|
2666
2770
|
const tableRowCount = tableRows.length;
|
|
@@ -2692,8 +2796,8 @@ function DataTable({
|
|
|
2692
2796
|
enabled: !!query.hasNextPage
|
|
2693
2797
|
});
|
|
2694
2798
|
const totalColumnWidth = react.useMemo(
|
|
2695
|
-
() =>
|
|
2696
|
-
[
|
|
2799
|
+
() => tableColumns.reduce((sum, c) => sum + (c.size ?? 140), 0),
|
|
2800
|
+
[tableColumns]
|
|
2697
2801
|
);
|
|
2698
2802
|
const columnsRef = react.useRef(columns);
|
|
2699
2803
|
columnsRef.current = columns;
|
|
@@ -7714,6 +7818,7 @@ exports.RecordFilterChips = RecordFilterChips;
|
|
|
7714
7818
|
exports.RecordFilterEditor = RecordFilterEditor;
|
|
7715
7819
|
exports.RenderChipItemProvider = RenderChipItemProvider;
|
|
7716
7820
|
exports.RenderValueInputProvider = RenderValueInputProvider;
|
|
7821
|
+
exports.SELECTION_COLUMN_ID = SELECTION_COLUMN_ID;
|
|
7717
7822
|
exports.SegmentedControl = SegmentedControl;
|
|
7718
7823
|
exports.Select = Select;
|
|
7719
7824
|
exports.Separator = Separator;
|