@nuvui/table 0.0.0-stage → 0.1.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/LICENSE +21 -0
- package/README.md +57 -2
- package/dist/components/data-table/content.d.ts +67 -0
- package/dist/components/data-table/content.js +278 -0
- package/dist/components/data-table/context.d.ts +23 -0
- package/dist/components/data-table/context.js +51 -0
- package/dist/components/data-table/create-data-table-hook.d.ts +78 -0
- package/dist/components/data-table/create-data-table-hook.js +103 -0
- package/dist/components/data-table/data-table.d.ts +76 -0
- package/dist/components/data-table/data-table.js +178 -0
- package/dist/components/data-table/header.d.ts +23 -0
- package/dist/components/data-table/header.js +120 -0
- package/dist/components/data-table/index.d.ts +11 -0
- package/dist/components/data-table/index.js +10 -0
- package/dist/components/data-table/labels.d.ts +50 -0
- package/dist/components/data-table/labels.js +35 -0
- package/dist/components/data-table/pagination.d.ts +18 -0
- package/dist/components/data-table/pagination.js +126 -0
- package/dist/components/data-table/selection.d.ts +48 -0
- package/dist/components/data-table/selection.js +153 -0
- package/dist/components/data-table/toolbar.d.ts +51 -0
- package/dist/components/data-table/toolbar.js +123 -0
- package/dist/components/data-table/use-data-table-request.d.ts +44 -0
- package/dist/components/data-table/use-data-table-request.js +72 -0
- package/dist/components/table/index.d.ts +2 -0
- package/dist/components/table/index.js +2 -0
- package/dist/components/table/table.d.ts +100 -0
- package/dist/components/table/table.js +130 -0
- package/dist/css/data-table.css +278 -0
- package/dist/css/table.css +164 -0
- package/dist/full.d.ts +264 -0
- package/dist/full.js +44 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.js +11 -0
- package/dist/styles.css +441 -0
- package/dist/utils/cx.js +6 -0
- package/dist/utils/use-shift-click.js +32 -0
- package/dist/virtual.d.ts +42 -0
- package/dist/virtual.js +133 -0
- package/package.json +101 -3
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { tableContexts } from "./context.js";
|
|
3
|
+
import { DataTableColumnHeader } from "./header.js";
|
|
4
|
+
import { DataTablePagination } from "./pagination.js";
|
|
5
|
+
import { DataTableBulkActions, DataTableExpandToggle, DataTableSelectAll, DataTableSelectRow } from "./selection.js";
|
|
6
|
+
import { DataTableColumnChooser, DataTableColumnFilter, DataTableFilter, DataTableToolbar } from "./toolbar.js";
|
|
7
|
+
import { createTableHook } from "@tanstack/react-table";
|
|
8
|
+
import { useDirection } from "@nuvui/react/direction";
|
|
9
|
+
//#region src/components/data-table/create-data-table-hook.tsx
|
|
10
|
+
const tableComponents = {
|
|
11
|
+
Toolbar: DataTableToolbar,
|
|
12
|
+
Filter: DataTableFilter,
|
|
13
|
+
ColumnChooser: DataTableColumnChooser,
|
|
14
|
+
BulkActions: DataTableBulkActions,
|
|
15
|
+
Pagination: DataTablePagination
|
|
16
|
+
};
|
|
17
|
+
const headerComponents = {
|
|
18
|
+
ColumnHeader: DataTableColumnHeader,
|
|
19
|
+
ColumnFilter: DataTableColumnFilter,
|
|
20
|
+
SelectAll: DataTableSelectAll
|
|
21
|
+
};
|
|
22
|
+
const cellComponents = {
|
|
23
|
+
SelectRow: DataTableSelectRow,
|
|
24
|
+
ExpandToggle: DataTableExpandToggle
|
|
25
|
+
};
|
|
26
|
+
const control = {
|
|
27
|
+
enableSorting: false,
|
|
28
|
+
enableHiding: false,
|
|
29
|
+
enableResizing: false,
|
|
30
|
+
enableColumnFilter: false,
|
|
31
|
+
enableGlobalFilter: false,
|
|
32
|
+
size: 48,
|
|
33
|
+
meta: { control: true }
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Makes the hook an app's tables are created with. Call it once, in a
|
|
37
|
+
* module of its own, with the features your tables use. Only those end up
|
|
38
|
+
* in your bundle.
|
|
39
|
+
*
|
|
40
|
+
* It's TanStack's `createTableHook` with this package's controls already
|
|
41
|
+
* registered, and takes the same options. What it returns:
|
|
42
|
+
*
|
|
43
|
+
* - `useDataTable`: `useTable`, bound to the features. Its table has the
|
|
44
|
+
* controls on it: `table.Pagination`, `table.Filter` and so on.
|
|
45
|
+
* - `createColumnHelper`: TanStack's column helper, bound to the features,
|
|
46
|
+
* with `select()` and `expand()` added. In a column's `header` and `cell`
|
|
47
|
+
* the controls are on what it's given: `header.ColumnHeader`,
|
|
48
|
+
* `cell.SelectRow`.
|
|
49
|
+
*/
|
|
50
|
+
function createDataTableHook(options) {
|
|
51
|
+
const hook = createTableHook({
|
|
52
|
+
...options,
|
|
53
|
+
...tableContexts,
|
|
54
|
+
tableComponents: {
|
|
55
|
+
...tableComponents,
|
|
56
|
+
...options.tableComponents
|
|
57
|
+
},
|
|
58
|
+
cellComponents: {
|
|
59
|
+
...cellComponents,
|
|
60
|
+
...options.cellComponents
|
|
61
|
+
},
|
|
62
|
+
headerComponents: {
|
|
63
|
+
...headerComponents,
|
|
64
|
+
...options.headerComponents
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
function useDataTable(tableOptions, selector) {
|
|
68
|
+
const columnResizeDirection = useDirection();
|
|
69
|
+
return hook.useAppTable({
|
|
70
|
+
columnResizeDirection,
|
|
71
|
+
...tableOptions
|
|
72
|
+
}, selector);
|
|
73
|
+
}
|
|
74
|
+
function createColumnHelper() {
|
|
75
|
+
const helper = hook.createAppColumnHelper();
|
|
76
|
+
return Object.assign(helper, {
|
|
77
|
+
select: (column = {}) => helper.display({
|
|
78
|
+
id: "select",
|
|
79
|
+
header: DataTableSelectAll,
|
|
80
|
+
cell: DataTableSelectRow,
|
|
81
|
+
...control,
|
|
82
|
+
...column
|
|
83
|
+
}),
|
|
84
|
+
expand: (column = {}) => helper.display({
|
|
85
|
+
id: "expand",
|
|
86
|
+
cell: DataTableExpandToggle,
|
|
87
|
+
...control,
|
|
88
|
+
...column
|
|
89
|
+
})
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
return {
|
|
93
|
+
/** The features object that was passed in. */
|
|
94
|
+
features: hook.appFeatures,
|
|
95
|
+
useDataTable,
|
|
96
|
+
createColumnHelper,
|
|
97
|
+
useTableContext: hook.useTableContext,
|
|
98
|
+
useHeaderContext: hook.useHeaderContext,
|
|
99
|
+
useCellContext: hook.useCellContext
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
//#endregion
|
|
103
|
+
export { createDataTableHook };
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { DataTableLabels } from "./labels.js";
|
|
2
|
+
import { DataTableContentOwnProps, DataTableName } from "./content.js";
|
|
3
|
+
import { ReactTable, Row, RowData, TableFeatures } from "@tanstack/react-table";
|
|
4
|
+
import { HTMLAttributes, ReactNode } from "react";
|
|
5
|
+
//#region src/components/data-table/data-table.d.ts
|
|
6
|
+
export interface DataTableRootOwnProps<TFeatures extends TableFeatures = any, TData extends RowData = any> {
|
|
7
|
+
/** The table, from `useDataTable` or from TanStack's own `useTable`. */
|
|
8
|
+
table: ReactTable<TFeatures, TData, any>;
|
|
9
|
+
/** Text the table writes itself, to change or translate. */
|
|
10
|
+
labels?: Partial<DataTableLabels>;
|
|
11
|
+
/**
|
|
12
|
+
* The id of the column that names each row, such as a person's name. Its
|
|
13
|
+
* cells become the row's heading, and the row's checkbox and expand
|
|
14
|
+
* button are named after it: "Select Ada Lovelace".
|
|
15
|
+
*/
|
|
16
|
+
rowHeader?: string;
|
|
17
|
+
/**
|
|
18
|
+
* A name for a row, where no one column gives it. It's used the way
|
|
19
|
+
* `rowHeader` is, for the names of the row's controls.
|
|
20
|
+
*/
|
|
21
|
+
getRowLabel?: (row: Row<TFeatures, TData>) => string;
|
|
22
|
+
/**
|
|
23
|
+
* Rows are on their way. With none yet, the table shows placeholders.
|
|
24
|
+
* With some, it keeps them and marks itself busy.
|
|
25
|
+
* @default false
|
|
26
|
+
*/
|
|
27
|
+
loading?: boolean;
|
|
28
|
+
/**
|
|
29
|
+
* The rows couldn't be loaded. A message of your own, or `true` for the
|
|
30
|
+
* standard one. It's shown in place of the rows.
|
|
31
|
+
*/
|
|
32
|
+
error?: ReactNode;
|
|
33
|
+
/** With `error`, adds a button that calls this. */
|
|
34
|
+
onRetry?: () => void;
|
|
35
|
+
}
|
|
36
|
+
export interface DataTableRootProps<TFeatures extends TableFeatures, TData extends RowData> extends DataTableRootOwnProps<TFeatures, TData>, HTMLAttributes<HTMLDivElement> {}
|
|
37
|
+
/**
|
|
38
|
+
* What every part of a data table sits in. It takes the table and hands it
|
|
39
|
+
* to the parts, and reads out what changes for screen readers.
|
|
40
|
+
*
|
|
41
|
+
* `DataTable` is this with the usual parts already inside. Use this one to
|
|
42
|
+
* arrange them yourself.
|
|
43
|
+
*/
|
|
44
|
+
export declare function DataTableRoot<TFeatures extends TableFeatures, TData extends RowData>({ table: given, labels, rowHeader, getRowLabel, loading, error, onRetry, className, children, ...props }: DataTableRootProps<TFeatures, TData>): import("react").JSX.Element;
|
|
45
|
+
export interface DataTableOwnProps<TFeatures extends TableFeatures = any, TData extends RowData = any> {
|
|
46
|
+
/**
|
|
47
|
+
* What goes above the table. Left out, it's the search field and the
|
|
48
|
+
* column chooser, where the table has those features. `false` for
|
|
49
|
+
* nothing.
|
|
50
|
+
*/
|
|
51
|
+
toolbar?: ReactNode;
|
|
52
|
+
/**
|
|
53
|
+
* Actions for the selected rows. They show in a bar above the table
|
|
54
|
+
* while anything is selected. As a function it's given the selected
|
|
55
|
+
* rows that are loaded.
|
|
56
|
+
*/
|
|
57
|
+
bulkActions?: ReactNode | ((rows: Row<TFeatures, TData>[]) => ReactNode);
|
|
58
|
+
/**
|
|
59
|
+
* What goes under the table. Left out, it's the pagination bar, where
|
|
60
|
+
* the table has pages. `false` for nothing.
|
|
61
|
+
*/
|
|
62
|
+
pagination?: ReactNode;
|
|
63
|
+
/**
|
|
64
|
+
* The choices for rows per page.
|
|
65
|
+
* @default [10, 20, 50, 100]
|
|
66
|
+
*/
|
|
67
|
+
pageSizes?: number[];
|
|
68
|
+
}
|
|
69
|
+
export type DataTableProps<TFeatures extends TableFeatures, TData extends RowData> = DataTableRootOwnProps<TFeatures, TData> & DataTableContentOwnProps<TFeatures, TData> & DataTableOwnProps<TFeatures, TData> & DataTableName;
|
|
70
|
+
/**
|
|
71
|
+
* A finished table from a TanStack table: headings that sort, rows, a
|
|
72
|
+
* search field, a column chooser, checkboxes and a pagination bar, each
|
|
73
|
+
* one there if the table was made with the feature for it.
|
|
74
|
+
*/
|
|
75
|
+
export declare function DataTable<TFeatures extends TableFeatures, TData extends RowData>(props: DataTableProps<TFeatures, TData>): import("react").JSX.Element;
|
|
76
|
+
//#endregion
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { ViewContext, columnLabel, supports, tableContexts, useDataTableView } from "./context.js";
|
|
3
|
+
import { cx } from "../../utils/cx.js";
|
|
4
|
+
import { DataTablePagination } from "./pagination.js";
|
|
5
|
+
import { DataTableBulkActions } from "./selection.js";
|
|
6
|
+
import { DataTableToolbar } from "./toolbar.js";
|
|
7
|
+
import { DataTableContent } from "./content.js";
|
|
8
|
+
import { defaultLabels } from "./labels.js";
|
|
9
|
+
import { useEffect, useMemo, useRef, useState } from "react";
|
|
10
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
11
|
+
//#region src/components/data-table/data-table.tsx
|
|
12
|
+
function Announcer() {
|
|
13
|
+
const { table, labels, loading } = useDataTableView("DataTable");
|
|
14
|
+
const [message, setMessage] = useState("");
|
|
15
|
+
const sorting = table.atoms.sorting?.get();
|
|
16
|
+
const columnFilters = table.atoms.columnFilters?.get();
|
|
17
|
+
const globalFilter = table.atoms.globalFilter?.get();
|
|
18
|
+
const pageIndex = table.atoms.pagination?.get().pageIndex;
|
|
19
|
+
const selection = supports.selection(table) ? table.getSelectedRowIds().length : 0;
|
|
20
|
+
const seen = useRef({
|
|
21
|
+
sorting,
|
|
22
|
+
columnFilters,
|
|
23
|
+
globalFilter,
|
|
24
|
+
pageIndex,
|
|
25
|
+
selection
|
|
26
|
+
});
|
|
27
|
+
const pending = useRef(null);
|
|
28
|
+
const asked = useRef(null);
|
|
29
|
+
const { data } = table.options;
|
|
30
|
+
const manualFilter = table.options.manualFiltering === true;
|
|
31
|
+
const manualSort = table.options.manualSorting === true;
|
|
32
|
+
const manualPage = table.options.manualPagination === true;
|
|
33
|
+
useEffect(() => {
|
|
34
|
+
const before = seen.current;
|
|
35
|
+
seen.current = {
|
|
36
|
+
sorting,
|
|
37
|
+
columnFilters,
|
|
38
|
+
globalFilter,
|
|
39
|
+
pageIndex,
|
|
40
|
+
selection
|
|
41
|
+
};
|
|
42
|
+
let fresh = true;
|
|
43
|
+
if (before.columnFilters !== columnFilters || before.globalFilter !== globalFilter) pending.current = "filter";
|
|
44
|
+
else if (before.sorting !== sorting) pending.current = "sort";
|
|
45
|
+
else if (before.pageIndex !== pageIndex) pending.current = "page";
|
|
46
|
+
else if (before.selection !== selection) pending.current = "selection";
|
|
47
|
+
else fresh = false;
|
|
48
|
+
const change = pending.current;
|
|
49
|
+
if (!change) return;
|
|
50
|
+
if (change === "filter" && manualFilter || change === "sort" && manualSort || change === "page" && manualPage) {
|
|
51
|
+
if (fresh) asked.current = {
|
|
52
|
+
data,
|
|
53
|
+
loaded: false
|
|
54
|
+
};
|
|
55
|
+
if (asked.current && loading) asked.current.loaded = true;
|
|
56
|
+
if (!(asked.current !== null && !loading && (asked.current.loaded || asked.current.data !== data))) return;
|
|
57
|
+
asked.current = null;
|
|
58
|
+
} else if (loading) return;
|
|
59
|
+
pending.current = null;
|
|
60
|
+
let text = "";
|
|
61
|
+
if (change === "filter") text = labels.results(supports.pagination(table) ? table.getRowCount() : table.getRowModel().rows.length);
|
|
62
|
+
else if (change === "sort") {
|
|
63
|
+
const first = sorting?.[0];
|
|
64
|
+
const column = first ? table.getColumn(first.id) : void 0;
|
|
65
|
+
if (!first || !column) text = labels.sortCleared;
|
|
66
|
+
else if (first.desc) text = labels.sortedDescending(columnLabel(column));
|
|
67
|
+
else text = labels.sortedAscending(columnLabel(column));
|
|
68
|
+
} else if (change === "page") text = labels.page((pageIndex ?? 0) + 1, Math.max(table.getPageCount(), 1));
|
|
69
|
+
else text = labels.selected(selection);
|
|
70
|
+
setMessage((last) => last === text ? `${text}\u00a0` : text);
|
|
71
|
+
}, [
|
|
72
|
+
table,
|
|
73
|
+
labels,
|
|
74
|
+
loading,
|
|
75
|
+
data,
|
|
76
|
+
manualFilter,
|
|
77
|
+
manualSort,
|
|
78
|
+
manualPage,
|
|
79
|
+
sorting,
|
|
80
|
+
columnFilters,
|
|
81
|
+
globalFilter,
|
|
82
|
+
pageIndex,
|
|
83
|
+
selection
|
|
84
|
+
]);
|
|
85
|
+
return /* @__PURE__ */ jsx("div", {
|
|
86
|
+
role: "status",
|
|
87
|
+
"aria-live": "polite",
|
|
88
|
+
"aria-atomic": "true",
|
|
89
|
+
className: "nuv-data-table__status",
|
|
90
|
+
children: message
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
function Provide({ table, labels: given, rowHeader, getRowLabel, loading, error, onRetry, children }) {
|
|
94
|
+
const labels = useMemo(() => ({
|
|
95
|
+
...defaultLabels,
|
|
96
|
+
...given
|
|
97
|
+
}), [given]);
|
|
98
|
+
const view = {
|
|
99
|
+
table,
|
|
100
|
+
labels,
|
|
101
|
+
rowHeader,
|
|
102
|
+
loading,
|
|
103
|
+
error,
|
|
104
|
+
onRetry,
|
|
105
|
+
rowName(row) {
|
|
106
|
+
if (getRowLabel) return getRowLabel(row);
|
|
107
|
+
if (rowHeader) return String(row.getValue(rowHeader) ?? "");
|
|
108
|
+
const index = row.getDisplayIndex();
|
|
109
|
+
return labels.rowName((index === -1 ? row.index : index) + 1);
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
return /* @__PURE__ */ jsx(tableContexts.tableContext.Provider, {
|
|
113
|
+
value: table,
|
|
114
|
+
children: /* @__PURE__ */ jsx(ViewContext.Provider, {
|
|
115
|
+
value: view,
|
|
116
|
+
children
|
|
117
|
+
})
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* What every part of a data table sits in. It takes the table and hands it
|
|
122
|
+
* to the parts, and reads out what changes for screen readers.
|
|
123
|
+
*
|
|
124
|
+
* `DataTable` is this with the usual parts already inside. Use this one to
|
|
125
|
+
* arrange them yourself.
|
|
126
|
+
*/
|
|
127
|
+
function DataTableRoot({ table: given, labels, rowHeader, getRowLabel, loading = false, error, onRetry, className, children, ...props }) {
|
|
128
|
+
const table = given;
|
|
129
|
+
return /* @__PURE__ */ jsx("div", {
|
|
130
|
+
className: cx("nuv-data-table", className),
|
|
131
|
+
"data-loading": loading ? "" : void 0,
|
|
132
|
+
...props,
|
|
133
|
+
children: /* @__PURE__ */ jsx(table.Subscribe, {
|
|
134
|
+
selector: (state) => state,
|
|
135
|
+
children: () => /* @__PURE__ */ jsxs(Provide, {
|
|
136
|
+
table,
|
|
137
|
+
labels,
|
|
138
|
+
rowHeader,
|
|
139
|
+
getRowLabel,
|
|
140
|
+
loading,
|
|
141
|
+
error,
|
|
142
|
+
onRetry,
|
|
143
|
+
children: [children, /* @__PURE__ */ jsx(Announcer, {})]
|
|
144
|
+
})
|
|
145
|
+
})
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* A finished table from a TanStack table: headings that sort, rows, a
|
|
150
|
+
* search field, a column chooser, checkboxes and a pagination bar, each
|
|
151
|
+
* one there if the table was made with the feature for it.
|
|
152
|
+
*/
|
|
153
|
+
function DataTable(props) {
|
|
154
|
+
return /* @__PURE__ */ jsx(DataTableLayout, {
|
|
155
|
+
...props,
|
|
156
|
+
content: (content) => /* @__PURE__ */ jsx(DataTableContent, { ...content })
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
function DataTableLayout({ content: renderContent, table, labels, rowHeader, getRowLabel, loading, error, onRetry, className, toolbar, bulkActions, pagination, pageSizes, ...content }) {
|
|
160
|
+
return /* @__PURE__ */ jsxs(DataTableRoot, {
|
|
161
|
+
table,
|
|
162
|
+
labels,
|
|
163
|
+
rowHeader,
|
|
164
|
+
getRowLabel,
|
|
165
|
+
loading,
|
|
166
|
+
error,
|
|
167
|
+
onRetry,
|
|
168
|
+
className,
|
|
169
|
+
children: [
|
|
170
|
+
toolbar === void 0 ? /* @__PURE__ */ jsx(DataTableToolbar, {}) : toolbar,
|
|
171
|
+
bulkActions !== void 0 ? /* @__PURE__ */ jsx(DataTableBulkActions, { children: bulkActions }) : null,
|
|
172
|
+
renderContent(content),
|
|
173
|
+
pagination === void 0 ? /* @__PURE__ */ jsx(DataTablePagination, { pageSizes }) : pagination
|
|
174
|
+
]
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
//#endregion
|
|
178
|
+
export { DataTable, DataTableLayout, DataTableRoot };
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import "./context.js";
|
|
2
|
+
import { Column, RowData, TableFeatures } from "@tanstack/react-table";
|
|
3
|
+
import { ReactNode } from "react";
|
|
4
|
+
//#region src/components/data-table/header.d.ts
|
|
5
|
+
export interface DataTableColumnHeaderProps<TFeatures extends TableFeatures = any, TData extends RowData = any> {
|
|
6
|
+
/**
|
|
7
|
+
* The column this is the heading of. Inside a column's `header` it's
|
|
8
|
+
* found without being passed.
|
|
9
|
+
*/
|
|
10
|
+
column?: Column<TFeatures, TData, any>;
|
|
11
|
+
/** The heading. Without it, the column's own label is used. */
|
|
12
|
+
children?: ReactNode;
|
|
13
|
+
className?: string;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* A column's heading. When the column can be sorted it's a button that
|
|
17
|
+
* sorts by it: ascending, then descending, then not at all. Shift and a
|
|
18
|
+
* click adds the column to a sort by several.
|
|
19
|
+
*
|
|
20
|
+
* A column whose `header` is a string gets this without asking.
|
|
21
|
+
*/
|
|
22
|
+
export declare function DataTableColumnHeader<TFeatures extends TableFeatures, TData extends RowData>({ column: given, children, className }: DataTableColumnHeaderProps<TFeatures, TData>): import("react").JSX.Element;
|
|
23
|
+
//#endregion
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { canSort, columnLabel, sorted, useDataTableView, useHeader } from "./context.js";
|
|
3
|
+
import { cx } from "../../utils/cx.js";
|
|
4
|
+
import { useShiftClick } from "../../utils/use-shift-click.js";
|
|
5
|
+
import { useDirection } from "@nuvui/react/direction";
|
|
6
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
7
|
+
//#region src/components/data-table/header.tsx
|
|
8
|
+
function SortIcon({ direction }) {
|
|
9
|
+
return /* @__PURE__ */ jsxs("svg", {
|
|
10
|
+
"aria-hidden": "true",
|
|
11
|
+
className: "nuv-data-table__sort-icon",
|
|
12
|
+
"data-direction": direction || void 0,
|
|
13
|
+
viewBox: "0 0 16 16",
|
|
14
|
+
width: "16",
|
|
15
|
+
height: "16",
|
|
16
|
+
fill: "none",
|
|
17
|
+
stroke: "currentColor",
|
|
18
|
+
strokeWidth: "1.5",
|
|
19
|
+
strokeLinecap: "round",
|
|
20
|
+
strokeLinejoin: "round",
|
|
21
|
+
children: [direction !== "desc" ? /* @__PURE__ */ jsx("path", { d: "M5 6.5l3-3 3 3" }) : null, direction !== "asc" ? /* @__PURE__ */ jsx("path", { d: "M5 9.5l3 3 3-3" }) : null]
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* A column's heading. When the column can be sorted it's a button that
|
|
26
|
+
* sorts by it: ascending, then descending, then not at all. Shift and a
|
|
27
|
+
* click adds the column to a sort by several.
|
|
28
|
+
*
|
|
29
|
+
* A column whose `header` is a string gets this without asking.
|
|
30
|
+
*/
|
|
31
|
+
function DataTableColumnHeader({ column: given, children, className }) {
|
|
32
|
+
useDataTableView("DataTableColumnHeader");
|
|
33
|
+
const click = useShiftClick();
|
|
34
|
+
const header = useHeader();
|
|
35
|
+
const column = given ?? header?.column;
|
|
36
|
+
if (!column) throw new Error("DataTableColumnHeader needs a column, or to be inside a column's header.");
|
|
37
|
+
const content = children ?? columnLabel(column);
|
|
38
|
+
if (!canSort(column)) return /* @__PURE__ */ jsx("span", {
|
|
39
|
+
className: cx("nuv-data-table__heading", className),
|
|
40
|
+
children: content
|
|
41
|
+
});
|
|
42
|
+
const direction = sorted(column);
|
|
43
|
+
const index = column.getSortIndex();
|
|
44
|
+
const several = (column.table.atoms.sorting?.get().length ?? 0) > 1;
|
|
45
|
+
return /* @__PURE__ */ jsxs("button", {
|
|
46
|
+
type: "button",
|
|
47
|
+
className: cx("nuv-data-table__sort", className),
|
|
48
|
+
"data-sorted": direction || void 0,
|
|
49
|
+
...click.props,
|
|
50
|
+
onClick: (event) => {
|
|
51
|
+
const shiftKey = click.shift(event);
|
|
52
|
+
column.getToggleSortingHandler()?.(shiftKey === event.shiftKey ? event : {
|
|
53
|
+
...event,
|
|
54
|
+
shiftKey
|
|
55
|
+
});
|
|
56
|
+
},
|
|
57
|
+
onMouseDown: (event) => {
|
|
58
|
+
if (event.shiftKey) event.preventDefault();
|
|
59
|
+
},
|
|
60
|
+
children: [
|
|
61
|
+
/* @__PURE__ */ jsx("span", {
|
|
62
|
+
className: "nuv-data-table__heading",
|
|
63
|
+
children: content
|
|
64
|
+
}),
|
|
65
|
+
/* @__PURE__ */ jsx(SortIcon, { direction }),
|
|
66
|
+
direction && several ? /* @__PURE__ */ jsx("span", {
|
|
67
|
+
className: "nuv-data-table__sort-index",
|
|
68
|
+
"aria-hidden": "true",
|
|
69
|
+
children: index + 1
|
|
70
|
+
}) : null
|
|
71
|
+
]
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
const step = 16;
|
|
75
|
+
const bigStep = 64;
|
|
76
|
+
/**
|
|
77
|
+
* The grip at the edge of a heading that changes the column's width. The
|
|
78
|
+
* data table puts one on every column that can be resized.
|
|
79
|
+
*/
|
|
80
|
+
function DataTableResizeHandle({ header }) {
|
|
81
|
+
const { labels } = useDataTableView("DataTableResizeHandle");
|
|
82
|
+
const direction = useDirection();
|
|
83
|
+
const { column } = header;
|
|
84
|
+
const size = column.getSize();
|
|
85
|
+
const min = column.columnDef.minSize ?? 20;
|
|
86
|
+
const max = column.columnDef.maxSize ?? Number.MAX_SAFE_INTEGER;
|
|
87
|
+
const resize = (next) => column.table.setColumnSizing((old) => ({
|
|
88
|
+
...old,
|
|
89
|
+
[column.id]: Math.min(Math.max(next, min), max)
|
|
90
|
+
}));
|
|
91
|
+
const onKeyDown = (event) => {
|
|
92
|
+
const amount = event.shiftKey ? bigStep : step;
|
|
93
|
+
const wider = direction === "rtl" ? "ArrowLeft" : "ArrowRight";
|
|
94
|
+
const narrower = direction === "rtl" ? "ArrowRight" : "ArrowLeft";
|
|
95
|
+
if (event.key === wider) resize(size + amount);
|
|
96
|
+
else if (event.key === narrower) resize(size - amount);
|
|
97
|
+
else if (event.key === "Home") resize(min);
|
|
98
|
+
else if (event.key === "Enter") column.resetSize();
|
|
99
|
+
else return;
|
|
100
|
+
event.preventDefault();
|
|
101
|
+
};
|
|
102
|
+
const start = header.getResizeHandler();
|
|
103
|
+
return /* @__PURE__ */ jsx("div", {
|
|
104
|
+
role: "separator",
|
|
105
|
+
"aria-orientation": "vertical",
|
|
106
|
+
"aria-label": labels.resizeColumn(columnLabel(column)),
|
|
107
|
+
"aria-valuenow": Math.round(size),
|
|
108
|
+
"aria-valuemin": min,
|
|
109
|
+
"aria-valuemax": max === Number.MAX_SAFE_INTEGER ? void 0 : max,
|
|
110
|
+
tabIndex: 0,
|
|
111
|
+
className: "nuv-data-table__resize",
|
|
112
|
+
"data-resizing": column.getIsResizing() ? "" : void 0,
|
|
113
|
+
onMouseDown: start,
|
|
114
|
+
onTouchStart: start,
|
|
115
|
+
onDoubleClick: () => column.resetSize(),
|
|
116
|
+
onKeyDown
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
//#endregion
|
|
120
|
+
export { DataTableColumnHeader, DataTableResizeHandle };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { DataTableLabels, defaultLabels } from "./labels.js";
|
|
2
|
+
import { DataTableColumnMeta } from "./context.js";
|
|
3
|
+
import { DataTableContent, DataTableContentOwnProps, DataTableContentProps, DataTableName } from "./content.js";
|
|
4
|
+
import { DataTableColumnHeader, DataTableColumnHeaderProps } from "./header.js";
|
|
5
|
+
import { DataTablePagination, DataTablePaginationOwnProps, DataTablePaginationProps } from "./pagination.js";
|
|
6
|
+
import { DataTableBulkActions, DataTableBulkActionsProps, DataTableExpandToggle, DataTableExpandToggleProps, DataTableSelectAll, DataTableSelectAllProps, DataTableSelectRow, DataTableSelectRowProps } from "./selection.js";
|
|
7
|
+
import { DataTableColumnChooser, DataTableColumnChooserProps, DataTableColumnFilter, DataTableColumnFilterProps, DataTableFilter, DataTableFilterOwnProps, DataTableFilterProps, DataTableToolbar, DataTableToolbarProps } from "./toolbar.js";
|
|
8
|
+
import { DataTableColumnHelper, DataTableHookOptions, createDataTableHook } from "./create-data-table-hook.js";
|
|
9
|
+
import { DataTable, DataTableOwnProps, DataTableProps, DataTableRoot, DataTableRootOwnProps, DataTableRootProps } from "./data-table.js";
|
|
10
|
+
import { DataTableRequest, DataTableRequestOptions, useDataTableRequest } from "./use-data-table-request.js";
|
|
11
|
+
export { DataTable, DataTableBulkActions, type DataTableBulkActionsProps, DataTableColumnChooser, type DataTableColumnChooserProps, DataTableColumnFilter, type DataTableColumnFilterProps, DataTableColumnHeader, type DataTableColumnHeaderProps, type DataTableColumnHelper, type DataTableColumnMeta, DataTableContent, type DataTableContentOwnProps, type DataTableContentProps, DataTableExpandToggle, type DataTableExpandToggleProps, DataTableFilter, type DataTableFilterOwnProps, type DataTableFilterProps, type DataTableHookOptions, type DataTableLabels, type DataTableName, type DataTableOwnProps, DataTablePagination, type DataTablePaginationOwnProps, type DataTablePaginationProps, type DataTableProps, type DataTableRequest, type DataTableRequestOptions, DataTableRoot, type DataTableRootOwnProps, type DataTableRootProps, DataTableSelectAll, type DataTableSelectAllProps, DataTableSelectRow, type DataTableSelectRowProps, DataTableToolbar, type DataTableToolbarProps, createDataTableHook, defaultLabels, useDataTableRequest };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { DataTableColumnHeader } from "./header.js";
|
|
2
|
+
import { DataTablePagination } from "./pagination.js";
|
|
3
|
+
import { DataTableBulkActions, DataTableExpandToggle, DataTableSelectAll, DataTableSelectRow } from "./selection.js";
|
|
4
|
+
import { DataTableColumnChooser, DataTableColumnFilter, DataTableFilter, DataTableToolbar } from "./toolbar.js";
|
|
5
|
+
import { createDataTableHook } from "./create-data-table-hook.js";
|
|
6
|
+
import { DataTableContent } from "./content.js";
|
|
7
|
+
import { defaultLabels } from "./labels.js";
|
|
8
|
+
import { DataTable, DataTableRoot } from "./data-table.js";
|
|
9
|
+
import { useDataTableRequest } from "./use-data-table-request.js";
|
|
10
|
+
export { DataTable, DataTableBulkActions, DataTableColumnChooser, DataTableColumnFilter, DataTableColumnHeader, DataTableContent, DataTableExpandToggle, DataTableFilter, DataTablePagination, DataTableRoot, DataTableSelectAll, DataTableSelectRow, DataTableToolbar, createDataTableHook, defaultLabels, useDataTableRequest };
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
//#region src/components/data-table/labels.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Every piece of text the data table writes itself. Pass the ones you want
|
|
4
|
+
* to change, or all of them in another language, as `labels`.
|
|
5
|
+
*/
|
|
6
|
+
export interface DataTableLabels {
|
|
7
|
+
/** The search field's name and placeholder. */
|
|
8
|
+
search: string;
|
|
9
|
+
/** The column chooser's button. */
|
|
10
|
+
columns: string;
|
|
11
|
+
/** Shown in place of rows when there are none. */
|
|
12
|
+
empty: string;
|
|
13
|
+
/** Shown in place of rows when a filter leaves none. */
|
|
14
|
+
noMatches: string;
|
|
15
|
+
clearFilters: string;
|
|
16
|
+
/** Shown when `error` is `true` and not a message of your own. */
|
|
17
|
+
error: string;
|
|
18
|
+
retry: string;
|
|
19
|
+
selectAll: string;
|
|
20
|
+
/** The header checkbox of a table with pages. */
|
|
21
|
+
selectPage: string;
|
|
22
|
+
selectRow: (row: string) => string;
|
|
23
|
+
/** A row's name when it has no `rowHeader` column: its place in the list. */
|
|
24
|
+
rowName: (position: number) => string;
|
|
25
|
+
expandRow: (row: string) => string;
|
|
26
|
+
collapseRow: (row: string) => string;
|
|
27
|
+
selected: (count: number) => string;
|
|
28
|
+
clearSelection: string;
|
|
29
|
+
/** The name of the bar that holds the actions for selected rows. */
|
|
30
|
+
bulkActions: string;
|
|
31
|
+
pagination: string;
|
|
32
|
+
rowsPerPage: string;
|
|
33
|
+
/** "21-40 of 143". */
|
|
34
|
+
pageRange: (from: number, to: number, total: number) => string;
|
|
35
|
+
page: (page: number, pages: number) => string;
|
|
36
|
+
firstPage: string;
|
|
37
|
+
previousPage: string;
|
|
38
|
+
nextPage: string;
|
|
39
|
+
lastPage: string;
|
|
40
|
+
resizeColumn: (column: string) => string;
|
|
41
|
+
filterColumn: (column: string) => string;
|
|
42
|
+
/** Read out after a sort. */
|
|
43
|
+
sortedAscending: (column: string) => string;
|
|
44
|
+
sortedDescending: (column: string) => string;
|
|
45
|
+
sortCleared: string;
|
|
46
|
+
/** Read out after a filter changes how many rows there are. */
|
|
47
|
+
results: (count: number) => string;
|
|
48
|
+
}
|
|
49
|
+
export declare const defaultLabels: DataTableLabels;
|
|
50
|
+
//#endregion
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
//#region src/components/data-table/labels.ts
|
|
2
|
+
const defaultLabels = {
|
|
3
|
+
search: "Search",
|
|
4
|
+
columns: "Columns",
|
|
5
|
+
empty: "No rows.",
|
|
6
|
+
noMatches: "No rows match.",
|
|
7
|
+
clearFilters: "Clear filters",
|
|
8
|
+
error: "The rows couldn't be loaded.",
|
|
9
|
+
retry: "Try again",
|
|
10
|
+
selectAll: "Select all rows",
|
|
11
|
+
selectPage: "Select all rows on this page",
|
|
12
|
+
selectRow: (row) => `Select ${row}`,
|
|
13
|
+
rowName: (position) => `row ${position}`,
|
|
14
|
+
expandRow: (row) => `Show details for ${row}`,
|
|
15
|
+
collapseRow: (row) => `Hide details for ${row}`,
|
|
16
|
+
selected: (count) => `${count} selected`,
|
|
17
|
+
clearSelection: "Clear selection",
|
|
18
|
+
bulkActions: "Selected rows",
|
|
19
|
+
pagination: "Pagination",
|
|
20
|
+
rowsPerPage: "Rows per page",
|
|
21
|
+
pageRange: (from, to, total) => total === 0 ? "0 of 0" : `${from}–${to} of ${total}`,
|
|
22
|
+
page: (page, pages) => `Page ${page} of ${pages}`,
|
|
23
|
+
firstPage: "First page",
|
|
24
|
+
previousPage: "Previous page",
|
|
25
|
+
nextPage: "Next page",
|
|
26
|
+
lastPage: "Last page",
|
|
27
|
+
resizeColumn: (column) => `Resize ${column}`,
|
|
28
|
+
filterColumn: (column) => `Filter ${column}`,
|
|
29
|
+
sortedAscending: (column) => `Sorted by ${column}, ascending`,
|
|
30
|
+
sortedDescending: (column) => `Sorted by ${column}, descending`,
|
|
31
|
+
sortCleared: "Sorting cleared",
|
|
32
|
+
results: (count) => count === 1 ? "1 row" : `${count} rows`
|
|
33
|
+
};
|
|
34
|
+
//#endregion
|
|
35
|
+
export { defaultLabels };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { HTMLAttributes } from "react";
|
|
2
|
+
//#region src/components/data-table/pagination.d.ts
|
|
3
|
+
export interface DataTablePaginationOwnProps {
|
|
4
|
+
/**
|
|
5
|
+
* The choices for rows per page. The size the table has now is added if
|
|
6
|
+
* it isn't one of them. Pass an empty list to leave the select out.
|
|
7
|
+
* @default [10, 20, 50, 100]
|
|
8
|
+
*/
|
|
9
|
+
pageSizes?: number[];
|
|
10
|
+
}
|
|
11
|
+
export interface DataTablePaginationProps extends DataTablePaginationOwnProps, HTMLAttributes<HTMLDivElement> {}
|
|
12
|
+
/**
|
|
13
|
+
* The bar under the table: rows per page, which rows are showing out of
|
|
14
|
+
* how many, and buttons for the first, previous, next and last page. It
|
|
15
|
+
* needs `rowPaginationFeature`, and shows nothing without it.
|
|
16
|
+
*/
|
|
17
|
+
export declare function DataTablePagination({ pageSizes, className, ...props }: DataTablePaginationProps): import("react").JSX.Element | null;
|
|
18
|
+
//#endregion
|