@nexub/react 5.2.0 → 5.3.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
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "5.
|
|
2
|
+
"version": "5.3.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.",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import { type ColumnDef, type RowData } from '@tanstack/react-table';
|
|
2
|
+
import { type ColumnDef, type RowSelectionState, type RowData, type VisibilityState } 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' */
|
|
@@ -8,6 +8,7 @@ declare module '@tanstack/react-table' {
|
|
|
8
8
|
}
|
|
9
9
|
/** A DataTable column: TanStack's `ColumnDef`, with `meta.align` for numeric columns. */
|
|
10
10
|
export type DataTableColumn<TData> = ColumnDef<TData, unknown>;
|
|
11
|
+
export type DataTableDensity = 'comfortable' | 'compact';
|
|
11
12
|
export interface DataTableProps<TData> extends React.HTMLAttributes<HTMLDivElement> {
|
|
12
13
|
columns: DataTableColumn<TData>[];
|
|
13
14
|
/** Names the table for assistive tech (and is shown below it unless `captionHidden`). */
|
|
@@ -17,8 +18,29 @@ export interface DataTableProps<TData> extends React.HTMLAttributes<HTMLDivEleme
|
|
|
17
18
|
data: TData[];
|
|
18
19
|
enableSorting?: boolean;
|
|
19
20
|
enableRowSelection?: boolean;
|
|
21
|
+
/**
|
|
22
|
+
* With `enableRowSelection`: a checkbox column with select-page in its
|
|
23
|
+
* header, and "Select all N" across pages. On by default from 6.0.
|
|
24
|
+
*/
|
|
25
|
+
selectionColumn?: boolean;
|
|
26
|
+
/** Controlled selection, keyed by row id (see `getRowId`). */
|
|
27
|
+
rowSelection?: RowSelectionState;
|
|
28
|
+
onRowSelectionChange?: (selection: RowSelectionState) => void;
|
|
29
|
+
/** Stable row ids for selection; defaults to the row index. */
|
|
30
|
+
getRowId?: (row: TData, index: number) => string;
|
|
31
|
+
/** A floating bar while rows are selected: render your actions for the selected rows. */
|
|
32
|
+
bulkActions?: (rows: TData[], clear: () => void) => React.ReactNode;
|
|
33
|
+
density?: DataTableDensity;
|
|
34
|
+
defaultDensity?: DataTableDensity;
|
|
35
|
+
onDensityChange?: (density: DataTableDensity) => void;
|
|
36
|
+
columnVisibility?: VisibilityState;
|
|
37
|
+
onColumnVisibilityChange?: (visibility: VisibilityState) => void;
|
|
38
|
+
/** A "View" menu above the table: density and which columns show. */
|
|
39
|
+
viewOptions?: boolean;
|
|
40
|
+
/** Content at the start of the row above the table (filters, a title). */
|
|
41
|
+
toolbar?: React.ReactNode;
|
|
20
42
|
pagination?: {
|
|
21
43
|
pageSize?: number;
|
|
22
44
|
};
|
|
23
45
|
}
|
|
24
|
-
export declare function DataTable<TData>({ columns, data, enableSorting, enableRowSelection, pagination, caption, captionHidden, className, 'aria-label': ariaLabel, 'aria-labelledby': ariaLabelledBy, ...props }: DataTableProps<TData>): React.JSX.Element;
|
|
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;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import {
|
|
5
5
|
useReactTable,
|
|
@@ -9,11 +9,16 @@ import {
|
|
|
9
9
|
getPaginationRowModel,
|
|
10
10
|
flexRender
|
|
11
11
|
} from "@tanstack/react-table";
|
|
12
|
-
import { ArrowUp, ArrowDown, ChevronsUpDown } from "@nexub/icons";
|
|
12
|
+
import { ArrowUp, ArrowDown, ChevronsUpDown, SlidersHorizontal, X } from "@nexub/icons";
|
|
13
13
|
import { cn } from "../../utils/cn.js";
|
|
14
14
|
import { disabledContainer, pressable } from "../../utils/control.js";
|
|
15
|
-
import {
|
|
15
|
+
import { floatingSurface } from "../../utils/surface.js";
|
|
16
|
+
import { useControllableState } from "../../hooks/useControllableState.js";
|
|
17
|
+
import { useLocale, useNexubMessages } from "../NexubProvider/NexubProvider.js";
|
|
16
18
|
import { Table } from "../Table/Table.js";
|
|
19
|
+
import { Checkbox } from "../Checkbox/Checkbox.js";
|
|
20
|
+
import { Button } from "../Button/Button.js";
|
|
21
|
+
import { DropdownMenu } from "../DropdownMenu/DropdownMenu.js";
|
|
17
22
|
const alignText = {
|
|
18
23
|
start: "text-start",
|
|
19
24
|
end: "text-end",
|
|
@@ -47,7 +52,7 @@ const TableRow = React.forwardRef(
|
|
|
47
52
|
{
|
|
48
53
|
ref,
|
|
49
54
|
className: cn(
|
|
50
|
-
"border-b border-[var(--color-outline-variant)] transition-colors hover:bg-[color-mix(in_srgb,var(--color-on-surface)_8%,transparent)] data-[state=selected]:bg-[var(--color-
|
|
55
|
+
"border-b border-[var(--color-outline-variant)] transition-colors hover:bg-[color-mix(in_srgb,var(--color-on-surface)_8%,transparent)] data-[state=selected]:bg-[color-mix(in_srgb,var(--color-primary)_10%,transparent)]",
|
|
51
56
|
className
|
|
52
57
|
),
|
|
53
58
|
...props
|
|
@@ -79,11 +84,25 @@ const TableCell = React.forwardRef(({ className, ...props }, ref) => /* @__PURE_
|
|
|
79
84
|
}
|
|
80
85
|
));
|
|
81
86
|
TableCell.displayName = "DataTable.TableCell";
|
|
87
|
+
const apply = (u, prev) => typeof u === "function" ? u(prev) : u;
|
|
88
|
+
const columnLabel = (c) => typeof c.columnDef.header === "string" ? c.columnDef.header : c.id;
|
|
82
89
|
function DataTable({
|
|
83
90
|
columns,
|
|
84
91
|
data,
|
|
85
92
|
enableSorting = false,
|
|
86
93
|
enableRowSelection = false,
|
|
94
|
+
selectionColumn = false,
|
|
95
|
+
rowSelection: rowSelectionProp,
|
|
96
|
+
onRowSelectionChange,
|
|
97
|
+
getRowId,
|
|
98
|
+
bulkActions,
|
|
99
|
+
density: densityProp,
|
|
100
|
+
defaultDensity = "comfortable",
|
|
101
|
+
onDensityChange,
|
|
102
|
+
columnVisibility: columnVisibilityProp,
|
|
103
|
+
onColumnVisibilityChange,
|
|
104
|
+
viewOptions = false,
|
|
105
|
+
toolbar,
|
|
87
106
|
pagination,
|
|
88
107
|
caption,
|
|
89
108
|
captionHidden = false,
|
|
@@ -93,26 +112,73 @@ function DataTable({
|
|
|
93
112
|
...props
|
|
94
113
|
}) {
|
|
95
114
|
const messages = useNexubMessages();
|
|
115
|
+
const { locale } = useLocale();
|
|
96
116
|
const [sorting, setSorting] = React.useState([]);
|
|
97
|
-
const [rowSelection, setRowSelection] =
|
|
117
|
+
const [rowSelection = {}, setRowSelection] = useControllableState({
|
|
118
|
+
value: rowSelectionProp,
|
|
119
|
+
defaultValue: {},
|
|
120
|
+
onChange: onRowSelectionChange
|
|
121
|
+
});
|
|
122
|
+
const [density = "comfortable", setDensity] = useControllableState({
|
|
123
|
+
value: densityProp,
|
|
124
|
+
defaultValue: defaultDensity,
|
|
125
|
+
onChange: onDensityChange
|
|
126
|
+
});
|
|
127
|
+
const [columnVisibility = {}, setColumnVisibility] = useControllableState({
|
|
128
|
+
value: columnVisibilityProp,
|
|
129
|
+
defaultValue: {},
|
|
130
|
+
onChange: onColumnVisibilityChange
|
|
131
|
+
});
|
|
98
132
|
const [paginationState, setPaginationState] = React.useState({
|
|
99
133
|
pageIndex: 0,
|
|
100
134
|
pageSize: pagination?.pageSize ?? 20
|
|
101
135
|
});
|
|
136
|
+
const withSelectColumn = enableRowSelection && selectionColumn;
|
|
137
|
+
const allColumns = React.useMemo(
|
|
138
|
+
() => withSelectColumn ? [
|
|
139
|
+
{
|
|
140
|
+
id: "__select",
|
|
141
|
+
enableSorting: false,
|
|
142
|
+
enableHiding: false,
|
|
143
|
+
header: ({ table: table2 }) => /* @__PURE__ */ jsx(
|
|
144
|
+
Checkbox,
|
|
145
|
+
{
|
|
146
|
+
"aria-label": messages.selectAllOnPage,
|
|
147
|
+
checked: table2.getIsAllPageRowsSelected() ? true : table2.getIsSomePageRowsSelected() ? "indeterminate" : false,
|
|
148
|
+
onCheckedChange: (v) => table2.toggleAllPageRowsSelected(v === true)
|
|
149
|
+
}
|
|
150
|
+
),
|
|
151
|
+
cell: ({ row }) => /* @__PURE__ */ jsx(
|
|
152
|
+
Checkbox,
|
|
153
|
+
{
|
|
154
|
+
"aria-label": messages.selectRow,
|
|
155
|
+
checked: row.getIsSelected(),
|
|
156
|
+
disabled: !row.getCanSelect(),
|
|
157
|
+
onCheckedChange: (v) => row.toggleSelected(v === true)
|
|
158
|
+
}
|
|
159
|
+
)
|
|
160
|
+
},
|
|
161
|
+
...columns
|
|
162
|
+
] : columns,
|
|
163
|
+
[withSelectColumn, columns, messages.selectAllOnPage, messages.selectRow]
|
|
164
|
+
);
|
|
102
165
|
const tableState = React.useMemo(
|
|
103
166
|
() => ({
|
|
104
167
|
sorting: enableSorting ? sorting : void 0,
|
|
105
168
|
rowSelection: enableRowSelection ? rowSelection : void 0,
|
|
106
|
-
pagination: pagination ? paginationState : void 0
|
|
169
|
+
pagination: pagination ? paginationState : void 0,
|
|
170
|
+
columnVisibility
|
|
107
171
|
}),
|
|
108
|
-
[enableSorting, sorting, enableRowSelection, rowSelection, pagination, paginationState]
|
|
172
|
+
[enableSorting, sorting, enableRowSelection, rowSelection, pagination, paginationState, columnVisibility]
|
|
109
173
|
);
|
|
110
174
|
const table = useReactTable({
|
|
111
175
|
data,
|
|
112
|
-
columns,
|
|
176
|
+
columns: allColumns,
|
|
113
177
|
state: tableState,
|
|
178
|
+
getRowId,
|
|
114
179
|
onSortingChange: enableSorting ? setSorting : void 0,
|
|
115
|
-
onRowSelectionChange: enableRowSelection ? setRowSelection : void 0,
|
|
180
|
+
onRowSelectionChange: enableRowSelection ? (u) => setRowSelection(apply(u, rowSelection)) : void 0,
|
|
181
|
+
onColumnVisibilityChange: (u) => setColumnVisibility(apply(u, columnVisibility)),
|
|
116
182
|
onPaginationChange: pagination ? setPaginationState : void 0,
|
|
117
183
|
getCoreRowModel: getCoreRowModel(),
|
|
118
184
|
getSortedRowModel: enableSorting ? getSortedRowModel() : void 0,
|
|
@@ -121,7 +187,49 @@ function DataTable({
|
|
|
121
187
|
enableRowSelection,
|
|
122
188
|
manualPagination: false
|
|
123
189
|
});
|
|
124
|
-
|
|
190
|
+
const compact = density === "compact";
|
|
191
|
+
const selectedRows = enableRowSelection ? table.getSelectedRowModel().rows : [];
|
|
192
|
+
const totalRows = table.getCoreRowModel().rows.length;
|
|
193
|
+
const fmt = (n) => new Intl.NumberFormat(locale).format(n);
|
|
194
|
+
const multiSort = sorting.length > 1;
|
|
195
|
+
const offerAll = withSelectColumn && !!pagination && table.getIsAllPageRowsSelected() && totalRows > table.getRowModel().rows.length;
|
|
196
|
+
const clear = () => table.resetRowSelection(true);
|
|
197
|
+
return /* @__PURE__ */ jsxs("div", { className: cn("space-y-4", className), "data-density": density, ...props, children: [
|
|
198
|
+
(toolbar != null || viewOptions) && /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [
|
|
199
|
+
toolbar,
|
|
200
|
+
viewOptions && /* @__PURE__ */ jsxs(DropdownMenu.Root, { children: [
|
|
201
|
+
/* @__PURE__ */ jsx(DropdownMenu.Trigger, { asChild: true, children: /* @__PURE__ */ jsxs(Button, { variant: "outline", size: "sm", className: "ms-auto", children: [
|
|
202
|
+
/* @__PURE__ */ jsx(SlidersHorizontal, { size: "sm", "aria-hidden": "true" }),
|
|
203
|
+
messages.view
|
|
204
|
+
] }) }),
|
|
205
|
+
/* @__PURE__ */ jsxs(DropdownMenu.Content, { align: "end", children: [
|
|
206
|
+
/* @__PURE__ */ jsx(DropdownMenu.Label, { children: messages.density }),
|
|
207
|
+
/* @__PURE__ */ jsxs(DropdownMenu.RadioGroup, { value: density, onValueChange: (v) => setDensity(v), children: [
|
|
208
|
+
/* @__PURE__ */ jsx(DropdownMenu.RadioItem, { value: "comfortable", children: messages.comfortable }),
|
|
209
|
+
/* @__PURE__ */ jsx(DropdownMenu.RadioItem, { value: "compact", children: messages.compact })
|
|
210
|
+
] }),
|
|
211
|
+
/* @__PURE__ */ jsx(DropdownMenu.Separator, {}),
|
|
212
|
+
/* @__PURE__ */ jsx(DropdownMenu.Label, { children: messages.columns }),
|
|
213
|
+
table.getAllLeafColumns().filter((c) => c.getCanHide()).map((c) => /* @__PURE__ */ jsx(
|
|
214
|
+
DropdownMenu.CheckboxItem,
|
|
215
|
+
{
|
|
216
|
+
checked: c.getIsVisible(),
|
|
217
|
+
onCheckedChange: (v) => c.toggleVisibility(v === true),
|
|
218
|
+
onSelect: (e) => e.preventDefault(),
|
|
219
|
+
children: columnLabel(c)
|
|
220
|
+
},
|
|
221
|
+
c.id
|
|
222
|
+
))
|
|
223
|
+
] })
|
|
224
|
+
] })
|
|
225
|
+
] }),
|
|
226
|
+
offerAll && /* @__PURE__ */ jsx("div", { className: "flex flex-wrap items-center gap-2 rounded-shape-container bg-[var(--color-surface-container-low)] px-4 py-2 text-body-sm", children: table.getIsAllRowsSelected() ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
227
|
+
/* @__PURE__ */ jsx("span", { children: messages.selectedCount(fmt(totalRows)) }),
|
|
228
|
+
/* @__PURE__ */ jsx(Button, { variant: "link", size: "sm", onClick: clear, children: messages.clearSelection })
|
|
229
|
+
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
230
|
+
/* @__PURE__ */ jsx("span", { children: messages.selectedCount(fmt(selectedRows.length)) }),
|
|
231
|
+
/* @__PURE__ */ jsx(Button, { variant: "link", size: "sm", onClick: () => table.toggleAllRowsSelected(true), children: messages.selectAllRows(fmt(totalRows)) })
|
|
232
|
+
] }) }),
|
|
125
233
|
/* @__PURE__ */ jsxs(TableRoot, { "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, children: [
|
|
126
234
|
caption != null && /* @__PURE__ */ jsx(Table.Caption, { className: cn(captionHidden && "sr-only"), children: caption }),
|
|
127
235
|
/* @__PURE__ */ jsx(TableHeader, { children: table.getHeaderGroups().map((headerGroup) => /* @__PURE__ */ jsx(TableRow, { className: "hover:bg-transparent", children: headerGroup.headers.map((header) => {
|
|
@@ -129,7 +237,7 @@ function DataTable({
|
|
|
129
237
|
return /* @__PURE__ */ jsx(
|
|
130
238
|
TableHead,
|
|
131
239
|
{
|
|
132
|
-
className: alignText[align],
|
|
240
|
+
className: cn(alignText[align], compact && "h-8", header.column.id === "__select" && "w-10"),
|
|
133
241
|
"aria-sort": enableSorting && header.column.getCanSort() ? header.column.getIsSorted() === "asc" ? "ascending" : header.column.getIsSorted() === "desc" ? "descending" : "none" : void 0,
|
|
134
242
|
children: header.isPlaceholder ? null : enableSorting && header.column.getCanSort() ? /* @__PURE__ */ jsxs(
|
|
135
243
|
"button",
|
|
@@ -142,6 +250,10 @@ function DataTable({
|
|
|
142
250
|
),
|
|
143
251
|
children: [
|
|
144
252
|
flexRender(header.column.columnDef.header, header.getContext()),
|
|
253
|
+
multiSort && header.column.getIsSorted() && /* @__PURE__ */ jsxs("span", { className: "text-[10px] tabular-nums leading-none", children: [
|
|
254
|
+
/* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: header.column.getSortIndex() + 1 }),
|
|
255
|
+
/* @__PURE__ */ jsx("span", { className: "sr-only", children: messages.sortOrder(header.column.getSortIndex() + 1) })
|
|
256
|
+
] }),
|
|
145
257
|
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" })
|
|
146
258
|
]
|
|
147
259
|
}
|
|
@@ -157,7 +269,7 @@ function DataTable({
|
|
|
157
269
|
children: row.getVisibleCells().map((cell) => /* @__PURE__ */ jsx(
|
|
158
270
|
TableCell,
|
|
159
271
|
{
|
|
160
|
-
className: alignText[cell.column.columnDef.meta?.align ?? "start"],
|
|
272
|
+
className: cn(alignText[cell.column.columnDef.meta?.align ?? "start"], compact && "py-1.5"),
|
|
161
273
|
children: flexRender(cell.column.columnDef.cell, cell.getContext())
|
|
162
274
|
},
|
|
163
275
|
cell.id
|
|
@@ -167,7 +279,7 @@ function DataTable({
|
|
|
167
279
|
)) : /* @__PURE__ */ jsx(TableRow, { children: /* @__PURE__ */ jsx(
|
|
168
280
|
TableCell,
|
|
169
281
|
{
|
|
170
|
-
colSpan:
|
|
282
|
+
colSpan: table.getVisibleLeafColumns().length,
|
|
171
283
|
className: "h-24 text-center text-[var(--color-on-surface-muted)]",
|
|
172
284
|
children: messages.noResults
|
|
173
285
|
}
|
|
@@ -197,7 +309,27 @@ function DataTable({
|
|
|
197
309
|
}
|
|
198
310
|
)
|
|
199
311
|
] })
|
|
200
|
-
] })
|
|
312
|
+
] }),
|
|
313
|
+
bulkActions && selectedRows.length > 0 && /* @__PURE__ */ jsxs(
|
|
314
|
+
"div",
|
|
315
|
+
{
|
|
316
|
+
role: "region",
|
|
317
|
+
"aria-label": messages.selectedCount(fmt(selectedRows.length)),
|
|
318
|
+
className: cn(
|
|
319
|
+
floatingSurface,
|
|
320
|
+
"nx-motion sticky bottom-4 z-10 mx-auto flex w-fit max-w-full flex-wrap items-center gap-3 px-4 py-2"
|
|
321
|
+
),
|
|
322
|
+
"data-state": "open",
|
|
323
|
+
children: [
|
|
324
|
+
/* @__PURE__ */ jsx("span", { "aria-live": "polite", className: "text-label-lg tabular-nums", children: messages.selectedCount(fmt(selectedRows.length)) }),
|
|
325
|
+
bulkActions(
|
|
326
|
+
selectedRows.map((r) => r.original),
|
|
327
|
+
clear
|
|
328
|
+
),
|
|
329
|
+
/* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", onClick: clear, "aria-label": messages.clearSelection, children: /* @__PURE__ */ jsx(X, { size: "sm", "aria-hidden": "true" }) })
|
|
330
|
+
]
|
|
331
|
+
}
|
|
332
|
+
)
|
|
201
333
|
] });
|
|
202
334
|
}
|
|
203
335
|
export {
|