@nexub/react 5.3.0 → 5.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/accessibility.json +21 -1
- package/dist/components/DataTable/DataTable.d.ts +29 -2
- package/dist/components/DataTable/DataTable.js +255 -51
- package/dist/components/DataTable/index.d.ts +1 -1
- package/dist/components/DataTableVirtual/DataTableVirtual.d.ts +19 -0
- package/dist/components/DataTableVirtual/DataTableVirtual.js +37 -0
- package/dist/components/DataTableVirtual/index.d.ts +2 -0
- package/dist/components/DataTableVirtual/index.js +4 -0
- package/dist/components/DateRangePicker/DateRangePicker.d.ts +17 -0
- package/dist/components/DateRangePicker/DateRangePicker.js +123 -71
- package/dist/components/DateRangePicker/index.d.ts +2 -1
- package/dist/components/DateRangePicker/index.js +3 -1
- package/dist/components/FilterBuilder/FilterBuilder.d.ts +50 -0
- package/dist/components/FilterBuilder/FilterBuilder.js +164 -0
- package/dist/components/FilterBuilder/index.d.ts +2 -0
- package/dist/components/FilterBuilder/index.js +8 -0
- package/dist/components/SavedViews/SavedViews.d.ts +26 -0
- package/dist/components/SavedViews/SavedViews.js +65 -0
- package/dist/components/SavedViews/index.d.ts +2 -0
- package/dist/components/SavedViews/index.js +4 -0
- package/dist/index.d.ts +7 -2
- package/dist/index.js +10 -0
- package/dist/styles.css +1 -1
- package/package.json +13 -6
package/accessibility.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "5.
|
|
2
|
+
"version": "5.5.0",
|
|
3
3
|
"standard": "WCAG 2.2 Level AA",
|
|
4
4
|
"method": {
|
|
5
5
|
"automated": "axe-core rules tagged wcag2a, wcag2aa, wcag21a, wcag21aa and wcag22aa, run on every Storybook story in light and dark on every pull request; a new violation fails the build.",
|
|
@@ -399,6 +399,17 @@
|
|
|
399
399
|
"keyboard": [],
|
|
400
400
|
"screenReader": []
|
|
401
401
|
},
|
|
402
|
+
{
|
|
403
|
+
"slug": "filter-builder",
|
|
404
|
+
"name": "Filter Builder",
|
|
405
|
+
"package": "@nexub/react",
|
|
406
|
+
"automated": "pass",
|
|
407
|
+
"exceptions": [],
|
|
408
|
+
"keyboard": [],
|
|
409
|
+
"screenReader": [
|
|
410
|
+
"Field, condition and value controls are labelled; remove buttons say what they remove."
|
|
411
|
+
]
|
|
412
|
+
},
|
|
402
413
|
{
|
|
403
414
|
"slug": "filter-chip",
|
|
404
415
|
"name": "Filter Chip",
|
|
@@ -839,6 +850,15 @@
|
|
|
839
850
|
"keyboard": [],
|
|
840
851
|
"screenReader": []
|
|
841
852
|
},
|
|
853
|
+
{
|
|
854
|
+
"slug": "saved-views",
|
|
855
|
+
"name": "Saved Views",
|
|
856
|
+
"package": "@nexub/react",
|
|
857
|
+
"automated": "pass",
|
|
858
|
+
"exceptions": [],
|
|
859
|
+
"keyboard": [],
|
|
860
|
+
"screenReader": []
|
|
861
|
+
},
|
|
842
862
|
{
|
|
843
863
|
"slug": "scroll-area",
|
|
844
864
|
"name": "Scroll Area",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import { type ColumnDef, type RowSelectionState, type RowData, type VisibilityState } from '@tanstack/react-table';
|
|
2
|
+
import { type ColumnDef, type RowSelectionState, type RowData, type VisibilityState, type ColumnSizingState, type ColumnOrderState, type ColumnPinningState } from '@tanstack/react-table';
|
|
3
3
|
declare module '@tanstack/react-table' {
|
|
4
4
|
interface ColumnMeta<TData extends RowData, TValue> {
|
|
5
5
|
/** Horizontal alignment of the header and cells. Use 'end' for numbers. @default 'start' */
|
|
@@ -9,6 +9,18 @@ declare module '@tanstack/react-table' {
|
|
|
9
9
|
/** A DataTable column: TanStack's `ColumnDef`, with `meta.align` for numeric columns. */
|
|
10
10
|
export type DataTableColumn<TData> = ColumnDef<TData, unknown>;
|
|
11
11
|
export type DataTableDensity = 'comfortable' | 'compact';
|
|
12
|
+
/** Which rows to render, and the space to leave above and below them. */
|
|
13
|
+
export interface DataTableRowWindow {
|
|
14
|
+
indexes: number[];
|
|
15
|
+
before: number;
|
|
16
|
+
after: number;
|
|
17
|
+
/** Ref for each rendered row, to measure its real height. */
|
|
18
|
+
measure?: (el: HTMLElement | null) => void;
|
|
19
|
+
/** True when rows are windowed: rows get aria-rowindex, the table aria-rowcount. */
|
|
20
|
+
virtual?: boolean;
|
|
21
|
+
}
|
|
22
|
+
/** A hook choosing the rendered rows. DataTableVirtual passes one; the default renders all. */
|
|
23
|
+
export type DataTableRowWindowHook = (count: number, getScrollElement: () => HTMLElement | null) => DataTableRowWindow;
|
|
12
24
|
export interface DataTableProps<TData> extends React.HTMLAttributes<HTMLDivElement> {
|
|
13
25
|
columns: DataTableColumn<TData>[];
|
|
14
26
|
/** Names the table for assistive tech (and is shown below it unless `captionHidden`). */
|
|
@@ -39,8 +51,23 @@ export interface DataTableProps<TData> extends React.HTMLAttributes<HTMLDivEleme
|
|
|
39
51
|
viewOptions?: boolean;
|
|
40
52
|
/** Content at the start of the row above the table (filters, a title). */
|
|
41
53
|
toolbar?: React.ReactNode;
|
|
54
|
+
/** Drag a header's edge, or focus it and press Arrow keys, to resize columns. */
|
|
55
|
+
enableColumnResizing?: boolean;
|
|
56
|
+
columnSizing?: ColumnSizingState;
|
|
57
|
+
onColumnSizingChange?: (sizing: ColumnSizingState) => void;
|
|
58
|
+
/** Drag headers to reorder columns, or press Alt+Arrow on a focused header. */
|
|
59
|
+
enableColumnReordering?: boolean;
|
|
60
|
+
columnOrder?: ColumnOrderState;
|
|
61
|
+
onColumnOrderChange?: (order: ColumnOrderState) => void;
|
|
62
|
+
/** Columns that stay in view while the rest scroll sideways: `{ left: ['name'], right: ['actions'] }`. */
|
|
63
|
+
columnPinning?: ColumnPinningState;
|
|
64
|
+
onColumnPinningChange?: (pinning: ColumnPinningState) => void;
|
|
65
|
+
/** Cap the table's height. Rows scroll and the header stays in view. */
|
|
66
|
+
maxHeight?: number | string;
|
|
67
|
+
/** Rows to render; set by DataTableVirtual. */
|
|
68
|
+
rowWindow?: DataTableRowWindowHook;
|
|
42
69
|
pagination?: {
|
|
43
70
|
pageSize?: number;
|
|
44
71
|
};
|
|
45
72
|
}
|
|
46
|
-
export declare function DataTable<TData>({ columns, data, enableSorting, enableRowSelection, selectionColumn, rowSelection: rowSelectionProp, onRowSelectionChange, getRowId, bulkActions, density: densityProp, defaultDensity, onDensityChange, columnVisibility: columnVisibilityProp, onColumnVisibilityChange, viewOptions, toolbar, pagination, caption, captionHidden, className, 'aria-label': ariaLabel, 'aria-labelledby': ariaLabelledBy, ...props }: DataTableProps<TData>): React.JSX.Element;
|
|
73
|
+
export declare function DataTable<TData>({ columns, data, enableSorting, enableRowSelection, selectionColumn, rowSelection: rowSelectionProp, onRowSelectionChange, getRowId, bulkActions, density: densityProp, defaultDensity, onDensityChange, columnVisibility: columnVisibilityProp, onColumnVisibilityChange, viewOptions, toolbar, enableColumnResizing, columnSizing: columnSizingProp, onColumnSizingChange, enableColumnReordering, columnOrder: columnOrderProp, onColumnOrderChange, columnPinning: columnPinningProp, onColumnPinningChange, maxHeight, rowWindow, pagination, caption, captionHidden, className, 'aria-label': ariaLabel, 'aria-labelledby': ariaLabelledBy, ...props }: DataTableProps<TData>): React.JSX.Element;
|
|
@@ -14,6 +14,7 @@ import { cn } from "../../utils/cn.js";
|
|
|
14
14
|
import { disabledContainer, pressable } from "../../utils/control.js";
|
|
15
15
|
import { floatingSurface } from "../../utils/surface.js";
|
|
16
16
|
import { useControllableState } from "../../hooks/useControllableState.js";
|
|
17
|
+
import { useIsomorphicLayoutEffect } from "../../hooks/useIsomorphicLayoutEffect.js";
|
|
17
18
|
import { useLocale, useNexubMessages } from "../NexubProvider/NexubProvider.js";
|
|
18
19
|
import { Table } from "../Table/Table.js";
|
|
19
20
|
import { Checkbox } from "../Checkbox/Checkbox.js";
|
|
@@ -84,6 +85,23 @@ const TableCell = React.forwardRef(({ className, ...props }, ref) => /* @__PURE_
|
|
|
84
85
|
}
|
|
85
86
|
));
|
|
86
87
|
TableCell.displayName = "DataTable.TableCell";
|
|
88
|
+
function Spacer({ height }) {
|
|
89
|
+
return /* @__PURE__ */ jsx("tr", { "aria-hidden": "true", className: "border-0", children: /* @__PURE__ */ jsx(
|
|
90
|
+
"td",
|
|
91
|
+
{
|
|
92
|
+
ref: (el) => {
|
|
93
|
+
if (el) el.style.height = px(height);
|
|
94
|
+
},
|
|
95
|
+
className: "p-0"
|
|
96
|
+
}
|
|
97
|
+
) });
|
|
98
|
+
}
|
|
99
|
+
const useAllRows = (count) => ({
|
|
100
|
+
indexes: Array.from({ length: count }, (_, i) => i),
|
|
101
|
+
before: 0,
|
|
102
|
+
after: 0
|
|
103
|
+
});
|
|
104
|
+
const px = (n) => typeof n === "number" ? `${n}px` : n;
|
|
87
105
|
const apply = (u, prev) => typeof u === "function" ? u(prev) : u;
|
|
88
106
|
const columnLabel = (c) => typeof c.columnDef.header === "string" ? c.columnDef.header : c.id;
|
|
89
107
|
function DataTable({
|
|
@@ -103,6 +121,16 @@ function DataTable({
|
|
|
103
121
|
onColumnVisibilityChange,
|
|
104
122
|
viewOptions = false,
|
|
105
123
|
toolbar,
|
|
124
|
+
enableColumnResizing = false,
|
|
125
|
+
columnSizing: columnSizingProp,
|
|
126
|
+
onColumnSizingChange,
|
|
127
|
+
enableColumnReordering = false,
|
|
128
|
+
columnOrder: columnOrderProp,
|
|
129
|
+
onColumnOrderChange,
|
|
130
|
+
columnPinning: columnPinningProp,
|
|
131
|
+
onColumnPinningChange,
|
|
132
|
+
maxHeight,
|
|
133
|
+
rowWindow,
|
|
106
134
|
pagination,
|
|
107
135
|
caption,
|
|
108
136
|
captionHidden = false,
|
|
@@ -112,7 +140,7 @@ function DataTable({
|
|
|
112
140
|
...props
|
|
113
141
|
}) {
|
|
114
142
|
const messages = useNexubMessages();
|
|
115
|
-
const { locale } = useLocale();
|
|
143
|
+
const { locale, dir } = useLocale();
|
|
116
144
|
const [sorting, setSorting] = React.useState([]);
|
|
117
145
|
const [rowSelection = {}, setRowSelection] = useControllableState({
|
|
118
146
|
value: rowSelectionProp,
|
|
@@ -129,11 +157,33 @@ function DataTable({
|
|
|
129
157
|
defaultValue: {},
|
|
130
158
|
onChange: onColumnVisibilityChange
|
|
131
159
|
});
|
|
160
|
+
const [columnSizing = {}, setColumnSizing] = useControllableState({
|
|
161
|
+
value: columnSizingProp,
|
|
162
|
+
defaultValue: {},
|
|
163
|
+
onChange: onColumnSizingChange
|
|
164
|
+
});
|
|
165
|
+
const [columnOrder = [], setColumnOrder] = useControllableState({
|
|
166
|
+
value: columnOrderProp,
|
|
167
|
+
defaultValue: [],
|
|
168
|
+
onChange: onColumnOrderChange
|
|
169
|
+
});
|
|
170
|
+
const [pinningIn = {}, setColumnPinning] = useControllableState({
|
|
171
|
+
value: columnPinningProp,
|
|
172
|
+
defaultValue: {},
|
|
173
|
+
onChange: onColumnPinningChange
|
|
174
|
+
});
|
|
175
|
+
const [announcement, setAnnouncement] = React.useState("");
|
|
176
|
+
const tableRef = React.useRef(null);
|
|
177
|
+
const dragged = React.useRef(null);
|
|
132
178
|
const [paginationState, setPaginationState] = React.useState({
|
|
133
179
|
pageIndex: 0,
|
|
134
180
|
pageSize: pagination?.pageSize ?? 20
|
|
135
181
|
});
|
|
136
182
|
const withSelectColumn = enableRowSelection && selectionColumn;
|
|
183
|
+
const columnPinning = React.useMemo(
|
|
184
|
+
() => withSelectColumn && pinningIn.left?.length && !pinningIn.left.includes("__select") ? { ...pinningIn, left: ["__select", ...pinningIn.left] } : pinningIn,
|
|
185
|
+
[withSelectColumn, pinningIn]
|
|
186
|
+
);
|
|
137
187
|
const allColumns = React.useMemo(
|
|
138
188
|
() => withSelectColumn ? [
|
|
139
189
|
{
|
|
@@ -167,9 +217,23 @@ function DataTable({
|
|
|
167
217
|
sorting: enableSorting ? sorting : void 0,
|
|
168
218
|
rowSelection: enableRowSelection ? rowSelection : void 0,
|
|
169
219
|
pagination: pagination ? paginationState : void 0,
|
|
170
|
-
columnVisibility
|
|
220
|
+
columnVisibility,
|
|
221
|
+
columnSizing,
|
|
222
|
+
columnOrder,
|
|
223
|
+
columnPinning
|
|
171
224
|
}),
|
|
172
|
-
[
|
|
225
|
+
[
|
|
226
|
+
enableSorting,
|
|
227
|
+
sorting,
|
|
228
|
+
enableRowSelection,
|
|
229
|
+
rowSelection,
|
|
230
|
+
pagination,
|
|
231
|
+
paginationState,
|
|
232
|
+
columnVisibility,
|
|
233
|
+
columnSizing,
|
|
234
|
+
columnOrder,
|
|
235
|
+
columnPinning
|
|
236
|
+
]
|
|
173
237
|
);
|
|
174
238
|
const table = useReactTable({
|
|
175
239
|
data,
|
|
@@ -179,6 +243,12 @@ function DataTable({
|
|
|
179
243
|
onSortingChange: enableSorting ? setSorting : void 0,
|
|
180
244
|
onRowSelectionChange: enableRowSelection ? (u) => setRowSelection(apply(u, rowSelection)) : void 0,
|
|
181
245
|
onColumnVisibilityChange: (u) => setColumnVisibility(apply(u, columnVisibility)),
|
|
246
|
+
onColumnSizingChange: (u) => setColumnSizing(apply(u, columnSizing)),
|
|
247
|
+
onColumnOrderChange: (u) => setColumnOrder(apply(u, columnOrder)),
|
|
248
|
+
onColumnPinningChange: (u) => setColumnPinning(apply(u, columnPinning)),
|
|
249
|
+
enableColumnResizing,
|
|
250
|
+
columnResizeMode: "onChange",
|
|
251
|
+
columnResizeDirection: dir,
|
|
182
252
|
onPaginationChange: pagination ? setPaginationState : void 0,
|
|
183
253
|
getCoreRowModel: getCoreRowModel(),
|
|
184
254
|
getSortedRowModel: enableSorting ? getSortedRowModel() : void 0,
|
|
@@ -194,6 +264,50 @@ function DataTable({
|
|
|
194
264
|
const multiSort = sorting.length > 1;
|
|
195
265
|
const offerAll = withSelectColumn && !!pagination && table.getIsAllPageRowsSelected() && totalRows > table.getRowModel().rows.length;
|
|
196
266
|
const clear = () => table.resetRowSelection(true);
|
|
267
|
+
const rows = table.getRowModel().rows;
|
|
268
|
+
const useRowWindow = rowWindow ?? useAllRows;
|
|
269
|
+
const rowWin = useRowWindow(rows.length, () => tableRef.current?.parentElement ?? null);
|
|
270
|
+
const pinned = (columnPinning.left?.length ?? 0) + (columnPinning.right?.length ?? 0) > 0;
|
|
271
|
+
useIsomorphicLayoutEffect(() => {
|
|
272
|
+
const t = tableRef.current;
|
|
273
|
+
if (!t) return;
|
|
274
|
+
t.style.width = enableColumnResizing ? px(table.getTotalSize()) : "";
|
|
275
|
+
const scroller = t.parentElement;
|
|
276
|
+
if (scroller) scroller.style.maxHeight = maxHeight == null ? "" : px(maxHeight);
|
|
277
|
+
});
|
|
278
|
+
const placeCell = (column) => (el) => {
|
|
279
|
+
if (!el) return;
|
|
280
|
+
el.style.width = enableColumnResizing ? px(column.getSize()) : "";
|
|
281
|
+
const side = column.getIsPinned();
|
|
282
|
+
el.style.insetInlineStart = side === "left" ? px(column.getStart("left")) : "";
|
|
283
|
+
el.style.insetInlineEnd = side === "right" ? px(column.getAfter("right")) : "";
|
|
284
|
+
};
|
|
285
|
+
const pinClass = (column) => {
|
|
286
|
+
const side = column.getIsPinned();
|
|
287
|
+
if (!side) return void 0;
|
|
288
|
+
return cn(
|
|
289
|
+
"sticky z-[1] bg-[var(--color-surface)]",
|
|
290
|
+
"[tr:hover>&]:bg-[color-mix(in_srgb,var(--color-on-surface)_8%,var(--color-surface))]",
|
|
291
|
+
"[tr[data-state=selected]>&]:bg-[color-mix(in_srgb,var(--color-primary)_10%,var(--color-surface))]",
|
|
292
|
+
side === "left" && column.getIsLastColumn("left") && "border-e border-[var(--color-outline-variant)]",
|
|
293
|
+
side === "right" && column.getIsFirstColumn("right") && "border-s border-[var(--color-outline-variant)]"
|
|
294
|
+
);
|
|
295
|
+
};
|
|
296
|
+
const moveColumn = (id, to) => {
|
|
297
|
+
const ids = table.getAllLeafColumns().map((c) => c.id);
|
|
298
|
+
const order = (columnOrder.length ? columnOrder : ids).filter((c) => c !== "__select" && ids.includes(c));
|
|
299
|
+
for (const c of ids) if (c !== "__select" && !order.includes(c)) order.push(c);
|
|
300
|
+
const from = order.indexOf(id);
|
|
301
|
+
const target = typeof to === "number" ? from + to : order.indexOf(to);
|
|
302
|
+
if (from < 0 || target < 0 || target >= order.length || target === from) return;
|
|
303
|
+
order.splice(from, 1);
|
|
304
|
+
order.splice(target, 0, id);
|
|
305
|
+
setColumnOrder(withSelectColumn ? ["__select", ...order] : order);
|
|
306
|
+
if (typeof to === "number") {
|
|
307
|
+
const col = table.getColumn(id);
|
|
308
|
+
setAnnouncement(messages.columnMoved(col ? columnLabel(col) : id, target + 1, order.length));
|
|
309
|
+
}
|
|
310
|
+
};
|
|
197
311
|
return /* @__PURE__ */ jsxs("div", { className: cn("space-y-4", className), "data-density": density, ...props, children: [
|
|
198
312
|
(toolbar != null || viewOptions) && /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [
|
|
199
313
|
toolbar,
|
|
@@ -230,61 +344,151 @@ function DataTable({
|
|
|
230
344
|
/* @__PURE__ */ jsx("span", { children: messages.selectedCount(fmt(selectedRows.length)) }),
|
|
231
345
|
/* @__PURE__ */ jsx(Button, { variant: "link", size: "sm", onClick: () => table.toggleAllRowsSelected(true), children: messages.selectAllRows(fmt(totalRows)) })
|
|
232
346
|
] }) }),
|
|
233
|
-
/* @__PURE__ */
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
347
|
+
/* @__PURE__ */ jsx("span", { "aria-live": "polite", className: "sr-only", children: announcement }),
|
|
348
|
+
/* @__PURE__ */ jsxs(
|
|
349
|
+
TableRoot,
|
|
350
|
+
{
|
|
351
|
+
ref: tableRef,
|
|
352
|
+
"aria-label": ariaLabel,
|
|
353
|
+
"aria-labelledby": ariaLabelledBy,
|
|
354
|
+
"aria-rowcount": rowWin.virtual ? rows.length + 1 : void 0,
|
|
355
|
+
className: cn(enableColumnResizing && "table-fixed"),
|
|
356
|
+
children: [
|
|
357
|
+
caption != null && /* @__PURE__ */ jsx(Table.Caption, { className: cn(captionHidden && "sr-only"), children: caption }),
|
|
358
|
+
/* @__PURE__ */ jsx(TableHeader, { children: table.getHeaderGroups().map((headerGroup) => /* @__PURE__ */ jsx(TableRow, { className: "hover:bg-transparent", children: headerGroup.headers.map((header) => {
|
|
359
|
+
const align = header.column.columnDef.meta?.align ?? "start";
|
|
360
|
+
const col = header.column;
|
|
361
|
+
const reorderable = enableColumnReordering && col.id !== "__select" && !col.getIsPinned();
|
|
362
|
+
const sortable = enableSorting && col.getCanSort();
|
|
363
|
+
return /* @__PURE__ */ jsxs(
|
|
364
|
+
TableHead,
|
|
244
365
|
{
|
|
245
|
-
|
|
246
|
-
onClick: header.column.getToggleSortingHandler(),
|
|
366
|
+
ref: placeCell(col),
|
|
247
367
|
className: cn(
|
|
248
|
-
|
|
249
|
-
|
|
368
|
+
alignText[align],
|
|
369
|
+
compact && "h-8",
|
|
370
|
+
col.id === "__select" && "w-10",
|
|
371
|
+
// Anchors the resize handle; sticky (below) wins when set and anchors it too.
|
|
372
|
+
(enableColumnResizing || reorderable) && "relative",
|
|
373
|
+
maxHeight != null && "sticky top-0 z-[2] bg-[var(--color-surface)]",
|
|
374
|
+
pinClass(col),
|
|
375
|
+
maxHeight != null && col.getIsPinned() && "z-[3] [tr:hover>&]:bg-[var(--color-surface)]",
|
|
376
|
+
reorderable && "cursor-grab"
|
|
250
377
|
),
|
|
378
|
+
tabIndex: reorderable && !sortable ? 0 : void 0,
|
|
379
|
+
draggable: reorderable || void 0,
|
|
380
|
+
onDragStart: reorderable ? () => dragged.current = col.id : void 0,
|
|
381
|
+
onDragOver: reorderable ? (e) => e.preventDefault() : void 0,
|
|
382
|
+
onDrop: reorderable ? (e) => {
|
|
383
|
+
e.preventDefault();
|
|
384
|
+
if (dragged.current) moveColumn(dragged.current, col.id);
|
|
385
|
+
dragged.current = null;
|
|
386
|
+
} : void 0,
|
|
387
|
+
onKeyDown: reorderable ? (e) => {
|
|
388
|
+
if (!e.altKey || e.key !== "ArrowLeft" && e.key !== "ArrowRight") return;
|
|
389
|
+
e.preventDefault();
|
|
390
|
+
const forward = e.key === "ArrowRight" === (dir !== "rtl");
|
|
391
|
+
moveColumn(col.id, forward ? 1 : -1);
|
|
392
|
+
} : void 0,
|
|
393
|
+
"aria-sort": enableSorting && header.column.getCanSort() ? header.column.getIsSorted() === "asc" ? "ascending" : header.column.getIsSorted() === "desc" ? "descending" : "none" : void 0,
|
|
251
394
|
children: [
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
395
|
+
header.isPlaceholder ? null : enableSorting && header.column.getCanSort() ? /* @__PURE__ */ jsxs(
|
|
396
|
+
"button",
|
|
397
|
+
{
|
|
398
|
+
type: "button",
|
|
399
|
+
onClick: header.column.getToggleSortingHandler(),
|
|
400
|
+
className: cn(
|
|
401
|
+
"flex w-full items-center gap-1 hover:text-[var(--color-on-surface)]",
|
|
402
|
+
alignJustify[align]
|
|
403
|
+
),
|
|
404
|
+
children: [
|
|
405
|
+
flexRender(header.column.columnDef.header, header.getContext()),
|
|
406
|
+
multiSort && header.column.getIsSorted() && /* @__PURE__ */ jsxs("span", { className: "text-[10px] tabular-nums leading-none", children: [
|
|
407
|
+
/* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: header.column.getSortIndex() + 1 }),
|
|
408
|
+
/* @__PURE__ */ jsx("span", { className: "sr-only", children: messages.sortOrder(header.column.getSortIndex() + 1) })
|
|
409
|
+
] }),
|
|
410
|
+
header.column.getIsSorted() === "asc" ? /* @__PURE__ */ jsx(ArrowUp, { size: "xs", "aria-hidden": "true" }) : header.column.getIsSorted() === "desc" ? /* @__PURE__ */ jsx(ArrowDown, { size: "xs", "aria-hidden": "true" }) : /* @__PURE__ */ jsx(ChevronsUpDown, { size: "xs", className: "opacity-40", "aria-hidden": "true" })
|
|
411
|
+
]
|
|
412
|
+
}
|
|
413
|
+
) : flexRender(header.column.columnDef.header, header.getContext()),
|
|
414
|
+
enableColumnResizing && col.getCanResize() && /* @__PURE__ */ jsx(
|
|
415
|
+
"div",
|
|
416
|
+
{
|
|
417
|
+
role: "separator",
|
|
418
|
+
"aria-orientation": "vertical",
|
|
419
|
+
"aria-label": messages.resizeColumn(columnLabel(col)),
|
|
420
|
+
"aria-valuenow": header.getSize(),
|
|
421
|
+
"aria-valuemin": col.columnDef.minSize ?? 20,
|
|
422
|
+
"aria-valuemax": col.columnDef.maxSize ?? 1e3,
|
|
423
|
+
tabIndex: 0,
|
|
424
|
+
draggable: false,
|
|
425
|
+
onMouseDown: header.getResizeHandler(),
|
|
426
|
+
onTouchStart: header.getResizeHandler(),
|
|
427
|
+
onDoubleClick: () => col.resetSize(),
|
|
428
|
+
onKeyDown: (e) => {
|
|
429
|
+
if (e.key !== "ArrowLeft" && e.key !== "ArrowRight") return;
|
|
430
|
+
e.preventDefault();
|
|
431
|
+
e.stopPropagation();
|
|
432
|
+
const grow = e.key === "ArrowRight" === (dir !== "rtl");
|
|
433
|
+
const min = col.columnDef.minSize ?? 20;
|
|
434
|
+
const max = col.columnDef.maxSize ?? 1e3;
|
|
435
|
+
const size = Math.min(max, Math.max(min, header.getSize() + (grow ? 16 : -16)));
|
|
436
|
+
table.setColumnSizing((s) => ({ ...s, [col.id]: size }));
|
|
437
|
+
},
|
|
438
|
+
className: cn(
|
|
439
|
+
"absolute inset-y-0 end-0 z-[1] w-2 cursor-col-resize touch-none select-none outline-none",
|
|
440
|
+
"after:absolute after:inset-y-2 after:end-0 after:w-px after:bg-[var(--color-outline-variant)]",
|
|
441
|
+
"hover:after:w-0.5 hover:after:bg-[var(--color-primary)] focus-visible:after:w-0.5 focus-visible:after:bg-[var(--color-primary)]",
|
|
442
|
+
col.getIsResizing() && "after:w-0.5 after:bg-[var(--color-primary)]"
|
|
443
|
+
)
|
|
444
|
+
}
|
|
445
|
+
)
|
|
258
446
|
]
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
447
|
+
},
|
|
448
|
+
header.id
|
|
449
|
+
);
|
|
450
|
+
}) }, headerGroup.id)) }),
|
|
451
|
+
/* @__PURE__ */ jsx(TableBody, { children: rows.length ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
452
|
+
rowWin.before > 0 && /* @__PURE__ */ jsx(Spacer, { height: rowWin.before }),
|
|
453
|
+
rowWin.indexes.map((i) => {
|
|
454
|
+
const row = rows[i];
|
|
455
|
+
return /* @__PURE__ */ jsx(
|
|
456
|
+
TableRow,
|
|
457
|
+
{
|
|
458
|
+
ref: rowWin.measure,
|
|
459
|
+
"data-index": i,
|
|
460
|
+
"aria-rowindex": rowWin.virtual ? i + 2 : void 0,
|
|
461
|
+
"data-state": enableRowSelection && row.getIsSelected() ? "selected" : void 0,
|
|
462
|
+
children: row.getVisibleCells().map((cell) => /* @__PURE__ */ jsx(
|
|
463
|
+
TableCell,
|
|
464
|
+
{
|
|
465
|
+
ref: pinned || enableColumnResizing ? placeCell(cell.column) : void 0,
|
|
466
|
+
className: cn(
|
|
467
|
+
alignText[cell.column.columnDef.meta?.align ?? "start"],
|
|
468
|
+
compact && "py-1.5",
|
|
469
|
+
pinClass(cell.column),
|
|
470
|
+
enableColumnResizing && "truncate"
|
|
471
|
+
),
|
|
472
|
+
children: flexRender(cell.column.columnDef.cell, cell.getContext())
|
|
473
|
+
},
|
|
474
|
+
cell.id
|
|
475
|
+
))
|
|
476
|
+
},
|
|
477
|
+
row.id
|
|
478
|
+
);
|
|
479
|
+
}),
|
|
480
|
+
rowWin.after > 0 && /* @__PURE__ */ jsx(Spacer, { height: rowWin.after })
|
|
481
|
+
] }) : /* @__PURE__ */ jsx(TableRow, { children: /* @__PURE__ */ jsx(
|
|
270
482
|
TableCell,
|
|
271
483
|
{
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
))
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
TableCell,
|
|
281
|
-
{
|
|
282
|
-
colSpan: table.getVisibleLeafColumns().length,
|
|
283
|
-
className: "h-24 text-center text-[var(--color-on-surface-muted)]",
|
|
284
|
-
children: messages.noResults
|
|
285
|
-
}
|
|
286
|
-
) }) })
|
|
287
|
-
] }),
|
|
484
|
+
colSpan: table.getVisibleLeafColumns().length,
|
|
485
|
+
className: "h-24 text-center text-[var(--color-on-surface-muted)]",
|
|
486
|
+
children: messages.noResults
|
|
487
|
+
}
|
|
488
|
+
) }) })
|
|
489
|
+
]
|
|
490
|
+
}
|
|
491
|
+
),
|
|
288
492
|
pagination && /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center justify-between gap-2 text-sm text-[var(--color-on-surface-muted)]", children: [
|
|
289
493
|
/* @__PURE__ */ jsx("span", { children: messages.pageOf(table.getState().pagination.pageIndex + 1, table.getPageCount()) }),
|
|
290
494
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export { DataTable } from './DataTable.js';
|
|
2
|
-
export type { DataTableProps, DataTableColumn } from './DataTable.js';
|
|
2
|
+
export type { DataTableProps, DataTableColumn, DataTableDensity, DataTableRowWindow, DataTableRowWindowHook } from './DataTable.js';
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { type DataTableProps } from '../DataTable/DataTable.js';
|
|
3
|
+
export interface DataTableVirtualProps<TData> extends Omit<DataTableProps<TData>, 'pagination' | 'rowWindow'> {
|
|
4
|
+
/** Height of the scrolling area. Required: rows are windowed within it. */
|
|
5
|
+
maxHeight: number | string;
|
|
6
|
+
/** Row height before measuring, in px. Default 49, or 37 when compact. */
|
|
7
|
+
estimateRowHeight?: number;
|
|
8
|
+
/** Rows rendered beyond the visible ones, each way. Default 8. */
|
|
9
|
+
overscan?: number;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* DataTable for thousands of rows: only the rows in view (plus `overscan`)
|
|
13
|
+
* are in the DOM. Rows carry aria-rowindex and the table aria-rowcount, so
|
|
14
|
+
* screen readers still report the full size. Needs `@tanstack/react-virtual`.
|
|
15
|
+
*/
|
|
16
|
+
export declare function DataTableVirtual<TData>({ estimateRowHeight, overscan, ...props }: DataTableVirtualProps<TData>): React.JSX.Element;
|
|
17
|
+
export declare namespace DataTableVirtual {
|
|
18
|
+
var displayName: string;
|
|
19
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { useVirtualizer } from "@tanstack/react-virtual";
|
|
5
|
+
import { DataTable } from "../DataTable/DataTable.js";
|
|
6
|
+
const Options = React.createContext({ estimate: 49, overscan: 8, height: 600 });
|
|
7
|
+
const useVirtualRows = (count, getScrollElement) => {
|
|
8
|
+
const { estimate, overscan, height } = React.useContext(Options);
|
|
9
|
+
const virtualizer = useVirtualizer({
|
|
10
|
+
count,
|
|
11
|
+
getScrollElement,
|
|
12
|
+
estimateSize: () => estimate,
|
|
13
|
+
overscan,
|
|
14
|
+
// First render (and the server) assumes the full height, so rows show before measuring.
|
|
15
|
+
initialRect: { width: 0, height }
|
|
16
|
+
});
|
|
17
|
+
const items = virtualizer.getVirtualItems();
|
|
18
|
+
const last = items[items.length - 1];
|
|
19
|
+
return {
|
|
20
|
+
indexes: items.map((i) => i.index),
|
|
21
|
+
before: items[0]?.start ?? 0,
|
|
22
|
+
after: last ? virtualizer.getTotalSize() - last.end : 0,
|
|
23
|
+
measure: virtualizer.measureElement,
|
|
24
|
+
virtual: true
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
function DataTableVirtual({ estimateRowHeight, overscan = 8, ...props }) {
|
|
28
|
+
const compact = (props.density ?? props.defaultDensity) === "compact";
|
|
29
|
+
const estimate = estimateRowHeight ?? (compact ? 37 : 49);
|
|
30
|
+
const height = typeof props.maxHeight === "number" ? props.maxHeight : 600;
|
|
31
|
+
const options = React.useMemo(() => ({ estimate, overscan, height }), [estimate, overscan, height]);
|
|
32
|
+
return /* @__PURE__ */ jsx(Options.Provider, { value: options, children: /* @__PURE__ */ jsx(DataTable, { ...props, rowWindow: useVirtualRows }) });
|
|
33
|
+
}
|
|
34
|
+
DataTableVirtual.displayName = "DataTableVirtual";
|
|
35
|
+
export {
|
|
36
|
+
DataTableVirtual
|
|
37
|
+
};
|
|
@@ -2,6 +2,13 @@ import * as React from 'react';
|
|
|
2
2
|
import { type DateRange } from 'react-day-picker';
|
|
3
3
|
import type { Locale } from 'date-fns';
|
|
4
4
|
export type { DateRange };
|
|
5
|
+
export interface DateRangePreset {
|
|
6
|
+
label: string;
|
|
7
|
+
/** `today` is the current date in the picker's time zone (midnight, local Date). */
|
|
8
|
+
range: (today: Date) => DateRange;
|
|
9
|
+
}
|
|
10
|
+
/** Today's calendar date in `timeZone`, as a local midnight Date. */
|
|
11
|
+
export declare function todayIn(timeZone?: string, now?: Date): Date;
|
|
5
12
|
export interface DateRangePickerProps {
|
|
6
13
|
value?: DateRange;
|
|
7
14
|
onChange?: (range: DateRange | undefined) => void;
|
|
@@ -16,5 +23,15 @@ export interface DateRangePickerProps {
|
|
|
16
23
|
'aria-label'?: string;
|
|
17
24
|
/** date-fns Locale for the calendar and trigger text; wins over NexubProvider's `dateLocale`. */
|
|
18
25
|
locale?: Locale;
|
|
26
|
+
/**
|
|
27
|
+
* Quick ranges beside the calendar. `true` shows Today, Last 7 days,
|
|
28
|
+
* Last 30 days, Month to date and Last month; or pass your own.
|
|
29
|
+
*/
|
|
30
|
+
presets?: boolean | DateRangePreset[];
|
|
31
|
+
/**
|
|
32
|
+
* IANA time zone the dates mean, e.g. 'Europe/London'. Shown under the
|
|
33
|
+
* calendar, and presets count "today" there.
|
|
34
|
+
*/
|
|
35
|
+
timeZone?: string;
|
|
19
36
|
}
|
|
20
37
|
export declare const DateRangePicker: React.ForwardRefExoticComponent<DateRangePickerProps & React.RefAttributes<HTMLButtonElement>>;
|