@hexclave/dashboard-ui-components 1.0.52 → 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
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { w as DataGridProps } from "./types-
|
|
1
|
+
import { w as DataGridProps } from "./types-D8o47V7M.js";
|
|
2
2
|
|
|
3
3
|
//#region src/components/data-grid/data-grid.d.ts
|
|
4
4
|
declare function isDataGridInteractiveRowClickTarget(target: EventTarget | null): boolean;
|
|
@@ -43,4 +43,4 @@ declare function isDataGridInteractiveRowClickTarget(target: EventTarget | null)
|
|
|
43
43
|
declare function DataGrid<TRow>(props: DataGridProps<TRow>): import("react/jsx-runtime").JSX.Element;
|
|
44
44
|
//#endregion
|
|
45
45
|
export { isDataGridInteractiveRowClickTarget as n, DataGrid as t };
|
|
46
|
-
//# sourceMappingURL=data-grid-
|
|
46
|
+
//# sourceMappingURL=data-grid-DWHAjaNo.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"data-grid-DWHAjaNo.d.ts","names":[],"sources":["../src/components/data-grid/data-grid.tsx"],"mappings":";;;iBA+EgB,mCAAA,CAAoC,MAA0B,EAAlB,WAAW;;AAAvE;;;;AAA8E;AA6mB9E;;;;;;;;;;;AAAyD;;;;;;;;;;;;;;;;;;;;;iBAAzC,QAAA,OAAe,KAAA,EAAO,aAAa,CAAC,IAAA,gCAAK,GAAA,CAAA,OAAA"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { i as DataGridColumnDef } from "./types-
|
|
1
|
+
import { i as DataGridColumnDef } from "./types-D8o47V7M.js";
|
|
2
2
|
|
|
3
3
|
//#region src/components/data-grid/data-grid-sizing.d.ts
|
|
4
4
|
declare const DEFAULT_MAX_COL_WIDTH = 800;
|
|
@@ -10,4 +10,4 @@ declare function getEffectiveMaxWidth<TRow>(col: DataGridColumnDef<TRow>): numbe
|
|
|
10
10
|
declare function clampColumnWidth<TRow>(col: DataGridColumnDef<TRow>, width: number): number;
|
|
11
11
|
//#endregion
|
|
12
12
|
export { getEffectiveMinWidth as a, getEffectiveMaxWidth as i, DEFAULT_MAX_COL_WIDTH as n, clampColumnWidth as r, DEFAULT_COL_WIDTH as t };
|
|
13
|
-
//# sourceMappingURL=data-grid-sizing-
|
|
13
|
+
//# sourceMappingURL=data-grid-sizing-D9y1xwaq.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"data-grid-sizing-
|
|
1
|
+
{"version":3,"file":"data-grid-sizing-D9y1xwaq.d.ts","names":[],"sources":["../src/components/data-grid/data-grid-sizing.ts"],"mappings":";;;cAIa,qBAAA;AAAA,cACA,iBAAA;AADb;;AAAA,iBA+BgB,oBAAA,OAA2B,GAAA,EAAK,iBAAiB,CAAC,IAAA;AAAA,iBASlD,oBAAA,OAA2B,GAAA,EAAK,iBAAiB,CAAC,IAAA;AAAA,iBAIlD,gBAAA,OAAuB,GAAA,EAAK,iBAAiB,CAAC,IAAA,GAAO,KAAA"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { M as DataGridToolbarContext } from "./types-
|
|
1
|
+
import { M as DataGridToolbarContext } from "./types-D8o47V7M.js";
|
|
2
2
|
import React from "react";
|
|
3
3
|
|
|
4
4
|
//#region src/components/data-grid/data-grid-toolbar.d.ts
|
|
@@ -6,13 +6,13 @@ declare function DataGridToolbar<TRow>({
|
|
|
6
6
|
ctx,
|
|
7
7
|
extra,
|
|
8
8
|
extraLeading,
|
|
9
|
+
extraActions,
|
|
9
10
|
hideQuickSearch
|
|
10
11
|
}: {
|
|
11
12
|
ctx: DataGridToolbarContext<TRow>;
|
|
12
|
-
/** Extra content
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* the default actions. */
|
|
13
|
+
/** Extra content in the leading cluster, after search / `extraLeading`
|
|
14
|
+
* (filters, status badges, row counts). Stays with the search side so
|
|
15
|
+
* it doesn't compete with the trailing icon actions. */
|
|
16
16
|
extra?: React.ReactNode;
|
|
17
17
|
/** Extra content rendered at the START of the toolbar row — occupies
|
|
18
18
|
* the same position as the built-in quick search (after it, if the
|
|
@@ -20,6 +20,10 @@ declare function DataGridToolbar<TRow>({
|
|
|
20
20
|
* to fully replace the quick search with a custom input, e.g. an
|
|
21
21
|
* AI-powered search bar. */
|
|
22
22
|
extraLeading?: React.ReactNode;
|
|
23
|
+
/** Extra content in the trailing action cluster, immediately before
|
|
24
|
+
* the built-in columns / export buttons (e.g. a labeled Refresh).
|
|
25
|
+
* When present, a hairline divider separates it from those icons. */
|
|
26
|
+
extraActions?: React.ReactNode;
|
|
23
27
|
/** Whether to hide the built-in quick-search input. When `true`,
|
|
24
28
|
* callers are expected to provide their own search UI via
|
|
25
29
|
* `extraLeading`. */
|
|
@@ -27,4 +31,4 @@ declare function DataGridToolbar<TRow>({
|
|
|
27
31
|
}): import("react/jsx-runtime").JSX.Element;
|
|
28
32
|
//#endregion
|
|
29
33
|
export { DataGridToolbar as t };
|
|
30
|
-
//# sourceMappingURL=data-grid-toolbar-
|
|
34
|
+
//# sourceMappingURL=data-grid-toolbar-dgvnYEpC.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"data-grid-toolbar-dgvnYEpC.d.ts","names":[],"sources":["../src/components/data-grid/data-grid-toolbar.tsx"],"mappings":";;;;iBA6QgB,eAAA;EACd,GAAA;EACA,KAAA;EACA,YAAA;EACA,YAAA;EACA;AAAA;EAEA,GAAA,EAAK,sBAAA,CAAuB,IAAA;EAPd;;;EAWd,KAAA,GAAQ,KAAA,CAAM,SAAA;EATd;;;;;EAeA,YAAA,GAAe,KAAA,CAAM,SAAA;EANb;;;EAUR,YAAA,GAAe,KAAA,CAAM,SAAA;EAAS;;;EAI9B,eAAA;AAAA,gCACD,GAAA,CAAA,OAAA"}
|
|
@@ -3,8 +3,8 @@ import React from "react";
|
|
|
3
3
|
|
|
4
4
|
//#region src/components/button.d.ts
|
|
5
5
|
declare const designButtonVariants: (props?: ({
|
|
6
|
-
variant?: "
|
|
7
|
-
size?: "
|
|
6
|
+
variant?: "plain" | "link" | "default" | "destructive" | "outline" | "secondary" | "ghost" | null | undefined;
|
|
7
|
+
size?: "sm" | "lg" | "default" | "icon" | null | undefined;
|
|
8
8
|
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
9
9
|
type DesignOriginalButtonProps = {
|
|
10
10
|
asChild?: boolean;
|
|
@@ -35,8 +35,8 @@ declare const DesignButton: React.FC<{
|
|
|
35
35
|
} & {
|
|
36
36
|
asChild?: boolean;
|
|
37
37
|
} & React.ButtonHTMLAttributes<HTMLButtonElement> & VariantProps<(props?: ({
|
|
38
|
-
variant?: "
|
|
39
|
-
size?: "
|
|
38
|
+
variant?: "plain" | "link" | "default" | "destructive" | "outline" | "secondary" | "ghost" | null | undefined;
|
|
39
|
+
size?: "sm" | "lg" | "default" | "icon" | null | undefined;
|
|
40
40
|
} & import("class-variance-authority/types").ClassProp) | undefined) => string> & {
|
|
41
41
|
ref?: React.Ref<HTMLButtonElement> | undefined;
|
|
42
42
|
}>;
|
|
@@ -6,13 +6,13 @@ declare function DataGridToolbar<TRow>({
|
|
|
6
6
|
ctx,
|
|
7
7
|
extra,
|
|
8
8
|
extraLeading,
|
|
9
|
+
extraActions,
|
|
9
10
|
hideQuickSearch
|
|
10
11
|
}: {
|
|
11
12
|
ctx: DataGridToolbarContext<TRow>;
|
|
12
|
-
/** Extra content
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* the default actions. */
|
|
13
|
+
/** Extra content in the leading cluster, after search / `extraLeading`
|
|
14
|
+
* (filters, status badges, row counts). Stays with the search side so
|
|
15
|
+
* it doesn't compete with the trailing icon actions. */
|
|
16
16
|
extra?: React.ReactNode;
|
|
17
17
|
/** Extra content rendered at the START of the toolbar row — occupies
|
|
18
18
|
* the same position as the built-in quick search (after it, if the
|
|
@@ -20,6 +20,10 @@ declare function DataGridToolbar<TRow>({
|
|
|
20
20
|
* to fully replace the quick search with a custom input, e.g. an
|
|
21
21
|
* AI-powered search bar. */
|
|
22
22
|
extraLeading?: React.ReactNode;
|
|
23
|
+
/** Extra content in the trailing action cluster, immediately before
|
|
24
|
+
* the built-in columns / export buttons (e.g. a labeled Refresh).
|
|
25
|
+
* When present, a hairline divider separates it from those icons. */
|
|
26
|
+
extraActions?: React.ReactNode;
|
|
23
27
|
/** Whether to hide the built-in quick-search input. When `true`,
|
|
24
28
|
* callers are expected to provide their own search UI via
|
|
25
29
|
* `extraLeading`. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"data-grid-toolbar.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;
|
|
1
|
+
{"version":3,"file":"data-grid-toolbar.d.ts","names":[],"sources":["../../../../src/components/data-grid/data-grid-toolbar.tsx"],"mappings":";;;;iBA6QgB,eAAA;EACd,GAAA;EACA,KAAA;EACA,YAAA;EACA,YAAA;EACA;AAAA;EAEA,GAAA,EAAK,sBAAA,CAAuB,IAAA;EAPd;;;EAWd,KAAA,GAAQ,KAAA,CAAM,SAAA;EATd;;;;;EAeA,YAAA,GAAe,KAAA,CAAM,SAAA;EANb;;;EAUR,YAAA,GAAe,KAAA,CAAM,SAAA;EAAS;;;EAI9B,eAAA;AAAA,gCACD,GAAA,CAAA,OAAA"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { Check, DownloadSimple, Eye, EyeSlash, MagnifyingGlass, X } from "@phosphor-icons/react";
|
|
3
3
|
import { cn } from "@hexclave/ui";
|
|
4
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
4
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
5
5
|
import React, { useCallback, useMemo, useRef, useState } from "react";
|
|
6
6
|
//#region src/components/data-grid/data-grid-toolbar.tsx
|
|
7
7
|
function usePopover() {
|
|
@@ -148,7 +148,7 @@ function ColumnManager({ columns, visibility, onChange, strings, dateDisplay, on
|
|
|
148
148
|
]
|
|
149
149
|
});
|
|
150
150
|
}
|
|
151
|
-
function DataGridToolbar({ ctx, extra, extraLeading, hideQuickSearch }) {
|
|
151
|
+
function DataGridToolbar({ ctx, extra, extraLeading, extraActions, hideQuickSearch }) {
|
|
152
152
|
const { state, onChange, columns, strings, exportCsv } = ctx;
|
|
153
153
|
const columnPopover = usePopover();
|
|
154
154
|
const updateVisibility = useCallback((visibility) => {
|
|
@@ -174,8 +174,9 @@ function DataGridToolbar({ ctx, extra, extraLeading, hideQuickSearch }) {
|
|
|
174
174
|
}));
|
|
175
175
|
}, [onChange]);
|
|
176
176
|
const hasDateColumns = useMemo(() => columns.some((c) => c.type === "date" || c.type === "dateTime"), [columns]);
|
|
177
|
+
const hasExtraActions = React.Children.toArray(extraActions).length > 0;
|
|
177
178
|
return /* @__PURE__ */ jsxs("div", {
|
|
178
|
-
className: "flex w-full min-w-0 flex-col gap-2 px-2.5 py-2.5 border-b border-foreground/[0.06] sm:flex-row sm:items-center sm:gap-2",
|
|
179
|
+
className: "flex w-full min-w-0 flex-col gap-2 px-2.5 py-2.5 pr-3 border-b border-foreground/[0.06] sm:flex-row sm:items-center sm:gap-2",
|
|
179
180
|
children: [/* @__PURE__ */ jsxs("div", {
|
|
180
181
|
className: "flex min-w-0 flex-1 flex-wrap items-center gap-2",
|
|
181
182
|
children: [
|
|
@@ -188,33 +189,40 @@ function DataGridToolbar({ ctx, extra, extraLeading, hideQuickSearch }) {
|
|
|
188
189
|
extra
|
|
189
190
|
]
|
|
190
191
|
}), /* @__PURE__ */ jsxs("div", {
|
|
191
|
-
className: "flex shrink-0 items-center justify-end gap-
|
|
192
|
-
children: [
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
192
|
+
className: "flex shrink-0 items-center justify-end gap-1.5",
|
|
193
|
+
children: [
|
|
194
|
+
hasExtraActions && /* @__PURE__ */ jsxs(Fragment, { children: [extraActions, /* @__PURE__ */ jsx("div", {
|
|
195
|
+
className: "mx-0.5 hidden h-4 w-px bg-foreground/[0.1] sm:block",
|
|
196
|
+
"aria-hidden": true
|
|
197
|
+
})] }),
|
|
198
|
+
/* @__PURE__ */ jsxs("div", {
|
|
199
|
+
className: "relative shrink-0",
|
|
200
|
+
ref: columnPopover.ref,
|
|
201
|
+
children: [/* @__PURE__ */ jsx(ToolbarButton, {
|
|
202
|
+
onClick: () => columnPopover.setOpen(!columnPopover.open),
|
|
203
|
+
active: columnPopover.open,
|
|
204
|
+
title: strings.columns,
|
|
205
|
+
children: /* @__PURE__ */ jsx(Eye, { className: "h-3.5 w-3.5" })
|
|
206
|
+
}), columnPopover.open && /* @__PURE__ */ jsx(PopoverPanel, {
|
|
207
|
+
popoverRef: columnPopover.ref,
|
|
208
|
+
className: "right-0 left-auto",
|
|
209
|
+
children: /* @__PURE__ */ jsx(ColumnManager, {
|
|
210
|
+
columns,
|
|
211
|
+
visibility: state.columnVisibility,
|
|
212
|
+
onChange: updateVisibility,
|
|
213
|
+
strings,
|
|
214
|
+
dateDisplay: state.dateDisplay,
|
|
215
|
+
onDateDisplayChange: updateDateDisplay,
|
|
216
|
+
hasDateColumns
|
|
217
|
+
})
|
|
218
|
+
})]
|
|
219
|
+
}),
|
|
220
|
+
/* @__PURE__ */ jsx(ToolbarButton, {
|
|
221
|
+
onClick: exportCsv,
|
|
222
|
+
title: strings.export,
|
|
223
|
+
children: /* @__PURE__ */ jsx(DownloadSimple, { className: "h-3.5 w-3.5" })
|
|
224
|
+
})
|
|
225
|
+
]
|
|
218
226
|
})]
|
|
219
227
|
});
|
|
220
228
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"data-grid-toolbar.js","names":[],"sources":["../../../../src/components/data-grid/data-grid-toolbar.tsx"],"sourcesContent":["\"use client\";\n\nimport { cn } from \"@hexclave/ui\";\nimport {\n Check,\n DownloadSimple,\n Eye,\n EyeSlash,\n MagnifyingGlass,\n X,\n} from \"@phosphor-icons/react\";\nimport React, { useCallback, useMemo, useRef, useState } from \"react\";\nimport type {\n DataGridColumnDef,\n DataGridDateDisplay,\n DataGridStrings,\n DataGridToolbarContext,\n} from \"./types\";\n\n// ─── Popover primitive ───────────────────────────────────────────────\n\nfunction usePopover() {\n const [open, setOpen] = useState(false);\n const ref = useRef<HTMLDivElement>(null);\n\n React.useEffect(() => {\n if (!open) return;\n const handler = (e: MouseEvent) => {\n if (ref.current && !ref.current.contains(e.target as Node)) {\n setOpen(false);\n }\n };\n document.addEventListener(\"mousedown\", handler);\n return () => document.removeEventListener(\"mousedown\", handler);\n }, [open]);\n\n React.useEffect(() => {\n if (!open) return;\n const handler = (e: KeyboardEvent) => {\n if (e.key === \"Escape\") setOpen(false);\n };\n document.addEventListener(\"keydown\", handler);\n return () => document.removeEventListener(\"keydown\", handler);\n }, [open]);\n\n return { open, setOpen, ref };\n}\n\nfunction PopoverPanel({\n children,\n className,\n popoverRef,\n}: {\n children: React.ReactNode;\n className?: string;\n popoverRef: React.Ref<HTMLDivElement>;\n}) {\n return (\n <div\n ref={popoverRef}\n className={cn(\n \"absolute top-full left-0 mt-1 z-50\",\n \"bg-white text-popover-foreground rounded-xl shadow-lg dark:bg-popover\",\n \"ring-1 ring-black/[0.08] dark:ring-white/[0.1]\",\n \"backdrop-blur-xl\",\n className,\n )}\n >\n {children}\n </div>\n );\n}\n\n// ─── Quick search ────────────────────────────────────────────────────\n\nfunction QuickSearch({\n value,\n onChange,\n placeholder,\n}: {\n value: string;\n onChange: (value: string) => void;\n placeholder: string;\n}) {\n return (\n <div className=\"relative flex min-w-0 flex-1 items-center sm:flex-initial\">\n <MagnifyingGlass className=\"absolute left-2.5 h-3.5 w-3.5 text-muted-foreground/60 pointer-events-none\" />\n <input\n type=\"text\"\n className={cn(\n \"h-8 w-full sm:w-52 pl-8 pr-7 rounded-xl text-xs\",\n \"border border-black/[0.08] dark:border-white/[0.06]\",\n \"bg-white dark:bg-background shadow-sm ring-1 ring-black/[0.08] dark:ring-white/[0.06]\",\n \"placeholder:text-muted-foreground/50\",\n \"focus:outline-none focus:ring-1 focus:ring-foreground/[0.1]\",\n \"transition-all duration-150 hover:transition-none hover:ring-black/[0.12] dark:hover:ring-white/[0.1]\",\n )}\n placeholder={placeholder}\n value={value}\n onChange={(e) => onChange(e.target.value)}\n />\n {value && (\n <button\n className=\"absolute right-2 text-muted-foreground/40 hover:text-muted-foreground\"\n onClick={() => onChange(\"\")}\n aria-label=\"Clear search\"\n >\n <X className=\"h-3 w-3\" />\n </button>\n )}\n </div>\n );\n}\n\n// ─── Toolbar button ──────────────────────────────────────────────────\n\nfunction ToolbarButton({\n children,\n onClick,\n active,\n title,\n className: extraClassName,\n}: {\n children: React.ReactNode;\n onClick?: () => void;\n active?: boolean;\n title?: string;\n className?: string;\n}) {\n return (\n <button\n className={cn(\n \"relative flex items-center justify-center rounded-lg text-xs font-medium\",\n \"h-7 w-7\",\n \"transition-colors duration-75\",\n active\n ? \"bg-foreground/[0.06] text-foreground\"\n : \"text-muted-foreground hover:text-foreground hover:bg-foreground/[0.04]\",\n extraClassName,\n )}\n onClick={onClick}\n title={title}\n >\n {children}\n </button>\n );\n}\n\n// ─── Column manager ──────────────────────────────────────────────────\n\nfunction ColumnManager<TRow>({\n columns,\n visibility,\n onChange,\n strings,\n dateDisplay,\n onDateDisplayChange,\n hasDateColumns,\n}: {\n columns: readonly DataGridColumnDef<TRow>[];\n visibility: Record<string, boolean>;\n onChange: (visibility: Record<string, boolean>) => void;\n strings: DataGridStrings;\n dateDisplay: DataGridDateDisplay;\n onDateDisplayChange: (mode: DataGridDateDisplay) => void;\n hasDateColumns: boolean;\n}) {\n const hideableColumns = useMemo(\n () => columns.filter((c) => c.hideable !== false),\n [columns],\n );\n\n const toggleColumn = (id: string) => {\n const current = visibility[id] !== false;\n onChange({ ...visibility, [id]: !current });\n };\n\n const showAll = () => {\n const next = { ...visibility };\n for (const col of hideableColumns) next[col.id] = true;\n onChange(next);\n };\n\n const hideAll = () => {\n const next = { ...visibility };\n for (const col of hideableColumns) next[col.id] = false;\n onChange(next);\n };\n\n return (\n <div className=\"p-2 min-w-[240px] max-w-[300px]\">\n <div className=\"max-h-[280px] overflow-y-auto space-y-0.5\">\n {hideableColumns.map((col) => {\n const visible = visibility[col.id] !== false;\n return (\n <button\n key={col.id}\n className={cn(\n \"flex items-center gap-2 w-full px-2.5 py-1.5 rounded-lg text-xs\",\n \"hover:bg-foreground/[0.06] transition-colors duration-75\",\n visible ? \"text-foreground\" : \"text-muted-foreground/50\",\n )}\n onClick={() => toggleColumn(col.id)}\n >\n {visible ? (\n <Eye className=\"h-3.5 w-3.5 flex-shrink-0 text-blue-500\" />\n ) : (\n <EyeSlash className=\"h-3.5 w-3.5 flex-shrink-0\" />\n )}\n <span className=\"truncate text-left\">\n {typeof col.header === \"string\" ? col.header : col.id}\n </span>\n {visible && <Check className=\"h-3 w-3 ml-auto flex-shrink-0 text-blue-500\" />}\n </button>\n );\n })}\n </div>\n <div className=\"flex items-center gap-2 mt-2 pt-2 border-t border-foreground/[0.06]\">\n <button className=\"text-[10px] text-muted-foreground hover:text-foreground font-medium uppercase tracking-wider transition-colors duration-75\" onClick={showAll}>\n {strings.showAll}\n </button>\n <span className=\"text-muted-foreground/20\">|</span>\n <button className=\"text-[10px] text-muted-foreground hover:text-foreground font-medium uppercase tracking-wider transition-colors duration-75\" onClick={hideAll}>\n {strings.hideAll}\n </button>\n </div>\n\n {/* Date format toggle — only rendered when at least one column\n uses `type: \"date\"` or `\"dateTime\"`. Toggling writes to\n `state.dateDisplay` and the grid re-renders every date cell. */}\n {hasDateColumns && (\n <div className=\"mt-2 pt-2 border-t border-foreground/[0.06]\">\n <div className=\"flex items-center justify-between gap-2 px-1\">\n <span className=\"text-[10px] font-medium uppercase tracking-wider text-muted-foreground\">\n {strings.dateFormat}\n </span>\n <div className=\"inline-flex items-center gap-0.5 rounded-lg bg-zinc-100/90 p-0.5 ring-1 ring-black/[0.06] dark:bg-foreground/[0.04] dark:ring-white/[0.06]\">\n <button\n className={cn(\n \"px-2 py-0.5 rounded-md text-[11px] font-medium transition-colors duration-150 hover:transition-none\",\n dateDisplay === \"relative\"\n ? \"bg-white text-foreground shadow-sm ring-1 ring-black/[0.12] dark:bg-background dark:ring-white/[0.06]\"\n : \"text-muted-foreground hover:text-foreground hover:bg-white/50 dark:hover:bg-white/[0.06]\",\n )}\n onClick={() => onDateDisplayChange(\"relative\")}\n >\n {strings.dateFormatRelative}\n </button>\n <button\n className={cn(\n \"px-2 py-0.5 rounded-md text-[11px] font-medium transition-colors duration-150 hover:transition-none\",\n dateDisplay === \"absolute\"\n ? \"bg-white text-foreground shadow-sm ring-1 ring-black/[0.12] dark:bg-background dark:ring-white/[0.06]\"\n : \"text-muted-foreground hover:text-foreground hover:bg-white/50 dark:hover:bg-white/[0.06]\",\n )}\n onClick={() => onDateDisplayChange(\"absolute\")}\n >\n {strings.dateFormatAbsolute}\n </button>\n </div>\n </div>\n </div>\n )}\n </div>\n );\n}\n\n// ─── Main toolbar ────────────────────────────────────────────────────\n\nexport function DataGridToolbar<TRow>({\n ctx,\n extra,\n extraLeading,\n hideQuickSearch,\n}: {\n ctx: DataGridToolbarContext<TRow>;\n /** Extra content rendered inside the toolbar row, to the left of the\n * built-in columns / export actions. Use this to add table-specific\n * affordances (refresh, custom toggles, row counts) without giving up\n * the default actions. */\n extra?: React.ReactNode;\n /** Extra content rendered at the START of the toolbar row — occupies\n * the same position as the built-in quick search (after it, if the\n * quick search is visible). Use this together with `hideQuickSearch`\n * to fully replace the quick search with a custom input, e.g. an\n * AI-powered search bar. */\n extraLeading?: React.ReactNode;\n /** Whether to hide the built-in quick-search input. When `true`,\n * callers are expected to provide their own search UI via\n * `extraLeading`. */\n hideQuickSearch?: boolean;\n}) {\n const { state, onChange, columns, strings, exportCsv } = ctx;\n\n const columnPopover = usePopover();\n\n const updateVisibility = useCallback(\n (visibility: Record<string, boolean>) => {\n onChange((s) => ({ ...s, columnVisibility: visibility }));\n },\n [onChange],\n );\n\n const updateDateDisplay = useCallback(\n (mode: DataGridDateDisplay) => {\n onChange((s) => ({ ...s, dateDisplay: mode }));\n },\n [onChange],\n );\n\n const updateQuickSearch = useCallback(\n (value: string) => {\n onChange((s) => ({\n ...s,\n quickSearch: value,\n // Reset to first page whenever the search text changes,\n // otherwise you can end up on a page index that no longer\n // exists in the filtered / refetched result set.\n pagination: { ...s.pagination, pageIndex: 0 },\n }));\n },\n [onChange],\n );\n\n const hasDateColumns = useMemo(\n () => columns.some((c) => c.type === \"date\" || c.type === \"dateTime\"),\n [columns],\n );\n\n return (\n <div className=\"flex w-full min-w-0 flex-col gap-2 px-2.5 py-2.5 border-b border-foreground/[0.06] sm:flex-row sm:items-center sm:gap-2\">\n <div className=\"flex min-w-0 flex-1 flex-wrap items-center gap-2\">\n {!hideQuickSearch && (\n <QuickSearch\n value={state.quickSearch}\n onChange={updateQuickSearch}\n placeholder={strings.searchPlaceholder}\n />\n )}\n {extraLeading}\n {extra}\n </div>\n <div className=\"flex shrink-0 items-center justify-end gap-2\">\n <div className=\"relative shrink-0\" ref={columnPopover.ref}>\n <ToolbarButton\n onClick={() => columnPopover.setOpen(!columnPopover.open)}\n active={columnPopover.open}\n title={strings.columns}\n >\n <Eye className=\"h-3.5 w-3.5\" />\n </ToolbarButton>\n {columnPopover.open && (\n <PopoverPanel popoverRef={columnPopover.ref} className=\"right-0 left-auto\">\n <ColumnManager\n columns={columns}\n visibility={state.columnVisibility}\n onChange={updateVisibility}\n strings={strings}\n dateDisplay={state.dateDisplay}\n onDateDisplayChange={updateDateDisplay}\n hasDateColumns={hasDateColumns}\n />\n </PopoverPanel>\n )}\n </div>\n\n <ToolbarButton onClick={exportCsv} title={strings.export}>\n <DownloadSimple className=\"h-3.5 w-3.5\" />\n </ToolbarButton>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"data-grid-toolbar.js","names":[],"sources":["../../../../src/components/data-grid/data-grid-toolbar.tsx"],"sourcesContent":["\"use client\";\n\nimport { cn } from \"@hexclave/ui\";\nimport {\n Check,\n DownloadSimple,\n Eye,\n EyeSlash,\n MagnifyingGlass,\n X,\n} from \"@phosphor-icons/react\";\nimport React, { useCallback, useMemo, useRef, useState } from \"react\";\nimport type {\n DataGridColumnDef,\n DataGridDateDisplay,\n DataGridStrings,\n DataGridToolbarContext,\n} from \"./types\";\n\n// ─── Popover primitive ───────────────────────────────────────────────\n\nfunction usePopover() {\n const [open, setOpen] = useState(false);\n const ref = useRef<HTMLDivElement>(null);\n\n React.useEffect(() => {\n if (!open) return;\n const handler = (e: MouseEvent) => {\n if (ref.current && !ref.current.contains(e.target as Node)) {\n setOpen(false);\n }\n };\n document.addEventListener(\"mousedown\", handler);\n return () => document.removeEventListener(\"mousedown\", handler);\n }, [open]);\n\n React.useEffect(() => {\n if (!open) return;\n const handler = (e: KeyboardEvent) => {\n if (e.key === \"Escape\") setOpen(false);\n };\n document.addEventListener(\"keydown\", handler);\n return () => document.removeEventListener(\"keydown\", handler);\n }, [open]);\n\n return { open, setOpen, ref };\n}\n\nfunction PopoverPanel({\n children,\n className,\n popoverRef,\n}: {\n children: React.ReactNode;\n className?: string;\n popoverRef: React.Ref<HTMLDivElement>;\n}) {\n return (\n <div\n ref={popoverRef}\n className={cn(\n \"absolute top-full left-0 mt-1 z-50\",\n \"bg-white text-popover-foreground rounded-xl shadow-lg dark:bg-popover\",\n \"ring-1 ring-black/[0.08] dark:ring-white/[0.1]\",\n \"backdrop-blur-xl\",\n className,\n )}\n >\n {children}\n </div>\n );\n}\n\n// ─── Quick search ────────────────────────────────────────────────────\n\nfunction QuickSearch({\n value,\n onChange,\n placeholder,\n}: {\n value: string;\n onChange: (value: string) => void;\n placeholder: string;\n}) {\n return (\n <div className=\"relative flex min-w-0 flex-1 items-center sm:flex-initial\">\n <MagnifyingGlass className=\"absolute left-2.5 h-3.5 w-3.5 text-muted-foreground/60 pointer-events-none\" />\n <input\n type=\"text\"\n className={cn(\n \"h-8 w-full sm:w-52 pl-8 pr-7 rounded-xl text-xs\",\n \"border border-black/[0.08] dark:border-white/[0.06]\",\n \"bg-white dark:bg-background shadow-sm ring-1 ring-black/[0.08] dark:ring-white/[0.06]\",\n \"placeholder:text-muted-foreground/50\",\n \"focus:outline-none focus:ring-1 focus:ring-foreground/[0.1]\",\n \"transition-all duration-150 hover:transition-none hover:ring-black/[0.12] dark:hover:ring-white/[0.1]\",\n )}\n placeholder={placeholder}\n value={value}\n onChange={(e) => onChange(e.target.value)}\n />\n {value && (\n <button\n className=\"absolute right-2 text-muted-foreground/40 hover:text-muted-foreground\"\n onClick={() => onChange(\"\")}\n aria-label=\"Clear search\"\n >\n <X className=\"h-3 w-3\" />\n </button>\n )}\n </div>\n );\n}\n\n// ─── Toolbar button ──────────────────────────────────────────────────\n\nfunction ToolbarButton({\n children,\n onClick,\n active,\n title,\n className: extraClassName,\n}: {\n children: React.ReactNode;\n onClick?: () => void;\n active?: boolean;\n title?: string;\n className?: string;\n}) {\n return (\n <button\n className={cn(\n \"relative flex items-center justify-center rounded-lg text-xs font-medium\",\n \"h-7 w-7\",\n \"transition-colors duration-75\",\n active\n ? \"bg-foreground/[0.06] text-foreground\"\n : \"text-muted-foreground hover:text-foreground hover:bg-foreground/[0.04]\",\n extraClassName,\n )}\n onClick={onClick}\n title={title}\n >\n {children}\n </button>\n );\n}\n\n// ─── Column manager ──────────────────────────────────────────────────\n\nfunction ColumnManager<TRow>({\n columns,\n visibility,\n onChange,\n strings,\n dateDisplay,\n onDateDisplayChange,\n hasDateColumns,\n}: {\n columns: readonly DataGridColumnDef<TRow>[];\n visibility: Record<string, boolean>;\n onChange: (visibility: Record<string, boolean>) => void;\n strings: DataGridStrings;\n dateDisplay: DataGridDateDisplay;\n onDateDisplayChange: (mode: DataGridDateDisplay) => void;\n hasDateColumns: boolean;\n}) {\n const hideableColumns = useMemo(\n () => columns.filter((c) => c.hideable !== false),\n [columns],\n );\n\n const toggleColumn = (id: string) => {\n const current = visibility[id] !== false;\n onChange({ ...visibility, [id]: !current });\n };\n\n const showAll = () => {\n const next = { ...visibility };\n for (const col of hideableColumns) next[col.id] = true;\n onChange(next);\n };\n\n const hideAll = () => {\n const next = { ...visibility };\n for (const col of hideableColumns) next[col.id] = false;\n onChange(next);\n };\n\n return (\n <div className=\"p-2 min-w-[240px] max-w-[300px]\">\n <div className=\"max-h-[280px] overflow-y-auto space-y-0.5\">\n {hideableColumns.map((col) => {\n const visible = visibility[col.id] !== false;\n return (\n <button\n key={col.id}\n className={cn(\n \"flex items-center gap-2 w-full px-2.5 py-1.5 rounded-lg text-xs\",\n \"hover:bg-foreground/[0.06] transition-colors duration-75\",\n visible ? \"text-foreground\" : \"text-muted-foreground/50\",\n )}\n onClick={() => toggleColumn(col.id)}\n >\n {visible ? (\n <Eye className=\"h-3.5 w-3.5 flex-shrink-0 text-blue-500\" />\n ) : (\n <EyeSlash className=\"h-3.5 w-3.5 flex-shrink-0\" />\n )}\n <span className=\"truncate text-left\">\n {typeof col.header === \"string\" ? col.header : col.id}\n </span>\n {visible && <Check className=\"h-3 w-3 ml-auto flex-shrink-0 text-blue-500\" />}\n </button>\n );\n })}\n </div>\n <div className=\"flex items-center gap-2 mt-2 pt-2 border-t border-foreground/[0.06]\">\n <button className=\"text-[10px] text-muted-foreground hover:text-foreground font-medium uppercase tracking-wider transition-colors duration-75\" onClick={showAll}>\n {strings.showAll}\n </button>\n <span className=\"text-muted-foreground/20\">|</span>\n <button className=\"text-[10px] text-muted-foreground hover:text-foreground font-medium uppercase tracking-wider transition-colors duration-75\" onClick={hideAll}>\n {strings.hideAll}\n </button>\n </div>\n\n {/* Date format toggle — only rendered when at least one column\n uses `type: \"date\"` or `\"dateTime\"`. Toggling writes to\n `state.dateDisplay` and the grid re-renders every date cell. */}\n {hasDateColumns && (\n <div className=\"mt-2 pt-2 border-t border-foreground/[0.06]\">\n <div className=\"flex items-center justify-between gap-2 px-1\">\n <span className=\"text-[10px] font-medium uppercase tracking-wider text-muted-foreground\">\n {strings.dateFormat}\n </span>\n <div className=\"inline-flex items-center gap-0.5 rounded-lg bg-zinc-100/90 p-0.5 ring-1 ring-black/[0.06] dark:bg-foreground/[0.04] dark:ring-white/[0.06]\">\n <button\n className={cn(\n \"px-2 py-0.5 rounded-md text-[11px] font-medium transition-colors duration-150 hover:transition-none\",\n dateDisplay === \"relative\"\n ? \"bg-white text-foreground shadow-sm ring-1 ring-black/[0.12] dark:bg-background dark:ring-white/[0.06]\"\n : \"text-muted-foreground hover:text-foreground hover:bg-white/50 dark:hover:bg-white/[0.06]\",\n )}\n onClick={() => onDateDisplayChange(\"relative\")}\n >\n {strings.dateFormatRelative}\n </button>\n <button\n className={cn(\n \"px-2 py-0.5 rounded-md text-[11px] font-medium transition-colors duration-150 hover:transition-none\",\n dateDisplay === \"absolute\"\n ? \"bg-white text-foreground shadow-sm ring-1 ring-black/[0.12] dark:bg-background dark:ring-white/[0.06]\"\n : \"text-muted-foreground hover:text-foreground hover:bg-white/50 dark:hover:bg-white/[0.06]\",\n )}\n onClick={() => onDateDisplayChange(\"absolute\")}\n >\n {strings.dateFormatAbsolute}\n </button>\n </div>\n </div>\n </div>\n )}\n </div>\n );\n}\n\n// ─── Main toolbar ────────────────────────────────────────────────────\n\nexport function DataGridToolbar<TRow>({\n ctx,\n extra,\n extraLeading,\n extraActions,\n hideQuickSearch,\n}: {\n ctx: DataGridToolbarContext<TRow>;\n /** Extra content in the leading cluster, after search / `extraLeading`\n * (filters, status badges, row counts). Stays with the search side so\n * it doesn't compete with the trailing icon actions. */\n extra?: React.ReactNode;\n /** Extra content rendered at the START of the toolbar row — occupies\n * the same position as the built-in quick search (after it, if the\n * quick search is visible). Use this together with `hideQuickSearch`\n * to fully replace the quick search with a custom input, e.g. an\n * AI-powered search bar. */\n extraLeading?: React.ReactNode;\n /** Extra content in the trailing action cluster, immediately before\n * the built-in columns / export buttons (e.g. a labeled Refresh).\n * When present, a hairline divider separates it from those icons. */\n extraActions?: React.ReactNode;\n /** Whether to hide the built-in quick-search input. When `true`,\n * callers are expected to provide their own search UI via\n * `extraLeading`. */\n hideQuickSearch?: boolean;\n}) {\n const { state, onChange, columns, strings, exportCsv } = ctx;\n\n const columnPopover = usePopover();\n\n const updateVisibility = useCallback(\n (visibility: Record<string, boolean>) => {\n onChange((s) => ({ ...s, columnVisibility: visibility }));\n },\n [onChange],\n );\n\n const updateDateDisplay = useCallback(\n (mode: DataGridDateDisplay) => {\n onChange((s) => ({ ...s, dateDisplay: mode }));\n },\n [onChange],\n );\n\n const updateQuickSearch = useCallback(\n (value: string) => {\n onChange((s) => ({\n ...s,\n quickSearch: value,\n // Reset to first page whenever the search text changes,\n // otherwise you can end up on a page index that no longer\n // exists in the filtered / refetched result set.\n pagination: { ...s.pagination, pageIndex: 0 },\n }));\n },\n [onChange],\n );\n\n const hasDateColumns = useMemo(\n () => columns.some((c) => c.type === \"date\" || c.type === \"dateTime\"),\n [columns],\n );\n const hasExtraActions = React.Children.toArray(extraActions).length > 0;\n\n return (\n <div className=\"flex w-full min-w-0 flex-col gap-2 px-2.5 py-2.5 pr-3 border-b border-foreground/[0.06] sm:flex-row sm:items-center sm:gap-2\">\n <div className=\"flex min-w-0 flex-1 flex-wrap items-center gap-2\">\n {!hideQuickSearch && (\n <QuickSearch\n value={state.quickSearch}\n onChange={updateQuickSearch}\n placeholder={strings.searchPlaceholder}\n />\n )}\n {extraLeading}\n {extra}\n </div>\n <div className=\"flex shrink-0 items-center justify-end gap-1.5\">\n {hasExtraActions && (\n <>\n {extraActions}\n <div\n className=\"mx-0.5 hidden h-4 w-px bg-foreground/[0.1] sm:block\"\n aria-hidden\n />\n </>\n )}\n <div className=\"relative shrink-0\" ref={columnPopover.ref}>\n <ToolbarButton\n onClick={() => columnPopover.setOpen(!columnPopover.open)}\n active={columnPopover.open}\n title={strings.columns}\n >\n <Eye className=\"h-3.5 w-3.5\" />\n </ToolbarButton>\n {columnPopover.open && (\n <PopoverPanel popoverRef={columnPopover.ref} className=\"right-0 left-auto\">\n <ColumnManager\n columns={columns}\n visibility={state.columnVisibility}\n onChange={updateVisibility}\n strings={strings}\n dateDisplay={state.dateDisplay}\n onDateDisplayChange={updateDateDisplay}\n hasDateColumns={hasDateColumns}\n />\n </PopoverPanel>\n )}\n </div>\n\n <ToolbarButton onClick={exportCsv} title={strings.export}>\n <DownloadSimple className=\"h-3.5 w-3.5\" />\n </ToolbarButton>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"data-grid.d.ts","names":[],"sources":["../../../../src/components/data-grid/data-grid.tsx"],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"data-grid.d.ts","names":[],"sources":["../../../../src/components/data-grid/data-grid.tsx"],"mappings":";;;iBA+EgB,mCAAA,CAAoC,MAA0B,EAAlB,WAAW;;AAAvE;;;;AAA8E;AA6mB9E;;;;;;;;;;;AAAyD;;;;;;;;;;;;;;;;;;;;;iBAAzC,QAAA,OAAe,KAAA,EAAO,aAAa,CAAC,IAAA,gCAAK,GAAA,CAAA,OAAA"}
|
|
@@ -14,6 +14,7 @@ import { DataGridExportDialog } from "./data-grid-export-dialog.js";
|
|
|
14
14
|
import { DataGridToolbar } from "./data-grid-toolbar.js";
|
|
15
15
|
//#region src/components/data-grid/data-grid.tsx
|
|
16
16
|
const DEFAULT_INFINITE_MAX_HEIGHT = "calc(100dvh - 16rem)";
|
|
17
|
+
const DATA_GRID_SCROLLBAR_CLASS_NAME = cn("[&::-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]");
|
|
17
18
|
function getEventTargetElement(target) {
|
|
18
19
|
if (target instanceof Element) return target;
|
|
19
20
|
if (target instanceof Node) return target.parentElement;
|
|
@@ -247,6 +248,20 @@ function hashStringToInt(value) {
|
|
|
247
248
|
for (let i = 0; i < value.length; i++) hash = (hash << 5) - hash + value.charCodeAt(i) | 0;
|
|
248
249
|
return Math.abs(hash);
|
|
249
250
|
}
|
|
251
|
+
/**
|
|
252
|
+
* Column flex fractions used when the grid is loading before any schema is
|
|
253
|
+
* known (e.g. QueryDataGrid discovers columns from the first result page).
|
|
254
|
+
* Without these, `SkeletonRow` would render zero cells and the body looks
|
|
255
|
+
* like an empty black void instead of a shimmer.
|
|
256
|
+
*/
|
|
257
|
+
const SCHEMA_PENDING_COL_FRACTIONS = [
|
|
258
|
+
.18,
|
|
259
|
+
.16,
|
|
260
|
+
.34,
|
|
261
|
+
.2,
|
|
262
|
+
.12
|
|
263
|
+
];
|
|
264
|
+
const SCHEMA_PENDING_SKELETON_ROWS = 10;
|
|
250
265
|
function SkeletonRow({ columns, height, showCheckbox }) {
|
|
251
266
|
return /* @__PURE__ */ jsxs("div", {
|
|
252
267
|
className: "flex",
|
|
@@ -266,6 +281,46 @@ function SkeletonRow({ columns, height, showCheckbox }) {
|
|
|
266
281
|
}, col.id))]
|
|
267
282
|
});
|
|
268
283
|
}
|
|
284
|
+
/** Sticky header shimmer when columns haven't been discovered yet. */
|
|
285
|
+
function SchemaPendingHeaderCells({ showCheckbox }) {
|
|
286
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [showCheckbox && /* @__PURE__ */ jsx("div", {
|
|
287
|
+
className: "flex shrink-0 items-center justify-center border-r border-foreground/[0.04]",
|
|
288
|
+
style: { width: 44 }
|
|
289
|
+
}), SCHEMA_PENDING_COL_FRACTIONS.map((frac, colIdx) => /* @__PURE__ */ jsx("div", {
|
|
290
|
+
className: "flex items-center px-3 border-r border-black/[0.04] dark:border-white/[0.04] last:border-r-0",
|
|
291
|
+
style: { width: `${frac * 100}%` },
|
|
292
|
+
children: /* @__PURE__ */ jsx(DesignSkeleton, {
|
|
293
|
+
className: "h-3 rounded-md",
|
|
294
|
+
style: { width: `${45 + colIdx * 13 % 30}%` }
|
|
295
|
+
})
|
|
296
|
+
}, colIdx))] });
|
|
297
|
+
}
|
|
298
|
+
/** Full-width body shimmer when columns haven't been discovered yet. */
|
|
299
|
+
function SchemaPendingSkeleton({ rowHeight, showCheckbox }) {
|
|
300
|
+
return /* @__PURE__ */ jsx("div", {
|
|
301
|
+
className: "w-full",
|
|
302
|
+
"data-data-grid-schema-pending-skeleton": "",
|
|
303
|
+
role: "status",
|
|
304
|
+
"aria-label": "Loading",
|
|
305
|
+
children: Array.from({ length: SCHEMA_PENDING_SKELETON_ROWS }).map((_, rowIdx) => /* @__PURE__ */ jsxs("div", {
|
|
306
|
+
className: "flex w-full border-b border-black/[0.04] dark:border-white/[0.04]",
|
|
307
|
+
style: { height: rowHeight },
|
|
308
|
+
role: "row",
|
|
309
|
+
children: [showCheckbox && /* @__PURE__ */ jsx("div", {
|
|
310
|
+
className: "flex shrink-0 items-center justify-center border-r border-black/[0.04] dark:border-white/[0.04]",
|
|
311
|
+
style: { width: 44 },
|
|
312
|
+
children: /* @__PURE__ */ jsx(DesignSkeleton, { className: "h-4 w-4 rounded" })
|
|
313
|
+
}), SCHEMA_PENDING_COL_FRACTIONS.map((frac, colIdx) => /* @__PURE__ */ jsx("div", {
|
|
314
|
+
className: "flex items-center px-3 border-r border-black/[0.04] dark:border-white/[0.04] last:border-r-0",
|
|
315
|
+
style: { width: `${frac * 100}%` },
|
|
316
|
+
children: /* @__PURE__ */ jsx(DesignSkeleton, {
|
|
317
|
+
className: "h-3.5 rounded-md",
|
|
318
|
+
style: { width: `${50 + (rowIdx * 7 + colIdx * 19) % 40}%` }
|
|
319
|
+
})
|
|
320
|
+
}, colIdx))]
|
|
321
|
+
}, rowIdx))
|
|
322
|
+
});
|
|
323
|
+
}
|
|
269
324
|
function SelectionCheckbox({ checked, indeterminate, onChange, ariaLabel, title }) {
|
|
270
325
|
const Icon = indeterminate ? MinusSquare : checked ? CheckSquare : Square;
|
|
271
326
|
return /* @__PURE__ */ jsx("button", {
|
|
@@ -430,7 +485,7 @@ function DefaultFooter({ ctx, pagination, onChange }) {
|
|
|
430
485
|
* - `renderCell` must be a pure function — no React hooks inside.
|
|
431
486
|
*/
|
|
432
487
|
function DataGrid(props) {
|
|
433
|
-
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", exportOptions, strings: stringsOverride, className, onRowClick, onRowDoubleClick, onSelectionChange, onSortChange, onColumnResize, onColumnVisibilityChange } = props;
|
|
488
|
+
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, horizontalScrollbarPosition = "bottom", toolbar, toolbarExtra, emptyState, loadingState, footer, footerExtra, exportFilename = "export", exportOptions, strings: stringsOverride, className, onRowClick, onRowDoubleClick, onSelectionChange, onSortChange, onColumnResize, onColumnVisibilityChange } = props;
|
|
434
489
|
const isDynamicRowHeight = rowHeightProp === "auto";
|
|
435
490
|
const fixedRowHeight = isDynamicRowHeight ? void 0 : rowHeightProp;
|
|
436
491
|
const estimatedRowHeight = estimatedRowHeightProp ?? fixedRowHeight ?? 44;
|
|
@@ -715,11 +770,31 @@ function DataGrid(props) {
|
|
|
715
770
|
observer.disconnect();
|
|
716
771
|
};
|
|
717
772
|
}, []);
|
|
718
|
-
const
|
|
773
|
+
const isSyncingHorizontalScrollRef = useRef(false);
|
|
774
|
+
const syncHorizontalScroll = useCallback((source) => {
|
|
719
775
|
const body = scrollContainerRef.current;
|
|
720
776
|
const header = headerScrollRef.current;
|
|
721
|
-
if (body
|
|
777
|
+
if (body == null || header == null || isSyncingHorizontalScrollRef.current) return;
|
|
778
|
+
const from = source === "header" ? header : body;
|
|
779
|
+
const to = source === "header" ? body : header;
|
|
780
|
+
if (to.scrollLeft === from.scrollLeft) return;
|
|
781
|
+
isSyncingHorizontalScrollRef.current = true;
|
|
782
|
+
to.scrollLeft = from.scrollLeft;
|
|
783
|
+
isSyncingHorizontalScrollRef.current = false;
|
|
722
784
|
}, []);
|
|
785
|
+
const handleBodyScroll = useCallback(() => {
|
|
786
|
+
syncHorizontalScroll("body");
|
|
787
|
+
}, [syncHorizontalScroll]);
|
|
788
|
+
const handleHeaderScroll = useCallback(() => {
|
|
789
|
+
syncHorizontalScroll("header");
|
|
790
|
+
}, [syncHorizontalScroll]);
|
|
791
|
+
const horizontalScrollbarAtTop = horizontalScrollbarPosition === "top";
|
|
792
|
+
const handleBodyWheel = useCallback((event) => {
|
|
793
|
+
if (!horizontalScrollbarAtTop || event.deltaX === 0) return;
|
|
794
|
+
const header = headerScrollRef.current;
|
|
795
|
+
if (header == null) return;
|
|
796
|
+
header.scrollLeft += event.deltaX;
|
|
797
|
+
}, [horizontalScrollbarAtTop]);
|
|
723
798
|
const toolbarCtx = useMemo(() => ({
|
|
724
799
|
state,
|
|
725
800
|
onChange,
|
|
@@ -798,15 +873,17 @@ function DataGrid(props) {
|
|
|
798
873
|
children: /* @__PURE__ */ jsx("div", { className: "h-full w-1/3 bg-blue-500/60 rounded-full animate-pulse" })
|
|
799
874
|
}), /* @__PURE__ */ jsx("div", {
|
|
800
875
|
ref: headerScrollRef,
|
|
801
|
-
className: "w-full min-w-0 shrink-0
|
|
802
|
-
|
|
876
|
+
className: cn("w-full min-w-0 shrink-0 border-b border-foreground/[0.06]", horizontalScrollbarAtTop ? cn("overflow-x-auto overflow-y-hidden", DATA_GRID_SCROLLBAR_CLASS_NAME) : "overflow-hidden"),
|
|
877
|
+
onScroll: horizontalScrollbarAtTop ? handleHeaderScroll : void 0,
|
|
878
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
803
879
|
className: "flex",
|
|
804
880
|
style: {
|
|
805
881
|
height: headerHeight,
|
|
806
|
-
minWidth: totalContentWidth
|
|
882
|
+
minWidth: totalContentWidth || void 0,
|
|
883
|
+
width: visibleColumns.length === 0 ? "100%" : void 0
|
|
807
884
|
},
|
|
808
885
|
role: "row",
|
|
809
|
-
children: [selectionMode !== "none" && /* @__PURE__ */ jsx("div", {
|
|
886
|
+
children: isLoading && visibleColumns.length === 0 ? /* @__PURE__ */ jsx(SchemaPendingHeaderCells, { showCheckbox: selectionMode !== "none" }) : /* @__PURE__ */ jsxs(Fragment, { children: [selectionMode !== "none" && /* @__PURE__ */ jsx("div", {
|
|
810
887
|
className: "flex items-center justify-center border-r border-foreground/[0.04]",
|
|
811
888
|
style: { width: 44 },
|
|
812
889
|
children: selectionMode === "multiple" && /* @__PURE__ */ jsx(SelectionCheckbox, {
|
|
@@ -824,15 +901,16 @@ function DataGrid(props) {
|
|
|
824
901
|
col,
|
|
825
902
|
resizable
|
|
826
903
|
}, col.id);
|
|
827
|
-
})]
|
|
904
|
+
})] })
|
|
828
905
|
})
|
|
829
906
|
})]
|
|
830
907
|
})]
|
|
831
908
|
}),
|
|
832
909
|
/* @__PURE__ */ jsx("div", {
|
|
833
910
|
ref: scrollContainerRef,
|
|
834
|
-
className: cn("relative z-0 w-full min-w-0
|
|
911
|
+
className: cn("relative z-0 w-full min-w-0 bg-transparent", horizontalScrollbarAtTop ? "overflow-y-auto overflow-x-hidden" : "overflow-auto", isBounded ? "min-h-0 flex-1" : "flex-none", DATA_GRID_SCROLLBAR_CLASS_NAME),
|
|
835
912
|
onScroll: handleBodyScroll,
|
|
913
|
+
onWheel: horizontalScrollbarAtTop ? handleBodyWheel : void 0,
|
|
836
914
|
children: /* @__PURE__ */ jsxs("div", {
|
|
837
915
|
ref: rowsClipRef,
|
|
838
916
|
"data-data-grid-rows-clip": "",
|
|
@@ -843,12 +921,18 @@ function DataGrid(props) {
|
|
|
843
921
|
},
|
|
844
922
|
children: [
|
|
845
923
|
isLoading && /* @__PURE__ */ jsx("div", {
|
|
846
|
-
style: {
|
|
847
|
-
|
|
924
|
+
style: {
|
|
925
|
+
minWidth: totalContentWidth || void 0,
|
|
926
|
+
width: visibleColumns.length === 0 ? "100%" : void 0
|
|
927
|
+
},
|
|
928
|
+
children: loadingState ?? (visibleColumns.length === 0 ? /* @__PURE__ */ jsx(SchemaPendingSkeleton, {
|
|
929
|
+
rowHeight: estimatedRowHeight,
|
|
930
|
+
showCheckbox: selectionMode !== "none"
|
|
931
|
+
}) : Array.from({ length: 8 }).map((_, i) => /* @__PURE__ */ jsx(SkeletonRow, {
|
|
848
932
|
columns: visibleColumns,
|
|
849
933
|
height: estimatedRowHeight,
|
|
850
934
|
showCheckbox: selectionMode !== "none"
|
|
851
|
-
}, i))
|
|
935
|
+
}, i)))
|
|
852
936
|
}),
|
|
853
937
|
!isLoading && rows.length === 0 && /* @__PURE__ */ jsx("div", {
|
|
854
938
|
className: "flex items-center justify-center py-16 text-sm text-muted-foreground",
|