@hexclave/dashboard-ui-components 1.0.53 → 1.0.54
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/button.d.ts +4 -4
- package/dist/components/data-grid/data-grid-export-dialog.d.ts +1 -1
- package/dist/components/data-grid/data-grid-sizing.d.ts +1 -1
- package/dist/components/data-grid/data-grid-toolbar.d.ts +1 -1
- package/dist/components/data-grid/data-grid-toolbar.js +37 -29
- package/dist/components/data-grid/data-grid-toolbar.js.map +1 -1
- package/dist/components/data-grid/data-grid.d.ts +1 -1
- package/dist/components/data-grid/data-grid.js +96 -12
- package/dist/components/data-grid/data-grid.js.map +1 -1
- package/dist/components/data-grid/data-grid.test.js +101 -2
- package/dist/components/data-grid/data-grid.test.js.map +1 -1
- package/dist/components/data-grid/index.d.ts +8 -8
- package/dist/components/data-grid/state.d.ts +1 -1
- package/dist/components/data-grid/strings.d.ts +1 -1
- package/dist/components/data-grid/types.d.ts +1 -1
- package/dist/components/data-grid/use-data-source.d.ts +1 -1
- package/dist/components/data-grid/use-url-state.d.ts +1 -1
- package/dist/dashboard-ui-components.global.js +21287 -4281
- package/dist/dashboard-ui-components.global.js.map +4 -4
- package/dist/{data-grid-AJi1TNDa.d.ts → data-grid-DWHAjaNo.d.ts} +2 -2
- package/dist/data-grid-DWHAjaNo.d.ts.map +1 -0
- package/dist/{data-grid-sizing-S1ixcZKo.d.ts → data-grid-sizing-D9y1xwaq.d.ts} +2 -2
- package/dist/{data-grid-sizing-S1ixcZKo.d.ts.map → data-grid-sizing-D9y1xwaq.d.ts.map} +1 -1
- package/dist/{data-grid-toolbar-BGSNGDpT.d.ts → data-grid-toolbar-dgvnYEpC.d.ts} +10 -6
- package/dist/data-grid-toolbar-dgvnYEpC.d.ts.map +1 -0
- package/dist/esm/components/button.d.ts +4 -4
- package/dist/esm/components/data-grid/data-grid-toolbar.d.ts +8 -4
- package/dist/esm/components/data-grid/data-grid-toolbar.d.ts.map +1 -1
- package/dist/esm/components/data-grid/data-grid-toolbar.js +38 -30
- package/dist/esm/components/data-grid/data-grid-toolbar.js.map +1 -1
- package/dist/esm/components/data-grid/data-grid.d.ts.map +1 -1
- package/dist/esm/components/data-grid/data-grid.js +96 -12
- package/dist/esm/components/data-grid/data-grid.js.map +1 -1
- package/dist/esm/components/data-grid/data-grid.test.js +102 -3
- package/dist/esm/components/data-grid/data-grid.test.js.map +1 -1
- package/dist/esm/components/data-grid/types.d.ts +7 -0
- package/dist/esm/components/data-grid/types.d.ts.map +1 -1
- package/dist/esm/package.json +12 -0
- package/dist/index.d.ts +8 -8
- package/dist/{state-6LW8ACh5.d.ts → state-D5wpAg_G.d.ts} +2 -2
- package/dist/{state-6LW8ACh5.d.ts.map → state-D5wpAg_G.d.ts.map} +1 -1
- package/dist/{strings-CACVtKlq.d.ts → strings-DsIJ_o4-.d.ts} +2 -2
- package/dist/{strings-CACVtKlq.d.ts.map → strings-DsIJ_o4-.d.ts.map} +1 -1
- package/dist/{types-1-eOd1JF.d.ts → types-D8o47V7M.d.ts} +8 -1
- package/dist/{types-1-eOd1JF.d.ts.map → types-D8o47V7M.d.ts.map} +1 -1
- package/dist/{use-data-source-BVFynerX.d.ts → use-data-source-CAZPhl8e.d.ts} +2 -2
- package/dist/{use-data-source-BVFynerX.d.ts.map → use-data-source-CAZPhl8e.d.ts.map} +1 -1
- package/dist/{use-url-state-B4_wnBjm.d.ts → use-url-state-97YdV81Q.d.ts} +2 -2
- package/dist/{use-url-state-B4_wnBjm.d.ts.map → use-url-state-97YdV81Q.d.ts.map} +1 -1
- package/package.json +3 -3
- package/src/components/data-grid/data-grid-toolbar.tsx +20 -6
- package/src/components/data-grid/data-grid.test.tsx +127 -1
- package/src/components/data-grid/data-grid.tsx +174 -37
- package/src/components/data-grid/types.ts +7 -0
- package/dist/data-grid-AJi1TNDa.d.ts.map +0 -1
- package/dist/data-grid-toolbar-BGSNGDpT.d.ts.map +0 -1
|
@@ -62,6 +62,12 @@ import type {
|
|
|
62
62
|
// (no `fillHeight`, no `maxHeight`). Leaves ~16rem of room for the top bar, page header, and grid
|
|
63
63
|
// toolbar. See the `effectiveMaxHeight` comment in DataGrid for why an infinite grid must be bounded.
|
|
64
64
|
const DEFAULT_INFINITE_MAX_HEIGHT = "calc(100dvh - 16rem)";
|
|
65
|
+
const DATA_GRID_SCROLLBAR_CLASS_NAME = cn(
|
|
66
|
+
"[&::-webkit-scrollbar]:w-1.5 [&::-webkit-scrollbar]:h-1.5",
|
|
67
|
+
"[&::-webkit-scrollbar-track]:bg-transparent",
|
|
68
|
+
"[&::-webkit-scrollbar-thumb]:bg-foreground/[0.08] [&::-webkit-scrollbar-thumb]:rounded-full",
|
|
69
|
+
"[&::-webkit-scrollbar-thumb]:hover:bg-foreground/[0.15]",
|
|
70
|
+
);
|
|
65
71
|
|
|
66
72
|
// ─── Row click target ────────────────────────────────────────────────
|
|
67
73
|
|
|
@@ -376,6 +382,15 @@ function hashStringToInt(value: string): number {
|
|
|
376
382
|
return Math.abs(hash);
|
|
377
383
|
}
|
|
378
384
|
|
|
385
|
+
/**
|
|
386
|
+
* Column flex fractions used when the grid is loading before any schema is
|
|
387
|
+
* known (e.g. QueryDataGrid discovers columns from the first result page).
|
|
388
|
+
* Without these, `SkeletonRow` would render zero cells and the body looks
|
|
389
|
+
* like an empty black void instead of a shimmer.
|
|
390
|
+
*/
|
|
391
|
+
const SCHEMA_PENDING_COL_FRACTIONS = [0.18, 0.16, 0.34, 0.2, 0.12] as const;
|
|
392
|
+
const SCHEMA_PENDING_SKELETON_ROWS = 10;
|
|
393
|
+
|
|
379
394
|
function SkeletonRow({
|
|
380
395
|
columns,
|
|
381
396
|
height,
|
|
@@ -405,6 +420,72 @@ function SkeletonRow({
|
|
|
405
420
|
);
|
|
406
421
|
}
|
|
407
422
|
|
|
423
|
+
/** Sticky header shimmer when columns haven't been discovered yet. */
|
|
424
|
+
function SchemaPendingHeaderCells({ showCheckbox }: { showCheckbox?: boolean }) {
|
|
425
|
+
return (
|
|
426
|
+
<>
|
|
427
|
+
{showCheckbox && (
|
|
428
|
+
<div
|
|
429
|
+
className="flex shrink-0 items-center justify-center border-r border-foreground/[0.04]"
|
|
430
|
+
style={{ width: 44 }}
|
|
431
|
+
/>
|
|
432
|
+
)}
|
|
433
|
+
{SCHEMA_PENDING_COL_FRACTIONS.map((frac, colIdx) => (
|
|
434
|
+
<div
|
|
435
|
+
key={colIdx}
|
|
436
|
+
className="flex items-center px-3 border-r border-black/[0.04] dark:border-white/[0.04] last:border-r-0"
|
|
437
|
+
style={{ width: `${frac * 100}%` }}
|
|
438
|
+
>
|
|
439
|
+
<DesignSkeleton
|
|
440
|
+
className="h-3 rounded-md"
|
|
441
|
+
style={{ width: `${45 + (colIdx * 13) % 30}%` }}
|
|
442
|
+
/>
|
|
443
|
+
</div>
|
|
444
|
+
))}
|
|
445
|
+
</>
|
|
446
|
+
);
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
/** Full-width body shimmer when columns haven't been discovered yet. */
|
|
450
|
+
function SchemaPendingSkeleton({
|
|
451
|
+
rowHeight,
|
|
452
|
+
showCheckbox,
|
|
453
|
+
}: {
|
|
454
|
+
rowHeight: number,
|
|
455
|
+
showCheckbox?: boolean,
|
|
456
|
+
}) {
|
|
457
|
+
return (
|
|
458
|
+
<div className="w-full" data-data-grid-schema-pending-skeleton="" role="status" aria-label="Loading">
|
|
459
|
+
{Array.from({ length: SCHEMA_PENDING_SKELETON_ROWS }).map((_, rowIdx) => (
|
|
460
|
+
<div
|
|
461
|
+
key={rowIdx}
|
|
462
|
+
className="flex w-full border-b border-black/[0.04] dark:border-white/[0.04]"
|
|
463
|
+
style={{ height: rowHeight }}
|
|
464
|
+
role="row"
|
|
465
|
+
>
|
|
466
|
+
{showCheckbox && (
|
|
467
|
+
<div className="flex shrink-0 items-center justify-center border-r border-black/[0.04] dark:border-white/[0.04]" style={{ width: 44 }}>
|
|
468
|
+
<DesignSkeleton className="h-4 w-4 rounded" />
|
|
469
|
+
</div>
|
|
470
|
+
)}
|
|
471
|
+
{SCHEMA_PENDING_COL_FRACTIONS.map((frac, colIdx) => (
|
|
472
|
+
<div
|
|
473
|
+
key={colIdx}
|
|
474
|
+
className="flex items-center px-3 border-r border-black/[0.04] dark:border-white/[0.04] last:border-r-0"
|
|
475
|
+
style={{ width: `${frac * 100}%` }}
|
|
476
|
+
>
|
|
477
|
+
<DesignSkeleton
|
|
478
|
+
className="h-3.5 rounded-md"
|
|
479
|
+
style={{ width: `${50 + ((rowIdx * 7 + colIdx * 19) % 40)}%` }}
|
|
480
|
+
/>
|
|
481
|
+
</div>
|
|
482
|
+
))}
|
|
483
|
+
</div>
|
|
484
|
+
))}
|
|
485
|
+
</div>
|
|
486
|
+
);
|
|
487
|
+
}
|
|
488
|
+
|
|
408
489
|
// ─── Selection checkbox ──────────────────────────────────────────────
|
|
409
490
|
|
|
410
491
|
function SelectionCheckbox({
|
|
@@ -640,6 +721,7 @@ export function DataGrid<TRow>(props: DataGridProps<TRow>) {
|
|
|
640
721
|
maxHeight,
|
|
641
722
|
fillHeight = true,
|
|
642
723
|
stickyTop,
|
|
724
|
+
horizontalScrollbarPosition = "bottom",
|
|
643
725
|
toolbar,
|
|
644
726
|
toolbarExtra,
|
|
645
727
|
emptyState,
|
|
@@ -970,12 +1052,42 @@ export function DataGrid<TRow>(props: DataGridProps<TRow>) {
|
|
|
970
1052
|
};
|
|
971
1053
|
}, []);
|
|
972
1054
|
|
|
973
|
-
|
|
1055
|
+
// Keep header + body horizontal offsets locked. When the scrollbar lives under
|
|
1056
|
+
// the column headers (`horizontalScrollbarPosition="top"`), the header is the
|
|
1057
|
+
// scroll owner and the body mirrors it (and vice versa for the default bottom bar).
|
|
1058
|
+
const isSyncingHorizontalScrollRef = useRef(false);
|
|
1059
|
+
const syncHorizontalScroll = useCallback((source: "header" | "body") => {
|
|
974
1060
|
const body = scrollContainerRef.current;
|
|
975
1061
|
const header = headerScrollRef.current;
|
|
976
|
-
if (body
|
|
1062
|
+
if (body == null || header == null || isSyncingHorizontalScrollRef.current) return;
|
|
1063
|
+
const from = source === "header" ? header : body;
|
|
1064
|
+
const to = source === "header" ? body : header;
|
|
1065
|
+
if (to.scrollLeft === from.scrollLeft) return;
|
|
1066
|
+
isSyncingHorizontalScrollRef.current = true;
|
|
1067
|
+
to.scrollLeft = from.scrollLeft;
|
|
1068
|
+
isSyncingHorizontalScrollRef.current = false;
|
|
977
1069
|
}, []);
|
|
978
1070
|
|
|
1071
|
+
const handleBodyScroll = useCallback(() => {
|
|
1072
|
+
syncHorizontalScroll("body");
|
|
1073
|
+
}, [syncHorizontalScroll]);
|
|
1074
|
+
|
|
1075
|
+
const handleHeaderScroll = useCallback(() => {
|
|
1076
|
+
syncHorizontalScroll("header");
|
|
1077
|
+
}, [syncHorizontalScroll]);
|
|
1078
|
+
|
|
1079
|
+
const horizontalScrollbarAtTop = horizontalScrollbarPosition === "top";
|
|
1080
|
+
// Shared thin thumb styling. When the bar is on top, the header scrollport shows
|
|
1081
|
+
// it; the body keeps overflow-x hidden so a second thumb doesn't appear at the bottom.
|
|
1082
|
+
// Trackpad / shift-wheel horizontal gestures land on the body; with the top
|
|
1083
|
+
// scrollbar the body has overflow-x:hidden, so forward deltaX to the header.
|
|
1084
|
+
const handleBodyWheel = useCallback((event: React.WheelEvent<HTMLDivElement>) => {
|
|
1085
|
+
if (!horizontalScrollbarAtTop || event.deltaX === 0) return;
|
|
1086
|
+
const header = headerScrollRef.current;
|
|
1087
|
+
if (header == null) return;
|
|
1088
|
+
header.scrollLeft += event.deltaX;
|
|
1089
|
+
}, [horizontalScrollbarAtTop]);
|
|
1090
|
+
|
|
979
1091
|
// ── Toolbar / Footer context ─────────────────────────────────
|
|
980
1092
|
const toolbarCtx: DataGridToolbarContext<TRow> = useMemo(
|
|
981
1093
|
() => ({
|
|
@@ -1084,34 +1196,46 @@ export function DataGrid<TRow>(props: DataGridProps<TRow>) {
|
|
|
1084
1196
|
)}
|
|
1085
1197
|
<div
|
|
1086
1198
|
ref={headerScrollRef}
|
|
1087
|
-
className=
|
|
1199
|
+
className={cn(
|
|
1200
|
+
"w-full min-w-0 shrink-0 border-b border-foreground/[0.06]",
|
|
1201
|
+
horizontalScrollbarAtTop
|
|
1202
|
+
? cn("overflow-x-auto overflow-y-hidden", DATA_GRID_SCROLLBAR_CLASS_NAME)
|
|
1203
|
+
: "overflow-hidden",
|
|
1204
|
+
)}
|
|
1205
|
+
onScroll={horizontalScrollbarAtTop ? handleHeaderScroll : undefined}
|
|
1088
1206
|
>
|
|
1089
1207
|
<div
|
|
1090
1208
|
className="flex"
|
|
1091
|
-
style={{ height: headerHeight, minWidth: totalContentWidth }}
|
|
1209
|
+
style={{ height: headerHeight, minWidth: totalContentWidth || undefined, width: visibleColumns.length === 0 ? "100%" : undefined }}
|
|
1092
1210
|
role="row"
|
|
1093
1211
|
>
|
|
1094
|
-
{
|
|
1095
|
-
<
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1212
|
+
{isLoading && visibleColumns.length === 0 ? (
|
|
1213
|
+
<SchemaPendingHeaderCells showCheckbox={selectionMode !== "none"} />
|
|
1214
|
+
) : (
|
|
1215
|
+
<>
|
|
1216
|
+
{selectionMode !== "none" && (
|
|
1217
|
+
<div
|
|
1218
|
+
className="flex items-center justify-center border-r border-foreground/[0.04]"
|
|
1219
|
+
style={{ width: 44 }}
|
|
1220
|
+
>
|
|
1221
|
+
{selectionMode === "multiple" && (
|
|
1222
|
+
<SelectionCheckbox
|
|
1223
|
+
checked={allSelected}
|
|
1224
|
+
indeterminate={someSelected}
|
|
1225
|
+
onChange={handleSelectAll}
|
|
1226
|
+
ariaLabel="Select all rows on this page"
|
|
1227
|
+
title="Select all rows on this page"
|
|
1228
|
+
/>
|
|
1229
|
+
)}
|
|
1230
|
+
</div>
|
|
1107
1231
|
)}
|
|
1108
|
-
|
|
1232
|
+
{visibleColumns.map((col) => {
|
|
1233
|
+
const header = headerByColId.get(col.id);
|
|
1234
|
+
if (!header) return null;
|
|
1235
|
+
return <HeaderCell key={col.id} header={header} col={col} resizable={resizable} />;
|
|
1236
|
+
})}
|
|
1237
|
+
</>
|
|
1109
1238
|
)}
|
|
1110
|
-
{visibleColumns.map((col) => {
|
|
1111
|
-
const header = headerByColId.get(col.id);
|
|
1112
|
-
if (!header) return null;
|
|
1113
|
-
return <HeaderCell key={col.id} header={header} col={col} resizable={resizable} />;
|
|
1114
|
-
})}
|
|
1115
1239
|
</div>
|
|
1116
1240
|
</div>
|
|
1117
1241
|
</div>
|
|
@@ -1120,14 +1244,15 @@ export function DataGrid<TRow>(props: DataGridProps<TRow>) {
|
|
|
1120
1244
|
<div
|
|
1121
1245
|
ref={scrollContainerRef}
|
|
1122
1246
|
className={cn(
|
|
1123
|
-
"relative z-0 w-full min-w-0
|
|
1247
|
+
"relative z-0 w-full min-w-0 bg-transparent",
|
|
1248
|
+
// Top scrollbar: body is y-only so the horizontal thumb isn't duplicated
|
|
1249
|
+
// under the rows. scrollLeft is still set programmatically from the header.
|
|
1250
|
+
horizontalScrollbarAtTop ? "overflow-y-auto overflow-x-hidden" : "overflow-auto",
|
|
1124
1251
|
isBounded ? "min-h-0 flex-1" : "flex-none",
|
|
1125
|
-
|
|
1126
|
-
"[&::-webkit-scrollbar-track]:bg-transparent",
|
|
1127
|
-
"[&::-webkit-scrollbar-thumb]:bg-foreground/[0.08] [&::-webkit-scrollbar-thumb]:rounded-full",
|
|
1128
|
-
"[&::-webkit-scrollbar-thumb]:hover:bg-foreground/[0.15]",
|
|
1252
|
+
DATA_GRID_SCROLLBAR_CLASS_NAME,
|
|
1129
1253
|
)}
|
|
1130
1254
|
onScroll={handleBodyScroll}
|
|
1255
|
+
onWheel={horizontalScrollbarAtTop ? handleBodyWheel : undefined}
|
|
1131
1256
|
>
|
|
1132
1257
|
<div
|
|
1133
1258
|
ref={rowsClipRef}
|
|
@@ -1139,15 +1264,27 @@ export function DataGrid<TRow>(props: DataGridProps<TRow>) {
|
|
|
1139
1264
|
}}
|
|
1140
1265
|
>
|
|
1141
1266
|
{isLoading && (
|
|
1142
|
-
<div style={{ minWidth: totalContentWidth }}>
|
|
1143
|
-
{loadingState ??
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
columns
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1267
|
+
<div style={{ minWidth: totalContentWidth || undefined, width: visibleColumns.length === 0 ? "100%" : undefined }}>
|
|
1268
|
+
{loadingState ?? (
|
|
1269
|
+
visibleColumns.length === 0 ? (
|
|
1270
|
+
// Schema not known yet (common for query grids that discover
|
|
1271
|
+
// columns from the first page) — fall back to a full-width
|
|
1272
|
+
// shimmer so loading never looks like an empty black pane.
|
|
1273
|
+
<SchemaPendingSkeleton
|
|
1274
|
+
rowHeight={estimatedRowHeight}
|
|
1275
|
+
showCheckbox={selectionMode !== "none"}
|
|
1276
|
+
/>
|
|
1277
|
+
) : (
|
|
1278
|
+
Array.from({ length: 8 }).map((_, i) => (
|
|
1279
|
+
<SkeletonRow
|
|
1280
|
+
key={i}
|
|
1281
|
+
columns={visibleColumns}
|
|
1282
|
+
height={estimatedRowHeight}
|
|
1283
|
+
showCheckbox={selectionMode !== "none"}
|
|
1284
|
+
/>
|
|
1285
|
+
))
|
|
1286
|
+
)
|
|
1287
|
+
)}
|
|
1151
1288
|
</div>
|
|
1152
1289
|
)}
|
|
1153
1290
|
|
|
@@ -345,6 +345,13 @@ export type DataGridProps<TRow> = {
|
|
|
345
345
|
fillHeight?: boolean;
|
|
346
346
|
/** Top offset for the sticky toolbar + header (px or CSS string). */
|
|
347
347
|
stickyTop?: number | string;
|
|
348
|
+
/**
|
|
349
|
+
* Where the horizontal scrollbar is shown when columns overflow.
|
|
350
|
+
* - `"bottom"` (default): on the row scrollport, under the last visible rows.
|
|
351
|
+
* - `"top"`: under the column headers (top of the table body), so wide
|
|
352
|
+
* tables stay reachable without scrolling to the bottom first.
|
|
353
|
+
*/
|
|
354
|
+
horizontalScrollbarPosition?: "top" | "bottom";
|
|
348
355
|
|
|
349
356
|
// ── Callbacks ──────────────────────────────────────────────────
|
|
350
357
|
} & DataGridCallbacks<TRow> & {
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"data-grid-AJi1TNDa.d.ts","names":[],"sources":["../src/components/data-grid/data-grid.tsx"],"mappings":";;;iBAyEgB,mCAAA,CAAoC,MAA0B,EAAlB,WAAW;;AAAvE;;;;AAA8E;AAkiB9E;;;;;;;;;;;AAAyD;;;;;;;;;;;;;;;;;;;;;iBAAzC,QAAA,OAAe,KAAA,EAAO,aAAa,CAAC,IAAA,gCAAK,GAAA,CAAA,OAAA"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"data-grid-toolbar-BGSNGDpT.d.ts","names":[],"sources":["../src/components/data-grid/data-grid-toolbar.tsx"],"mappings":";;;;iBA6QgB,eAAA;EACd,GAAA;EACA,KAAA;EACA,YAAA;EACA;AAAA;EAEA,GAAA,EAAK,sBAAA,CAAuB,IAAA;EANd;;;;EAWd,KAAA,GAAQ,KAAA,CAAM,SAAA;EARd;;;;;EAcA,YAAA,GAAe,KAAA,CAAM,SAAA;EAAS;;;EAI9B,eAAA;AAAA,gCACD,GAAA,CAAA,OAAA"}
|