@stackframe/dashboard-ui-components 2.8.86 → 2.8.89
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/components/analytics-chart/analytics-chart-pie.js +3 -3
- package/dist/components/analytics-chart/analytics-chart-pie.js.map +1 -1
- package/dist/components/data-grid/data-grid-sizing.d.ts +2 -1
- package/dist/components/data-grid/data-grid-sizing.d.ts.map +1 -1
- package/dist/components/data-grid/data-grid-sizing.js +33 -4
- package/dist/components/data-grid/data-grid-sizing.js.map +1 -1
- package/dist/components/data-grid/data-grid-toolbar.js +18 -15
- package/dist/components/data-grid/data-grid-toolbar.js.map +1 -1
- package/dist/components/data-grid/data-grid.d.ts +35 -1
- package/dist/components/data-grid/data-grid.d.ts.map +1 -1
- package/dist/components/data-grid/data-grid.js +329 -127
- package/dist/components/data-grid/data-grid.js.map +1 -1
- package/dist/components/data-grid/data-grid.test.d.ts +1 -0
- package/dist/components/data-grid/data-grid.test.js +215 -0
- package/dist/components/data-grid/data-grid.test.js.map +1 -0
- package/dist/components/data-grid/index.d.ts +3 -2
- package/dist/components/data-grid/index.js +13 -0
- package/dist/components/data-grid/state.d.ts.map +1 -1
- package/dist/components/data-grid/state.js +24 -7
- package/dist/components/data-grid/state.js.map +1 -1
- package/dist/components/data-grid/types.d.ts +34 -3
- package/dist/components/data-grid/types.d.ts.map +1 -1
- package/dist/components/data-grid/use-data-source.d.ts +6 -0
- package/dist/components/data-grid/use-data-source.d.ts.map +1 -1
- package/dist/components/data-grid/use-data-source.js +10 -2
- package/dist/components/data-grid/use-data-source.js.map +1 -1
- package/dist/components/tabs.d.ts +5 -1
- package/dist/components/tabs.d.ts.map +1 -1
- package/dist/components/tabs.js +40 -27
- package/dist/components/tabs.js.map +1 -1
- package/dist/dashboard-ui-components.global.js +672 -368
- package/dist/dashboard-ui-components.global.js.map +4 -4
- package/dist/esm/components/analytics-chart/analytics-chart-pie.js +3 -3
- package/dist/esm/components/analytics-chart/analytics-chart-pie.js.map +1 -1
- package/dist/esm/components/data-grid/data-grid-sizing.d.ts +2 -1
- package/dist/esm/components/data-grid/data-grid-sizing.d.ts.map +1 -1
- package/dist/esm/components/data-grid/data-grid-sizing.js +33 -5
- package/dist/esm/components/data-grid/data-grid-sizing.js.map +1 -1
- package/dist/esm/components/data-grid/data-grid-toolbar.js +18 -15
- package/dist/esm/components/data-grid/data-grid-toolbar.js.map +1 -1
- package/dist/esm/components/data-grid/data-grid.d.ts +35 -1
- package/dist/esm/components/data-grid/data-grid.d.ts.map +1 -1
- package/dist/esm/components/data-grid/data-grid.js +329 -128
- package/dist/esm/components/data-grid/data-grid.js.map +1 -1
- package/dist/esm/components/data-grid/data-grid.test.d.ts +1 -0
- package/dist/esm/components/data-grid/data-grid.test.js +215 -0
- package/dist/esm/components/data-grid/data-grid.test.js.map +1 -0
- package/dist/esm/components/data-grid/index.d.ts +3 -2
- package/dist/esm/components/data-grid/index.js +3 -2
- package/dist/esm/components/data-grid/state.d.ts.map +1 -1
- package/dist/esm/components/data-grid/state.js +24 -7
- package/dist/esm/components/data-grid/state.js.map +1 -1
- package/dist/esm/components/data-grid/types.d.ts +34 -3
- package/dist/esm/components/data-grid/types.d.ts.map +1 -1
- package/dist/esm/components/data-grid/use-data-source.d.ts +6 -0
- package/dist/esm/components/data-grid/use-data-source.d.ts.map +1 -1
- package/dist/esm/components/data-grid/use-data-source.js +10 -2
- package/dist/esm/components/data-grid/use-data-source.js.map +1 -1
- package/dist/esm/components/tabs.d.ts +5 -1
- package/dist/esm/components/tabs.d.ts.map +1 -1
- package/dist/esm/components/tabs.js +40 -27
- package/dist/esm/components/tabs.js.map +1 -1
- package/dist/index.d.ts +3 -2
- package/package.json +4 -4
|
@@ -9,10 +9,11 @@ let react = require("react");
|
|
|
9
9
|
react = require_chunk.__toESM(react);
|
|
10
10
|
let __state_js = require("./state.js");
|
|
11
11
|
let __strings_js = require("./strings.js");
|
|
12
|
+
let _stackframe_stack_shared_dist_utils_errors = require("@stackframe/stack-shared/dist/utils/errors");
|
|
12
13
|
let _tanstack_react_virtual = require("@tanstack/react-virtual");
|
|
13
14
|
let ___skeleton_js = require("../skeleton.js");
|
|
14
|
-
let __data_grid_toolbar_js = require("./data-grid-toolbar.js");
|
|
15
15
|
let __data_grid_sizing_js = require("./data-grid-sizing.js");
|
|
16
|
+
let __data_grid_toolbar_js = require("./data-grid-toolbar.js");
|
|
16
17
|
|
|
17
18
|
//#region src/components/data-grid/data-grid.tsx
|
|
18
19
|
function ResizeHandle({ onResize, onResizeEnd }) {
|
|
@@ -72,6 +73,37 @@ function ResizeHandle({ onResize, onResizeEnd }) {
|
|
|
72
73
|
onPointerDown
|
|
73
74
|
});
|
|
74
75
|
}
|
|
76
|
+
function getNearestVerticalScrollElement(element) {
|
|
77
|
+
let current = element?.parentElement ?? null;
|
|
78
|
+
while (current) {
|
|
79
|
+
const style = window.getComputedStyle(current);
|
|
80
|
+
const overflowY = style.overflowY === "visible" ? style.overflow : style.overflowY;
|
|
81
|
+
if ((overflowY === "auto" || overflowY === "scroll" || overflowY === "overlay") && current.scrollHeight > current.clientHeight + 1) return current;
|
|
82
|
+
current = current.parentElement;
|
|
83
|
+
}
|
|
84
|
+
return window;
|
|
85
|
+
}
|
|
86
|
+
function getEventTargetElement(target) {
|
|
87
|
+
if (target instanceof Element) return target;
|
|
88
|
+
if (target instanceof Node) return target.parentElement;
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
function isDataGridInteractiveRowClickTarget(target) {
|
|
92
|
+
return getEventTargetElement(target)?.closest([
|
|
93
|
+
"a",
|
|
94
|
+
"button",
|
|
95
|
+
"input",
|
|
96
|
+
"select",
|
|
97
|
+
"textarea",
|
|
98
|
+
"[role=\"button\"]",
|
|
99
|
+
"[role=\"menuitem\"]",
|
|
100
|
+
"[contenteditable]:not([contenteditable=\"false\"])",
|
|
101
|
+
"[data-no-row-click]"
|
|
102
|
+
].join(",")) != null;
|
|
103
|
+
}
|
|
104
|
+
function shouldIgnoreRowClick(event) {
|
|
105
|
+
return event.defaultPrevented || isDataGridInteractiveRowClickTarget(event.target);
|
|
106
|
+
}
|
|
75
107
|
function HeaderCell({ col, isSorted, sortIndex, resizable, onSort, onResize, onResizeEnd }) {
|
|
76
108
|
const ctx = {
|
|
77
109
|
columnId: col.id,
|
|
@@ -82,7 +114,7 @@ function HeaderCell({ col, isSorted, sortIndex, resizable, onSort, onResize, onR
|
|
|
82
114
|
const label = typeof col.header === "function" ? col.header(ctx) : col.header;
|
|
83
115
|
const sortable = col.sortable !== false;
|
|
84
116
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
85
|
-
className: (0, _stackframe_stack_ui.cn)("group/header relative flex items-center gap-1.5 px-3 select-none bg-transparent", "border-r border-black/[0.04] dark:border-white/[0.04] last:border-r-0", sortable && "cursor-pointer"),
|
|
117
|
+
className: (0, _stackframe_stack_ui.cn)("group/header relative flex items-center gap-1.5 px-3 select-none bg-transparent overflow-hidden", "border-r border-black/[0.04] dark:border-white/[0.04] last:border-r-0", sortable && "cursor-pointer"),
|
|
86
118
|
style: (0, __data_grid_sizing_js.getColumnSizingStyle)(col),
|
|
87
119
|
"data-col-id": col.id,
|
|
88
120
|
onClick: (e) => sortable && onSort(col.id, e.metaKey || e.ctrlKey),
|
|
@@ -90,7 +122,7 @@ function HeaderCell({ col, isSorted, sortIndex, resizable, onSort, onResize, onR
|
|
|
90
122
|
"aria-sort": isSorted === "asc" ? "ascending" : isSorted === "desc" ? "descending" : "none",
|
|
91
123
|
children: [
|
|
92
124
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
93
|
-
className: (0, _stackframe_stack_ui.cn)("flex-1 truncate text-xs font-semibold uppercase tracking-wider text-muted-foreground", col.align === "center" && "text-center", col.align === "right" && "text-right"),
|
|
125
|
+
className: (0, _stackframe_stack_ui.cn)("flex-1 min-w-0 truncate text-xs font-semibold uppercase tracking-wider text-muted-foreground", col.align === "center" && "text-center", col.align === "right" && "text-right"),
|
|
94
126
|
children: label
|
|
95
127
|
}),
|
|
96
128
|
isSorted && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
@@ -140,8 +172,9 @@ function DataCell({ col, row, rowId, rowIndex, isSelected, dateDisplay }) {
|
|
|
140
172
|
else if (isDateCol) content = renderDateCell(value, dateDisplay, col);
|
|
141
173
|
else content = formatCellValue(value);
|
|
142
174
|
const hasCellClick = col.onCellClick || col.onCellDoubleClick;
|
|
175
|
+
const isWrap = col.cellOverflow === "wrap";
|
|
143
176
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
144
|
-
className: (0, _stackframe_stack_ui.cn)("flex
|
|
177
|
+
className: (0, _stackframe_stack_ui.cn)("flex px-3 bg-transparent overflow-hidden", "border-r border-black/[0.04] dark:border-white/[0.04] last:border-r-0", "text-sm text-foreground", isWrap ? "items-start py-2" : "items-center", col.align === "center" && "justify-center", col.align === "right" && "justify-end", hasCellClick && "cursor-pointer"),
|
|
145
178
|
style: (0, __data_grid_sizing_js.getColumnSizingStyle)(col),
|
|
146
179
|
"data-col-id": col.id,
|
|
147
180
|
role: "gridcell",
|
|
@@ -153,7 +186,10 @@ function DataCell({ col, row, rowId, rowIndex, isSelected, dateDisplay }) {
|
|
|
153
186
|
e.stopPropagation();
|
|
154
187
|
col.onCellDoubleClick(ctx, e);
|
|
155
188
|
} : void 0,
|
|
156
|
-
children:
|
|
189
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
190
|
+
className: (0, _stackframe_stack_ui.cn)("min-w-0", isWrap ? "flex-1" : "truncate"),
|
|
191
|
+
children: content
|
|
192
|
+
})
|
|
157
193
|
});
|
|
158
194
|
}
|
|
159
195
|
function formatCellValue(value) {
|
|
@@ -193,6 +229,11 @@ function renderDateCell(value, dateDisplay, col) {
|
|
|
193
229
|
children: display
|
|
194
230
|
});
|
|
195
231
|
}
|
|
232
|
+
function hashStringToInt(value) {
|
|
233
|
+
let hash = 0;
|
|
234
|
+
for (let i = 0; i < value.length; i++) hash = (hash << 5) - hash + value.charCodeAt(i) | 0;
|
|
235
|
+
return Math.abs(hash);
|
|
236
|
+
}
|
|
196
237
|
function SkeletonRow({ columns, height, showCheckbox }) {
|
|
197
238
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
198
239
|
className: "flex",
|
|
@@ -207,7 +248,7 @@ function SkeletonRow({ columns, height, showCheckbox }) {
|
|
|
207
248
|
style: (0, __data_grid_sizing_js.getColumnSizingStyle)(col),
|
|
208
249
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(___skeleton_js.DesignSkeleton, {
|
|
209
250
|
className: "h-3.5 rounded-md",
|
|
210
|
-
style: { width: `${40 +
|
|
251
|
+
style: { width: `${40 + hashStringToInt(col.id) % 40}%` }
|
|
211
252
|
})
|
|
212
253
|
}, col.id))]
|
|
213
254
|
});
|
|
@@ -229,17 +270,21 @@ function SelectionCheckbox({ checked, indeterminate, onChange, ariaLabel }) {
|
|
|
229
270
|
})
|
|
230
271
|
});
|
|
231
272
|
}
|
|
232
|
-
|
|
273
|
+
const NOOP = () => {};
|
|
274
|
+
function InfiniteScrollSentinel({ onIntersect, isLoading, rootRef, strings }) {
|
|
233
275
|
const ref = (0, react.useRef)(null);
|
|
234
276
|
(0, react.useEffect)(() => {
|
|
235
277
|
const el = ref.current;
|
|
236
278
|
if (!el) return;
|
|
237
279
|
const observer = new IntersectionObserver((entries) => {
|
|
238
280
|
if (entries[0]?.isIntersecting) onIntersect();
|
|
239
|
-
}, {
|
|
281
|
+
}, {
|
|
282
|
+
root: rootRef?.current ?? null,
|
|
283
|
+
rootMargin: "200px"
|
|
284
|
+
});
|
|
240
285
|
observer.observe(el);
|
|
241
286
|
return () => observer.disconnect();
|
|
242
|
-
}, [onIntersect]);
|
|
287
|
+
}, [onIntersect, rootRef]);
|
|
243
288
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
244
289
|
ref,
|
|
245
290
|
className: "flex items-center justify-center py-4",
|
|
@@ -516,6 +561,39 @@ function DefaultFooter({ ctx, pagination, onChange }) {
|
|
|
516
561
|
* and footer all call it for you. You do not need to wire any of this
|
|
517
562
|
* manually.
|
|
518
563
|
*
|
|
564
|
+
* ## Cell overflow and dynamic row heights
|
|
565
|
+
*
|
|
566
|
+
* By default every cell truncates its content with an ellipsis
|
|
567
|
+
* (`cellOverflow: "truncate"`). For columns whose content should wrap
|
|
568
|
+
* — badge lists, multi-line text, permission chips — set
|
|
569
|
+
* `cellOverflow: "wrap"` on the column definition.
|
|
570
|
+
*
|
|
571
|
+
* To let rows grow to fit their tallest cell, set `rowHeight="auto"`
|
|
572
|
+
* on the grid. The virtualizer will measure each row after render and
|
|
573
|
+
* adjust scroll positions accordingly. Pair with `estimatedRowHeight`
|
|
574
|
+
* (default 44) for better scroll-position estimates before measurement.
|
|
575
|
+
*
|
|
576
|
+
* ```tsx
|
|
577
|
+
* // Columns: UUIDs truncate, auth-method badges wrap
|
|
578
|
+
* const columns = [
|
|
579
|
+
* { id: "userId", header: "User ID", width: 130 }, // default truncate
|
|
580
|
+
* { id: "auth", header: "Auth methods", width: 150, cellOverflow: "wrap",
|
|
581
|
+
* renderCell: ({ row }) => (
|
|
582
|
+
* <div className="flex flex-wrap gap-1">
|
|
583
|
+
* {row.authTypes.map((t) => <Badge key={t}>{t}</Badge>)}
|
|
584
|
+
* </div>
|
|
585
|
+
* ),
|
|
586
|
+
* },
|
|
587
|
+
* ];
|
|
588
|
+
*
|
|
589
|
+
* <DataGrid columns={columns} rowHeight="auto" estimatedRowHeight={48} ... />
|
|
590
|
+
* ```
|
|
591
|
+
*
|
|
592
|
+
* With a fixed numeric `rowHeight` (the default), `cellOverflow: "wrap"`
|
|
593
|
+
* still lets content wrap within the row, but anything exceeding the
|
|
594
|
+
* fixed height is clipped. This is useful when you want controlled
|
|
595
|
+
* wrapping without variable row heights.
|
|
596
|
+
*
|
|
519
597
|
* ## Height and scrolling
|
|
520
598
|
*
|
|
521
599
|
* DataGrid is NOT a card. It has no border, rounded corners, or shadow of
|
|
@@ -556,7 +634,10 @@ function DefaultFooter({ ctx, pagination, onChange }) {
|
|
|
556
634
|
* toggle for `date` / `dateTime` columns.
|
|
557
635
|
*/
|
|
558
636
|
function DataGrid(props) {
|
|
559
|
-
const { columns: allColumns, rows, getRowId, totalRowCount, isLoading = false, isRefetching = false, hasMore = false, isLoadingMore = false, onLoadMore, state, onChange, paginationMode = "paginated", selectionMode = "none", resizable = true, rowHeight = 44, headerHeight = 44, overscan = 5, maxHeight, toolbar, toolbarExtra, emptyState, loadingState, footer, footerExtra, exportFilename = "export", strings: stringsOverride, className, onRowClick, onRowDoubleClick, onSelectionChange, onSortChange } = props;
|
|
637
|
+
const { columns: allColumns, rows, getRowId, totalRowCount, isLoading = false, isRefetching = false, hasMore = false, isLoadingMore = false, onLoadMore, state, onChange, paginationMode = "paginated", selectionMode = "none", resizable = true, rowHeight: rowHeightProp = 44, estimatedRowHeight: estimatedRowHeightProp, headerHeight = 44, overscan = 5, maxHeight, fillHeight = true, stickyTop, toolbar, toolbarExtra, emptyState, loadingState, footer, footerExtra, exportFilename = "export", strings: stringsOverride, className, onRowClick, onRowDoubleClick, onSelectionChange, onSortChange } = props;
|
|
638
|
+
const isDynamicRowHeight = rowHeightProp === "auto";
|
|
639
|
+
const fixedRowHeight = isDynamicRowHeight ? void 0 : rowHeightProp;
|
|
640
|
+
const estimatedRowHeight = estimatedRowHeightProp ?? fixedRowHeight ?? 44;
|
|
560
641
|
const strings = (0, react.useMemo)(() => (0, __strings_js.resolveDataGridStrings)(stringsOverride), [stringsOverride]);
|
|
561
642
|
const visibleColumns = (0, react.useMemo)(() => (state.columnOrder.length > 0 ? state.columnOrder.map((id) => allColumns.find((c) => c.id === id)).filter(Boolean) : allColumns).filter((col) => (0, __state_js.isColumnVisible)(col.id, state.columnVisibility)), [
|
|
562
643
|
allColumns,
|
|
@@ -585,15 +666,17 @@ function DataGrid(props) {
|
|
|
585
666
|
const resizeRef = (0, react.useRef)(null);
|
|
586
667
|
const gridRef = (0, react.useRef)(null);
|
|
587
668
|
const handleSort = (0, react.useCallback)((columnId, multi) => {
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
669
|
+
const next = (0, __state_js.toggleSort)(state.sorting, columnId, multi);
|
|
670
|
+
onChange((s) => ({
|
|
671
|
+
...s,
|
|
672
|
+
sorting: next
|
|
673
|
+
}));
|
|
674
|
+
onSortChange?.(next);
|
|
675
|
+
}, [
|
|
676
|
+
onChange,
|
|
677
|
+
onSortChange,
|
|
678
|
+
state.sorting
|
|
679
|
+
]);
|
|
597
680
|
const handleResize = (0, react.useCallback)((columnId, delta) => {
|
|
598
681
|
const col = allColumns.find((c) => c.id === columnId);
|
|
599
682
|
if (!col) return;
|
|
@@ -631,17 +714,17 @@ function DataGrid(props) {
|
|
|
631
714
|
}));
|
|
632
715
|
}, [onChange]);
|
|
633
716
|
const handleRowClick = (0, react.useCallback)((row, rowId, event) => {
|
|
634
|
-
if (selectionMode !== "none")
|
|
635
|
-
const next = (0, __state_js.toggleRowSelection)(
|
|
717
|
+
if (selectionMode !== "none") {
|
|
718
|
+
const next = (0, __state_js.toggleRowSelection)(state.selection, rowId, selectionMode, event.shiftKey, event.metaKey || event.ctrlKey, rowIds);
|
|
719
|
+
onChange((s) => ({
|
|
720
|
+
...s,
|
|
721
|
+
selection: next
|
|
722
|
+
}));
|
|
636
723
|
if (onSelectionChange) {
|
|
637
724
|
const selectedRows = rows.filter((r) => next.selectedIds.has(getRowId(r)));
|
|
638
|
-
|
|
725
|
+
onSelectionChange(next.selectedIds, selectedRows);
|
|
639
726
|
}
|
|
640
|
-
|
|
641
|
-
...s,
|
|
642
|
-
selection: next
|
|
643
|
-
};
|
|
644
|
-
});
|
|
727
|
+
}
|
|
645
728
|
onRowClick?.(row, rowId, event);
|
|
646
729
|
}, [
|
|
647
730
|
selectionMode,
|
|
@@ -650,29 +733,27 @@ function DataGrid(props) {
|
|
|
650
733
|
onSelectionChange,
|
|
651
734
|
rowIds,
|
|
652
735
|
rows,
|
|
653
|
-
getRowId
|
|
736
|
+
getRowId,
|
|
737
|
+
state.selection
|
|
654
738
|
]);
|
|
655
739
|
const handleRowSelectionCheckboxClick = (0, react.useCallback)((row, rowId, event) => {
|
|
656
740
|
handleRowClick(row, rowId, event);
|
|
657
741
|
}, [handleRowClick]);
|
|
658
742
|
const handleSelectAll = (0, react.useCallback)(() => {
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
...s,
|
|
668
|
-
selection: next
|
|
669
|
-
};
|
|
670
|
-
});
|
|
743
|
+
const allSelectedNow = rowIds.every((id) => state.selection.selectedIds.has(id));
|
|
744
|
+
const next = allSelectedNow ? (0, __state_js.clearSelection)() : (0, __state_js.selectAll)(rowIds);
|
|
745
|
+
const selectedRows = allSelectedNow ? [] : rows;
|
|
746
|
+
onChange((s) => ({
|
|
747
|
+
...s,
|
|
748
|
+
selection: next
|
|
749
|
+
}));
|
|
750
|
+
if (onSelectionChange) onSelectionChange(next.selectedIds, [...selectedRows]);
|
|
671
751
|
}, [
|
|
672
752
|
onChange,
|
|
673
753
|
rowIds,
|
|
674
754
|
rows,
|
|
675
|
-
onSelectionChange
|
|
755
|
+
onSelectionChange,
|
|
756
|
+
state.selection
|
|
676
757
|
]);
|
|
677
758
|
const handleExportCsv = (0, react.useCallback)(() => {
|
|
678
759
|
(0, __state_js.exportToCsv)(rows, visibleColumns, exportFilename);
|
|
@@ -683,12 +764,118 @@ function DataGrid(props) {
|
|
|
683
764
|
]);
|
|
684
765
|
const scrollContainerRef = (0, react.useRef)(null);
|
|
685
766
|
const headerScrollRef = (0, react.useRef)(null);
|
|
767
|
+
const stickyChromeRef = (0, react.useRef)(null);
|
|
768
|
+
const rowsClipRef = (0, react.useRef)(null);
|
|
769
|
+
const measureElementFn = (0, react.useCallback)((el) => el.getBoundingClientRect().height, []);
|
|
686
770
|
const rowVirtualizer = (0, _tanstack_react_virtual.useVirtualizer)({
|
|
687
771
|
count: rows.length,
|
|
688
772
|
getScrollElement: () => scrollContainerRef.current,
|
|
689
|
-
estimateSize: () =>
|
|
690
|
-
overscan
|
|
773
|
+
estimateSize: () => estimatedRowHeight,
|
|
774
|
+
overscan,
|
|
775
|
+
getItemKey: (index) => {
|
|
776
|
+
const row = rows[index];
|
|
777
|
+
return row != null ? String(getRowId(row)) : index;
|
|
778
|
+
},
|
|
779
|
+
...isDynamicRowHeight ? { measureElement: measureElementFn } : {}
|
|
691
780
|
});
|
|
781
|
+
(0, react.useLayoutEffect)(() => {
|
|
782
|
+
const grid = gridRef.current;
|
|
783
|
+
const stickyEl = stickyChromeRef.current;
|
|
784
|
+
if (!grid || !stickyEl) return;
|
|
785
|
+
const parseRgba = (raw) => {
|
|
786
|
+
const rgbaMatch = raw.match(/rgba?\(\s*([\d.]+),\s*([\d.]+),\s*([\d.]+)(?:,\s*([\d.]+))?\s*\)/);
|
|
787
|
+
if (!rgbaMatch) return null;
|
|
788
|
+
const alphaRaw = rgbaMatch[4];
|
|
789
|
+
return [
|
|
790
|
+
Number(rgbaMatch[1]),
|
|
791
|
+
Number(rgbaMatch[2]),
|
|
792
|
+
Number(rgbaMatch[3]),
|
|
793
|
+
alphaRaw === void 0 ? 1 : Number(alphaRaw)
|
|
794
|
+
];
|
|
795
|
+
};
|
|
796
|
+
const blendOver = (base, top) => {
|
|
797
|
+
const [tr, tg, tb, ta] = top;
|
|
798
|
+
const [br, bg, bb, ba] = base;
|
|
799
|
+
const outA = ta + ba * (1 - ta);
|
|
800
|
+
if (outA === 0) return [
|
|
801
|
+
0,
|
|
802
|
+
0,
|
|
803
|
+
0,
|
|
804
|
+
0
|
|
805
|
+
];
|
|
806
|
+
return [
|
|
807
|
+
(tr * ta + br * ba * (1 - ta)) / outA,
|
|
808
|
+
(tg * ta + bg * ba * (1 - ta)) / outA,
|
|
809
|
+
(tb * ta + bb * ba * (1 - ta)) / outA,
|
|
810
|
+
outA
|
|
811
|
+
];
|
|
812
|
+
};
|
|
813
|
+
const detect = () => {
|
|
814
|
+
const layers = [];
|
|
815
|
+
let ancestor = grid.parentElement;
|
|
816
|
+
while (ancestor) {
|
|
817
|
+
const parsed = parseRgba(getComputedStyle(ancestor).backgroundColor);
|
|
818
|
+
if (parsed && parsed[3] > 0) {
|
|
819
|
+
layers.push(parsed);
|
|
820
|
+
if (parsed[3] >= 1) break;
|
|
821
|
+
}
|
|
822
|
+
ancestor = ancestor.parentElement;
|
|
823
|
+
}
|
|
824
|
+
if (layers.length === 0) {
|
|
825
|
+
stickyEl.style.backgroundColor = "";
|
|
826
|
+
return;
|
|
827
|
+
}
|
|
828
|
+
let result = layers[layers.length - 1];
|
|
829
|
+
for (let i = layers.length - 2; i >= 0; i--) result = blendOver(result, layers[i]);
|
|
830
|
+
const [r, g, b] = result;
|
|
831
|
+
stickyEl.style.backgroundColor = `rgb(${Math.round(r)}, ${Math.round(g)}, ${Math.round(b)})`;
|
|
832
|
+
};
|
|
833
|
+
detect();
|
|
834
|
+
const observer = new MutationObserver(detect);
|
|
835
|
+
observer.observe(document.documentElement, {
|
|
836
|
+
attributes: true,
|
|
837
|
+
attributeFilter: ["class"]
|
|
838
|
+
});
|
|
839
|
+
return () => observer.disconnect();
|
|
840
|
+
}, []);
|
|
841
|
+
(0, react.useLayoutEffect)(() => {
|
|
842
|
+
const gridEl = gridRef.current;
|
|
843
|
+
const stickyEl = stickyChromeRef.current;
|
|
844
|
+
const bodyEl = scrollContainerRef.current;
|
|
845
|
+
const clipEl = rowsClipRef.current;
|
|
846
|
+
if (!gridEl || !stickyEl || !bodyEl || !clipEl) return;
|
|
847
|
+
const verticalScrollEl = fillHeight ? bodyEl : getNearestVerticalScrollElement(gridEl);
|
|
848
|
+
let extraObservedScrollEl = null;
|
|
849
|
+
if (verticalScrollEl instanceof HTMLElement && verticalScrollEl !== bodyEl) extraObservedScrollEl = verticalScrollEl;
|
|
850
|
+
const updateClip = () => {
|
|
851
|
+
const stickyRect = stickyEl.getBoundingClientRect();
|
|
852
|
+
const clipRect = clipEl.getBoundingClientRect();
|
|
853
|
+
const overlap = Math.max(0, stickyRect.bottom - clipRect.top);
|
|
854
|
+
const clipValue = overlap > 0 ? `inset(${overlap}px 0 0 0)` : "";
|
|
855
|
+
const maskValue = overlap > 0 ? `linear-gradient(to bottom, transparent 0px, transparent ${overlap}px, black ${overlap}px, black 100%)` : "";
|
|
856
|
+
clipEl.style.clipPath = clipValue;
|
|
857
|
+
clipEl.style.setProperty("-webkit-clip-path", clipValue);
|
|
858
|
+
clipEl.style.maskImage = maskValue;
|
|
859
|
+
clipEl.style.setProperty("-webkit-mask-image", maskValue);
|
|
860
|
+
};
|
|
861
|
+
updateClip();
|
|
862
|
+
bodyEl.addEventListener("scroll", updateClip);
|
|
863
|
+
if (verticalScrollEl === window) window.addEventListener("scroll", updateClip, true);
|
|
864
|
+
else if (extraObservedScrollEl) extraObservedScrollEl.addEventListener("scroll", updateClip);
|
|
865
|
+
window.addEventListener("resize", updateClip);
|
|
866
|
+
const ro = new ResizeObserver(updateClip);
|
|
867
|
+
ro.observe(gridEl);
|
|
868
|
+
ro.observe(stickyEl);
|
|
869
|
+
ro.observe(bodyEl);
|
|
870
|
+
if (extraObservedScrollEl) ro.observe(extraObservedScrollEl);
|
|
871
|
+
return () => {
|
|
872
|
+
bodyEl.removeEventListener("scroll", updateClip);
|
|
873
|
+
if (verticalScrollEl === window) window.removeEventListener("scroll", updateClip, true);
|
|
874
|
+
else if (extraObservedScrollEl) extraObservedScrollEl.removeEventListener("scroll", updateClip);
|
|
875
|
+
window.removeEventListener("resize", updateClip);
|
|
876
|
+
ro.disconnect();
|
|
877
|
+
};
|
|
878
|
+
}, [fillHeight]);
|
|
692
879
|
const handleBodyScroll = (0, react.useCallback)(() => {
|
|
693
880
|
const body = scrollContainerRef.current;
|
|
694
881
|
const header = headerScrollRef.current;
|
|
@@ -728,9 +915,10 @@ function DataGrid(props) {
|
|
|
728
915
|
]);
|
|
729
916
|
const allSelected = rowIds.length > 0 && rowIds.every((id) => state.selection.selectedIds.has(id));
|
|
730
917
|
const someSelected = !allSelected && rowIds.some((id) => state.selection.selectedIds.has(id));
|
|
918
|
+
const infiniteScrollRootRef = paginationMode === "infinite" && (fillHeight || maxHeight != null) ? scrollContainerRef : void 0;
|
|
731
919
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
732
920
|
ref: gridRef,
|
|
733
|
-
className: (0, _stackframe_stack_ui.cn)("flex flex-col h-full min-h-0
|
|
921
|
+
className: (0, _stackframe_stack_ui.cn)("flex w-full min-w-0 max-w-full flex-col bg-transparent rounded-[calc(var(--radius)*2)]", fillHeight ? "min-h-0 h-full" : "min-h-0 h-auto", className),
|
|
734
922
|
style: maxHeight != null ? {
|
|
735
923
|
...gridSizingStyle,
|
|
736
924
|
maxHeight
|
|
@@ -739,23 +927,24 @@ function DataGrid(props) {
|
|
|
739
927
|
"aria-rowcount": totalRowCount ?? rows.length,
|
|
740
928
|
"aria-colcount": visibleColumns.length,
|
|
741
929
|
children: [
|
|
742
|
-
toolbar !== false && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
743
|
-
className: "relative shrink-0 bg-transparent",
|
|
744
|
-
children: toolbar ? toolbar(toolbarCtx) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__data_grid_toolbar_js.DataGridToolbar, {
|
|
745
|
-
ctx: toolbarCtx,
|
|
746
|
-
extra: typeof toolbarExtra === "function" ? toolbarExtra(toolbarCtx) : toolbarExtra
|
|
747
|
-
})
|
|
748
|
-
}),
|
|
749
930
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
931
|
+
ref: stickyChromeRef,
|
|
932
|
+
className: "sticky z-20 w-full min-w-0 shrink-0 rounded-t-[calc(var(--radius)*2)] bg-background",
|
|
933
|
+
style: { top: stickyTop ?? "var(--data-grid-sticky-top, 0px)" },
|
|
934
|
+
children: [toolbar !== false && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
935
|
+
className: "relative bg-transparent",
|
|
936
|
+
children: toolbar ? toolbar(toolbarCtx) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__data_grid_toolbar_js.DataGridToolbar, {
|
|
937
|
+
ctx: toolbarCtx,
|
|
938
|
+
extra: typeof toolbarExtra === "function" ? toolbarExtra(toolbarCtx) : toolbarExtra
|
|
939
|
+
})
|
|
940
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
941
|
+
className: "relative",
|
|
942
|
+
children: [isRefetching && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
753
943
|
className: "absolute top-0 left-0 right-0 h-0.5 z-30 bg-foreground/[0.04] overflow-hidden",
|
|
754
944
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: "h-full w-1/3 bg-blue-500/60 rounded-full animate-pulse" })
|
|
755
|
-
}),
|
|
756
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
945
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
757
946
|
ref: headerScrollRef,
|
|
758
|
-
className: "
|
|
947
|
+
className: "w-full min-w-0 shrink-0 overflow-hidden border-b border-foreground/[0.06]",
|
|
759
948
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
760
949
|
className: "flex",
|
|
761
950
|
style: {
|
|
@@ -782,77 +971,89 @@ function DataGrid(props) {
|
|
|
782
971
|
onResizeEnd: handleResizeEnd
|
|
783
972
|
}, col.id))]
|
|
784
973
|
})
|
|
785
|
-
})
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
974
|
+
})]
|
|
975
|
+
})]
|
|
976
|
+
}),
|
|
977
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
978
|
+
ref: scrollContainerRef,
|
|
979
|
+
className: (0, _stackframe_stack_ui.cn)("w-full min-w-0 overflow-auto bg-transparent", fillHeight ? "min-h-0 flex-1" : "flex-none", "[&::-webkit-scrollbar]:w-1.5 [&::-webkit-scrollbar]:h-1.5", "[&::-webkit-scrollbar-track]:bg-transparent", "[&::-webkit-scrollbar-thumb]:bg-foreground/[0.08] [&::-webkit-scrollbar-thumb]:rounded-full", "[&::-webkit-scrollbar-thumb]:hover:bg-foreground/[0.15]"),
|
|
980
|
+
onScroll: handleBodyScroll,
|
|
981
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
982
|
+
ref: rowsClipRef,
|
|
983
|
+
children: [
|
|
984
|
+
isLoading && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
985
|
+
style: { minWidth: visibleColumnMetrics.totalWidth },
|
|
986
|
+
children: loadingState ?? Array.from({ length: 8 }).map((_, i) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SkeletonRow, {
|
|
987
|
+
columns: visibleColumns,
|
|
988
|
+
height: estimatedRowHeight,
|
|
989
|
+
showCheckbox: selectionMode !== "none"
|
|
990
|
+
}, i))
|
|
991
|
+
}),
|
|
992
|
+
!isLoading && rows.length === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
993
|
+
className: "flex items-center justify-center py-16 text-sm text-muted-foreground",
|
|
994
|
+
style: { minWidth: visibleColumnMetrics.totalWidth },
|
|
995
|
+
children: emptyState ?? strings.noData
|
|
996
|
+
}),
|
|
997
|
+
!isLoading && rows.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
998
|
+
style: {
|
|
999
|
+
height: rowVirtualizer.getTotalSize(),
|
|
1000
|
+
width: "100%",
|
|
1001
|
+
minWidth: visibleColumnMetrics.totalWidth,
|
|
1002
|
+
position: "relative"
|
|
1003
|
+
},
|
|
1004
|
+
children: rowVirtualizer.getVirtualItems().map((virtualRow) => {
|
|
1005
|
+
const row = rows[virtualRow.index] ?? (0, _stackframe_stack_shared_dist_utils_errors.throwErr)(`DataGrid: virtualized row index ${virtualRow.index} out of range (rows.length=${rows.length})`);
|
|
1006
|
+
const rowId = getRowId(row);
|
|
1007
|
+
const isSelected = state.selection.selectedIds.has(rowId);
|
|
1008
|
+
const isOddRow = virtualRow.index % 2 === 1;
|
|
1009
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1010
|
+
ref: isDynamicRowHeight ? rowVirtualizer.measureElement : void 0,
|
|
1011
|
+
"data-index": virtualRow.index,
|
|
1012
|
+
className: (0, _stackframe_stack_ui.cn)("absolute left-0 w-full flex", "border-b border-black/[0.03] dark:border-white/[0.03]", "transition-colors duration-75", isSelected ? "bg-blue-500/[0.06] dark:bg-blue-400/[0.08] hover:bg-blue-500/[0.08] dark:hover:bg-blue-400/[0.1]" : isOddRow ? "bg-foreground/[0.02] dark:bg-foreground/[0.03] hover:bg-foreground/[0.04] dark:hover:bg-foreground/[0.06]" : "hover:bg-foreground/[0.025] dark:hover:bg-foreground/[0.04]", (selectionMode !== "none" || onRowClick) && "cursor-pointer"),
|
|
1013
|
+
style: {
|
|
1014
|
+
...isDynamicRowHeight ? { minHeight: estimatedRowHeight } : { height: fixedRowHeight },
|
|
1015
|
+
transform: `translateY(${virtualRow.start}px)`
|
|
1016
|
+
},
|
|
1017
|
+
onClick: (e) => {
|
|
1018
|
+
if (shouldIgnoreRowClick(e)) return;
|
|
1019
|
+
handleRowClick(row, rowId, e);
|
|
1020
|
+
},
|
|
1021
|
+
onDoubleClick: (e) => {
|
|
1022
|
+
if (shouldIgnoreRowClick(e)) return;
|
|
1023
|
+
onRowDoubleClick?.(row, rowId, e);
|
|
1024
|
+
},
|
|
1025
|
+
role: "row",
|
|
1026
|
+
"aria-rowindex": virtualRow.index + 2,
|
|
1027
|
+
"aria-selected": isSelected,
|
|
1028
|
+
"data-row-id": rowId,
|
|
1029
|
+
"data-state": isSelected ? "selected" : void 0,
|
|
1030
|
+
children: [selectionMode !== "none" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1031
|
+
className: "flex items-center justify-center border-r border-black/[0.04] dark:border-white/[0.04]",
|
|
1032
|
+
style: { width: 44 },
|
|
1033
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SelectionCheckbox, {
|
|
1034
|
+
checked: isSelected,
|
|
1035
|
+
onChange: (event) => handleRowSelectionCheckboxClick(row, rowId, event),
|
|
1036
|
+
ariaLabel: `Select row ${rowId}`
|
|
1037
|
+
})
|
|
1038
|
+
}), visibleColumns.map((col) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DataCell, {
|
|
1039
|
+
col,
|
|
1040
|
+
row,
|
|
1041
|
+
rowId,
|
|
1042
|
+
rowIndex: virtualRow.index,
|
|
1043
|
+
isSelected,
|
|
1044
|
+
dateDisplay: state.dateDisplay
|
|
1045
|
+
}, col.id))]
|
|
1046
|
+
}, rowId);
|
|
852
1047
|
})
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
1048
|
+
}),
|
|
1049
|
+
paginationMode === "infinite" && hasMore && !isLoading && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InfiniteScrollSentinel, {
|
|
1050
|
+
onIntersect: onLoadMore ?? NOOP,
|
|
1051
|
+
isLoading: isLoadingMore,
|
|
1052
|
+
rootRef: infiniteScrollRootRef,
|
|
1053
|
+
strings
|
|
1054
|
+
})
|
|
1055
|
+
]
|
|
1056
|
+
})
|
|
856
1057
|
}),
|
|
857
1058
|
footer !== false && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
858
1059
|
className: "relative z-10 shrink-0 bg-transparent",
|
|
@@ -868,4 +1069,5 @@ function DataGrid(props) {
|
|
|
868
1069
|
|
|
869
1070
|
//#endregion
|
|
870
1071
|
exports.DataGrid = DataGrid;
|
|
1072
|
+
exports.isDataGridInteractiveRowClickTarget = isDataGridInteractiveRowClickTarget;
|
|
871
1073
|
//# sourceMappingURL=data-grid.js.map
|