@mendylanda/ui 0.2.0 → 0.3.0-alpha.1
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/README.md +22 -0
- package/dist/class-names.js +1 -1
- package/dist/styles.css +162 -0
- package/dist/styles.tailwind3.css +162 -0
- package/dist/table/clipboard.d.ts +6 -0
- package/dist/table/clipboard.js +51 -0
- package/dist/table/columns.d.ts +60 -0
- package/dist/table/columns.js +72 -0
- package/dist/table/data-table.d.ts +14 -0
- package/dist/table/data-table.js +15 -0
- package/dist/table/features.d.ts +34 -0
- package/dist/table/features.js +35 -0
- package/dist/table/index.d.ts +9 -0
- package/dist/table/index.js +9 -0
- package/dist/table/state.d.ts +31 -0
- package/dist/table/state.js +48 -0
- package/dist/table/table-controls.d.ts +23 -0
- package/dist/table/table-controls.js +99 -0
- package/dist/table/table-feedback.d.ts +7 -0
- package/dist/table/table-feedback.js +11 -0
- package/dist/table/table-layout.d.ts +43 -0
- package/dist/table/table-layout.js +51 -0
- package/dist/table/table-loading.d.ts +11 -0
- package/dist/table/table-loading.js +5 -0
- package/dist/table/table-parts.d.ts +24 -0
- package/dist/table/table-parts.js +67 -0
- package/dist/table/table-view.d.ts +35 -0
- package/dist/table/table-view.js +78 -0
- package/dist/table/use-data-table.d.ts +24 -0
- package/dist/table/use-data-table.js +100 -0
- package/dist/table/use-result-selection.d.ts +17 -0
- package/dist/table/use-result-selection.js +53 -0
- package/dist/table/use-table-interaction.d.ts +15 -0
- package/dist/table/use-table-interaction.js +125 -0
- package/dist/table/use-viewport-width.d.ts +3 -0
- package/dist/table/use-viewport-width.js +17 -0
- package/package.json +14 -7
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { ColumnPinningState, ColumnSizingState, ColumnVisibilityState, SortingState } from "@tanstack/react-table";
|
|
2
|
+
import type { TableColumn } from "./columns.js";
|
|
3
|
+
export interface TablePreferences {
|
|
4
|
+
version: 1;
|
|
5
|
+
columnOrder: string[];
|
|
6
|
+
columnVisibility: ColumnVisibilityState;
|
|
7
|
+
columnSizing: ColumnSizingState;
|
|
8
|
+
columnPinning: ColumnPinningState;
|
|
9
|
+
}
|
|
10
|
+
export interface PreferenceStorage {
|
|
11
|
+
read: (key: string) => unknown | Promise<unknown>;
|
|
12
|
+
write: (key: string, value: TablePreferences) => void | Promise<void>;
|
|
13
|
+
}
|
|
14
|
+
export interface SavedTableView<F = unknown> {
|
|
15
|
+
id: string;
|
|
16
|
+
name: string;
|
|
17
|
+
filters: F;
|
|
18
|
+
sorting: SortingState;
|
|
19
|
+
preferences: TablePreferences;
|
|
20
|
+
}
|
|
21
|
+
export type TableSelection<Q = unknown> = {
|
|
22
|
+
mode: "ids";
|
|
23
|
+
ids: string[];
|
|
24
|
+
} | {
|
|
25
|
+
mode: "matching";
|
|
26
|
+
scope: Q;
|
|
27
|
+
excludedIds: string[];
|
|
28
|
+
};
|
|
29
|
+
export declare function reconcilePreferences<T extends object>(input: unknown, columns: readonly TableColumn<T>[], defaults?: Partial<TablePreferences>): TablePreferences;
|
|
30
|
+
export declare function csvValue(value: string): string;
|
|
31
|
+
export declare function tsvValue(value: string): string;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
export function reconcilePreferences(input, columns, defaults) {
|
|
2
|
+
const ids = columns.map((c) => c.id ?? ("accessorKey" in c ? String(c.accessorKey) : ""));
|
|
3
|
+
const allowed = new Set(ids);
|
|
4
|
+
const byId = new Map(columns.map((column, index) => [ids[index], column]));
|
|
5
|
+
const raw = input && typeof input === "object" && "version" in input && input.version === 1
|
|
6
|
+
? input
|
|
7
|
+
: (defaults ?? {});
|
|
8
|
+
const order = [
|
|
9
|
+
...new Set(Array.isArray(raw.columnOrder)
|
|
10
|
+
? raw.columnOrder.filter((v) => typeof v === "string" && allowed.has(v))
|
|
11
|
+
: []),
|
|
12
|
+
];
|
|
13
|
+
const visibility = Object.fromEntries(columns
|
|
14
|
+
.filter((c) => c.defaultHidden && c.enableHiding !== false)
|
|
15
|
+
.map((c) => [c.id ?? String("accessorKey" in c ? c.accessorKey : ""), false]));
|
|
16
|
+
const sizes = {};
|
|
17
|
+
for (const [id, v] of Object.entries(raw.columnVisibility ?? {}))
|
|
18
|
+
if (allowed.has(id) && byId.get(id)?.enableHiding !== false && typeof v === "boolean")
|
|
19
|
+
visibility[id] = v;
|
|
20
|
+
for (const [id, v] of Object.entries(raw.columnSizing ?? {})) {
|
|
21
|
+
const column = byId.get(id);
|
|
22
|
+
if (column && typeof v === "number" && Number.isFinite(v))
|
|
23
|
+
sizes[id] = Math.min(column.maxSize ?? 1200, Math.max(column.minSize ?? 48, v));
|
|
24
|
+
}
|
|
25
|
+
const pin = (side) => [
|
|
26
|
+
...new Set((Array.isArray(raw.columnPinning?.[side])
|
|
27
|
+
? raw.columnPinning[side]
|
|
28
|
+
: columns.filter((c) => c.pin === side).map((c) => c.id)).filter((id) => allowed.has(id))),
|
|
29
|
+
];
|
|
30
|
+
const start = pin("start");
|
|
31
|
+
const ordered = new Set(order);
|
|
32
|
+
const startSet = new Set(start);
|
|
33
|
+
return {
|
|
34
|
+
version: 1,
|
|
35
|
+
columnOrder: [...order, ...ids.filter((id) => !ordered.has(id))],
|
|
36
|
+
columnVisibility: visibility,
|
|
37
|
+
columnSizing: sizes,
|
|
38
|
+
columnPinning: { start, end: pin("end").filter((id) => !startSet.has(id)) },
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
export function csvValue(value) {
|
|
42
|
+
// Spreadsheet programs interpret formula prefixes even in quoted CSV fields.
|
|
43
|
+
const safe = /^[=+@\-\t\r]/.test(value) ? `'${value}` : value;
|
|
44
|
+
return `"${safe.replaceAll('"', '""')}"`;
|
|
45
|
+
}
|
|
46
|
+
export function tsvValue(value) {
|
|
47
|
+
return /["\t\r\n]/.test(value) ? `"${value.replaceAll('"', '""')}"` : value;
|
|
48
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
import type { DataTableInstance } from "./use-data-table.js";
|
|
3
|
+
import type { TableColumn } from "./columns.js";
|
|
4
|
+
import type { SavedTableView } from "./state.js";
|
|
5
|
+
export declare function TableColumnSettings<T extends object>({ table }: {
|
|
6
|
+
table: DataTableInstance<T>;
|
|
7
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
8
|
+
export declare function TablePagination<T extends object>({ table, loading, }: {
|
|
9
|
+
table: DataTableInstance<T>;
|
|
10
|
+
loading?: boolean;
|
|
11
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
12
|
+
export declare function selectionColumn<T extends object>(): TableColumn<T>;
|
|
13
|
+
export declare function TableActionBar({ count, children, onClear, }: {
|
|
14
|
+
count: number;
|
|
15
|
+
children: ReactNode;
|
|
16
|
+
onClear: () => void;
|
|
17
|
+
}): import("react/jsx-runtime").JSX.Element | null;
|
|
18
|
+
export declare function TableSavedViews<F>({ views, onApply, onSave, onDelete, }: {
|
|
19
|
+
views: readonly SavedTableView<F>[];
|
|
20
|
+
onApply: (view: SavedTableView<F>) => void;
|
|
21
|
+
onSave: (name: string) => Promise<unknown>;
|
|
22
|
+
onDelete?: (view: SavedTableView<F>) => Promise<unknown>;
|
|
23
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useState } from "react";
|
|
4
|
+
import { ChevronLeft, ChevronRight, SlidersHorizontal } from "lucide-react";
|
|
5
|
+
import { Button } from "../primitives/button.js";
|
|
6
|
+
import { Checkbox } from "../primitives/checkbox.js";
|
|
7
|
+
import { Input } from "../primitives/input.js";
|
|
8
|
+
import { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem, DropdownMenuCheckboxItem, DropdownMenuSeparator, DropdownMenuSub, DropdownMenuSubTrigger, DropdownMenuSubContent, } from "../primitives/dropdown-menu.js";
|
|
9
|
+
import { applyTablePreferences } from "./use-data-table.js";
|
|
10
|
+
export function TableColumnSettings({ table }) {
|
|
11
|
+
const columns = table.getAllLeafColumns();
|
|
12
|
+
const byId = new Map(columns.map((column) => [column.id, column]));
|
|
13
|
+
const orderedIds = new Set(table.state.columnOrder);
|
|
14
|
+
const order = [
|
|
15
|
+
...table.state.columnOrder,
|
|
16
|
+
...columns.map((column) => column.id).filter((id) => !orderedIds.has(id)),
|
|
17
|
+
];
|
|
18
|
+
function move(id, delta) {
|
|
19
|
+
const next = [...order], index = next.indexOf(id), destination = index + delta;
|
|
20
|
+
if (index < 0 || destination < 0 || destination >= next.length)
|
|
21
|
+
return;
|
|
22
|
+
next.splice(index, 1);
|
|
23
|
+
next.splice(destination, 0, id);
|
|
24
|
+
table.setColumnOrder(next);
|
|
25
|
+
}
|
|
26
|
+
return (_jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsxs(Button, { variant: "outline", size: "sm", "aria-label": "Column settings", children: [_jsx(SlidersHorizontal, { className: "mui-100c22d5776b" }), _jsx("span", { children: "Columns" })] }) }), _jsxs(DropdownMenuContent, { align: "end", className: "mui-b057efba3283 mui-72b37c1a040d mui-1a26a0d28420", "aria-label": "Column settings", onCloseAutoFocus: (event) => {
|
|
27
|
+
// The close animation can finish after the user has already selected a cell.
|
|
28
|
+
if (document.activeElement?.closest('[role="grid"]'))
|
|
29
|
+
event.preventDefault();
|
|
30
|
+
}, children: [order.map((id, index) => {
|
|
31
|
+
const column = byId.get(id);
|
|
32
|
+
if (!column)
|
|
33
|
+
return null;
|
|
34
|
+
const definition = column.columnDef;
|
|
35
|
+
const label = definition.label ?? (typeof definition.header === "string" ? definition.header : id);
|
|
36
|
+
return (_jsxs(DropdownMenuSub, { children: [_jsx(DropdownMenuSubTrigger, { children: label }), _jsxs(DropdownMenuSubContent, { children: [_jsx(DropdownMenuCheckboxItem, { checked: column.getIsVisible(), disabled: !column.getCanHide(), onSelect: (event) => event.preventDefault(), onCheckedChange: (value) => column.toggleVisibility(value), children: "Visible" }), _jsx(DropdownMenuSeparator, {}), _jsxs(DropdownMenuItem, { disabled: !column.getCanPin(), onSelect: (event) => {
|
|
37
|
+
event.preventDefault();
|
|
38
|
+
column.pin(column.getIsPinned() === "start" ? false : "start");
|
|
39
|
+
}, children: [" ", column.getIsPinned() === "start" ? "Unpin" : "Pin to start"] }), _jsx(DropdownMenuItem, { disabled: !column.getCanPin(), onSelect: (event) => {
|
|
40
|
+
event.preventDefault();
|
|
41
|
+
column.pin(column.getIsPinned() === "end" ? false : "end");
|
|
42
|
+
}, children: column.getIsPinned() === "end" ? "Unpin" : "Pin to end" }), _jsx(DropdownMenuItem, { disabled: index === 0, onSelect: (event) => {
|
|
43
|
+
event.preventDefault();
|
|
44
|
+
move(id, -1);
|
|
45
|
+
}, children: "Move earlier" }), _jsx(DropdownMenuItem, { disabled: index === order.length - 1, onSelect: (event) => {
|
|
46
|
+
event.preventDefault();
|
|
47
|
+
move(id, 1);
|
|
48
|
+
}, children: "Move later" })] })] }, id));
|
|
49
|
+
}), _jsx(DropdownMenuSeparator, {}), _jsx(DropdownMenuItem, { onSelect: () => applyTablePreferences(table, { version: 1, ...table.initialState }), children: "Reset columns" })] })] }));
|
|
50
|
+
}
|
|
51
|
+
export function TablePagination({ table, loading, }) {
|
|
52
|
+
return (_jsxs("nav", { "data-mendy-ui": "", "aria-label": "Table pagination", className: "mui-222f930b8752 mui-faa8a23c68f6 mui-71556df3b421 mui-307644c34db6 mui-d7db3451d154 mui-b5edc3ea7c91 mui-c74ab393b96d", children: [_jsxs("span", { children: ["Page ", table.state.pagination.pageIndex + 1, table.getPageCount() >= 0 ? ` of ${Math.max(1, table.getPageCount())}` : ""] }), _jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsxs(Button, { variant: "outline", size: "sm", children: [table.state.pagination.pageSize, " rows"] }) }), _jsx(DropdownMenuContent, { children: [10, 20, 50, 100].map((size) => (_jsxs(DropdownMenuItem, { onSelect: () => table.setPageSize(size), children: [size, " rows"] }, size))) })] }), _jsx(Button, { variant: "outline", size: "icon-sm", disabled: loading || !table.getCanPreviousPage(), "aria-label": "Previous page", onClick: () => table.previousPage(), children: _jsx(ChevronLeft, { className: "mui-100c22d5776b" }) }), _jsx(Button, { variant: "outline", size: "icon-sm", disabled: loading || !table.getCanNextPage(), "aria-label": "Next page", onClick: () => table.nextPage(), children: _jsx(ChevronRight, { className: "mui-100c22d5776b" }) })] }));
|
|
53
|
+
}
|
|
54
|
+
export function selectionColumn() {
|
|
55
|
+
return {
|
|
56
|
+
id: "_selection",
|
|
57
|
+
label: "Select rows",
|
|
58
|
+
size: 44,
|
|
59
|
+
minSize: 44,
|
|
60
|
+
maxSize: 44,
|
|
61
|
+
enableSorting: false,
|
|
62
|
+
enableHiding: false,
|
|
63
|
+
enableResizing: false,
|
|
64
|
+
enableCellSelection: false,
|
|
65
|
+
pin: "start",
|
|
66
|
+
exportOptions: false,
|
|
67
|
+
header: ({ table }) => (_jsx(Checkbox, { "data-mendy-ui": "", "aria-label": "Select loaded rows", checked: table.getIsAllPageRowsSelected() || (table.getIsSomePageRowsSelected() && "indeterminate"), onCheckedChange: (value) => table.toggleAllPageRowsSelected(value === true) })),
|
|
68
|
+
cell: ({ row }) => (_jsx(Checkbox, { "data-mendy-ui": "", "aria-label": `Select row ${row.id}`, checked: row.getIsSelected(), disabled: !row.getCanSelect(), onCheckedChange: (value) => row.toggleSelected(value === true) })),
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
export function TableActionBar({ count, children, onClear, }) {
|
|
72
|
+
if (!count)
|
|
73
|
+
return null;
|
|
74
|
+
return (_jsxs("div", { "data-mendy-ui": "", role: "region", "aria-label": "Selected row actions", className: "mui-964a9431ff49 mui-a5c36273c0f0 mui-fbb62f5cb6fe mui-1752f11474a6 mui-222f930b8752 mui-8cfb5a42ef9f mui-81b198a2db84 mui-faa8a23c68f6 mui-71556df3b421 mui-d7db3451d154 mui-3fa8c572949b mui-4f1a55de40bc mui-5b272f3c5076 mui-0f9e6672913a mui-b5edc3ea7c91 mui-94ea94fde25f", children: [_jsxs("span", { className: "mui-c74ab393b96d", children: [count, " selected"] }), children, _jsx(Button, { variant: "ghost", size: "sm", onClick: onClear, children: "Clear selection" })] }));
|
|
75
|
+
}
|
|
76
|
+
export function TableSavedViews({ views, onApply, onSave, onDelete, }) {
|
|
77
|
+
const [name, setName] = useState("");
|
|
78
|
+
const [pending, setPending] = useState(false);
|
|
79
|
+
const [error, setError] = useState(null);
|
|
80
|
+
async function run(action) {
|
|
81
|
+
setPending(true);
|
|
82
|
+
setError(null);
|
|
83
|
+
try {
|
|
84
|
+
await action();
|
|
85
|
+
setName("");
|
|
86
|
+
}
|
|
87
|
+
catch (reason) {
|
|
88
|
+
setError(reason instanceof Error ? reason.message : "Could not save changes.");
|
|
89
|
+
}
|
|
90
|
+
finally {
|
|
91
|
+
setPending(false);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return (_jsxs("div", { "data-mendy-ui": "", className: "mui-222f930b8752 mui-faa8a23c68f6 mui-71556df3b421 mui-074569488cca", children: [_jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsx(Button, { size: "sm", variant: "outline", children: "Saved views" }) }), _jsx(DropdownMenuContent, { children: views.length ? (views.map((view) => (_jsxs(DropdownMenuSub, { children: [_jsx(DropdownMenuSubTrigger, { children: view.name }), _jsxs(DropdownMenuSubContent, { children: [_jsx(DropdownMenuItem, { onSelect: () => onApply(view), children: "Apply" }), onDelete && (_jsx(DropdownMenuItem, { disabled: pending, onSelect: () => void run(() => onDelete(view)), children: "Delete" }))] })] }, view.id)))) : (_jsx(DropdownMenuItem, { disabled: true, children: "No saved views" })) })] }), _jsxs("form", { className: "mui-222f930b8752 mui-71556df3b421 mui-074569488cca", onSubmit: (event) => {
|
|
95
|
+
event.preventDefault();
|
|
96
|
+
if (name.trim() && !pending)
|
|
97
|
+
void run(() => onSave(name.trim()));
|
|
98
|
+
}, children: [_jsx(Input, { "aria-label": "New view name", value: name, onChange: (event) => setName(event.target.value), className: "mui-1fb07b38c63b mui-4ac4ec8dfbce" }), _jsx(Button, { type: "submit", size: "sm", disabled: pending || !name.trim(), children: "Save view" })] }), error && (_jsx("span", { role: "alert", className: "mui-c74ab393b96d mui-887b9502d5c7", children: error }))] }));
|
|
99
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
import type { TableDataState } from "./table-view.js";
|
|
3
|
+
export declare function TableInitialState({ status, hasRows, loadingState, emptyState, error, retry, }: Pick<TableDataState, "status" | "error" | "retry"> & {
|
|
4
|
+
hasRows: boolean;
|
|
5
|
+
loadingState?: ReactNode;
|
|
6
|
+
emptyState?: ReactNode;
|
|
7
|
+
}): string | number | bigint | boolean | import("react/jsx-runtime").JSX.Element | Iterable<ReactNode> | Promise<string | number | bigint | boolean | import("react").ReactPortal | import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>> | Iterable<ReactNode> | null | undefined> | null;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Button } from "../primitives/button.js";
|
|
3
|
+
export function TableInitialState({ status, hasRows, loadingState, emptyState, error, retry, }) {
|
|
4
|
+
if (hasRows)
|
|
5
|
+
return null;
|
|
6
|
+
if (status === "loading")
|
|
7
|
+
return loadingState ?? null;
|
|
8
|
+
if (status === "error")
|
|
9
|
+
return (_jsxs("div", { role: "alert", className: "mui-c432836760e3 mui-05ab12448317", children: [error ?? "Could not load rows.", retry && (_jsx(Button, { variant: "outline", size: "sm", onClick: retry, children: "Retry" }))] }));
|
|
10
|
+
return (_jsx("div", { role: "status", className: "mui-c7902d77ad80 mui-05ab12448317 mui-35f35c41d134", children: emptyState ?? "No results." }));
|
|
11
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { CSSProperties } from "react";
|
|
2
|
+
import type { Column } from "@tanstack/react-table";
|
|
3
|
+
import type { DataTableFeatures } from "./features.js";
|
|
4
|
+
import type { DataTableInstance } from "./use-data-table.js";
|
|
5
|
+
/** Keep headers, skeletons, and loaded cells on the same column geometry. */
|
|
6
|
+
export declare function tableLayout<T extends object>(table: DataTableInstance<T>, viewportWidth: number | null, rowHeight: number): {
|
|
7
|
+
columns: Column<{
|
|
8
|
+
cellSelectionFeature: import("@tanstack/react-table").TableFeature;
|
|
9
|
+
columnResizingFeature: import("@tanstack/react-table").TableFeature;
|
|
10
|
+
columnOrderingFeature: import("@tanstack/react-table").TableFeature;
|
|
11
|
+
columnPinningFeature: import("@tanstack/react-table").TableFeature;
|
|
12
|
+
columnSizingFeature: import("@tanstack/react-table").TableFeature;
|
|
13
|
+
columnVisibilityFeature: import("@tanstack/react-table").TableFeature;
|
|
14
|
+
columnFilteringFeature: import("@tanstack/react-table").TableFeature;
|
|
15
|
+
columnFacetingFeature: import("@tanstack/react-table").TableFeature;
|
|
16
|
+
globalFilteringFeature: import("@tanstack/react-table").TableFeature;
|
|
17
|
+
rowPaginationFeature: import("@tanstack/react-table").TableFeature;
|
|
18
|
+
rowSelectionFeature: import("@tanstack/react-table").TableFeature;
|
|
19
|
+
rowSortingFeature: import("@tanstack/react-table").TableFeature;
|
|
20
|
+
filteredRowModel: (table: import("@tanstack/react-table").Table<any, any>) => () => import("@tanstack/react-table").RowModel<any, any>;
|
|
21
|
+
sortedRowModel: (table: import("@tanstack/react-table").Table<any, any>) => () => import("@tanstack/react-table").RowModel<any, any>;
|
|
22
|
+
paginatedRowModel: (table: import("@tanstack/react-table").Table<any, any>) => () => import("@tanstack/react-table").RowModel<any, any>;
|
|
23
|
+
facetedRowModel: (table: import("@tanstack/react-table").Table<any, any>, columnId: string) => () => import("@tanstack/react-table").RowModel<any, any>;
|
|
24
|
+
facetedUniqueValues: (table: import("@tanstack/react-table").Table<import("@tanstack/react-table").TableFeatures, any>, columnId: string) => () => Map<any, number>;
|
|
25
|
+
filterFns: {
|
|
26
|
+
includesString: import("@tanstack/react-table").CreatedFilterFn<any, any>;
|
|
27
|
+
inNumberRange: import("@tanstack/react-table").CreatedFilterFn<any, any>;
|
|
28
|
+
inDateRange: import("@tanstack/react-table").CreatedFilterFn<any, any>;
|
|
29
|
+
equals: import("@tanstack/react-table").CreatedFilterFn<any, any>;
|
|
30
|
+
arrIncludes: import("@tanstack/react-table").CreatedFilterFn<any, any>;
|
|
31
|
+
weakEquals: import("@tanstack/react-table").CreatedFilterFn<any, any>;
|
|
32
|
+
};
|
|
33
|
+
sortFns: {
|
|
34
|
+
alphanumeric: import("@tanstack/react-table").CreatedSortFn<any, any>;
|
|
35
|
+
basic: import("@tanstack/react-table").CreatedSortFn<any, any>;
|
|
36
|
+
datetime: import("@tanstack/react-table").CreatedSortFn<any, any>;
|
|
37
|
+
text: import("@tanstack/react-table").CreatedSortFn<any, any>;
|
|
38
|
+
};
|
|
39
|
+
}, T, unknown>[];
|
|
40
|
+
totalWidth: number;
|
|
41
|
+
pinningActive: boolean;
|
|
42
|
+
cellStyle: (column: Column<DataTableFeatures, T>) => CSSProperties;
|
|
43
|
+
};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/** Keep headers, skeletons, and loaded cells on the same column geometry. */
|
|
2
|
+
export function tableLayout(table, viewportWidth, rowHeight) {
|
|
3
|
+
const columns = [
|
|
4
|
+
...table.getStartVisibleLeafColumns(),
|
|
5
|
+
...table.getCenterVisibleLeafColumns(),
|
|
6
|
+
...table.getEndVisibleLeafColumns(),
|
|
7
|
+
];
|
|
8
|
+
const totalWidth = columns.reduce((sum, column) => sum + column.getSize(), 0);
|
|
9
|
+
const pinnedWidth = [
|
|
10
|
+
...table.getStartVisibleLeafColumns(),
|
|
11
|
+
...table.getEndVisibleLeafColumns(),
|
|
12
|
+
].reduce((sum, column) => sum + column.getSize(), 0);
|
|
13
|
+
// Preserve stored pins, but leave room to reach the middle columns on narrow screens.
|
|
14
|
+
const pinningActive = (viewportWidth ?? Infinity) >= Math.min(totalWidth, pinnedWidth + 120);
|
|
15
|
+
const positions = new Map();
|
|
16
|
+
let offset = 0;
|
|
17
|
+
for (const column of table.getStartVisibleLeafColumns()) {
|
|
18
|
+
positions.set(column.id, { side: "left", offset });
|
|
19
|
+
offset += column.getSize();
|
|
20
|
+
}
|
|
21
|
+
offset = 0;
|
|
22
|
+
for (const column of [...table.getEndVisibleLeafColumns()].reverse()) {
|
|
23
|
+
positions.set(column.id, { side: "right", offset });
|
|
24
|
+
offset += column.getSize();
|
|
25
|
+
}
|
|
26
|
+
function cellStyle(column) {
|
|
27
|
+
const pin = pinningActive ? positions.get(column.id) : undefined;
|
|
28
|
+
return {
|
|
29
|
+
width: column.getSize(),
|
|
30
|
+
minWidth: column.getSize(),
|
|
31
|
+
height: rowHeight,
|
|
32
|
+
...(column.id === table.getEndVisibleLeafColumns()[0]?.id
|
|
33
|
+
? { marginInlineStart: "auto" }
|
|
34
|
+
: {}),
|
|
35
|
+
...(pin
|
|
36
|
+
? {
|
|
37
|
+
position: "sticky",
|
|
38
|
+
[pin.side]: pin.offset,
|
|
39
|
+
zIndex: 2,
|
|
40
|
+
...(column.id === table.getStartVisibleLeafColumns().at(-1)?.id
|
|
41
|
+
? { borderRightWidth: 4 }
|
|
42
|
+
: {}),
|
|
43
|
+
...(column.id === table.getEndVisibleLeafColumns()[0]?.id
|
|
44
|
+
? { borderLeftWidth: 4 }
|
|
45
|
+
: {}),
|
|
46
|
+
}
|
|
47
|
+
: {}),
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
return { columns, totalWidth, pinningActive, cellStyle };
|
|
51
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { CSSProperties } from "react";
|
|
2
|
+
import type { Column } from "@tanstack/react-table";
|
|
3
|
+
import type { DataTableFeatures } from "./features.js";
|
|
4
|
+
/** Loading shares the live column geometry and its single scroll container. */
|
|
5
|
+
export declare function TableLoadingRows<T extends object>({ columns, count, width, rowHeight, cellStyle, }: {
|
|
6
|
+
columns: Column<DataTableFeatures, T>[];
|
|
7
|
+
count: number;
|
|
8
|
+
width: number;
|
|
9
|
+
rowHeight: number;
|
|
10
|
+
cellStyle: (column: Column<DataTableFeatures, T>) => CSSProperties;
|
|
11
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
/** Loading shares the live column geometry and its single scroll container. */
|
|
3
|
+
export function TableLoadingRows({ columns, count, width, rowHeight, cellStyle, }) {
|
|
4
|
+
return (_jsx("div", { role: "status", "aria-label": "Loading rows", className: "mui-2bf6510f1330", style: { width }, children: Array.from({ length: count }, (_, index) => (_jsx("div", { "aria-hidden": "true", "data-slot": "table-loading-row", className: "mui-222f930b8752 mui-2bf6510f1330", style: { height: rowHeight }, children: columns.map((column) => (_jsx("div", { "data-slot": "table-loading-cell", "data-column-id": column.id, className: "mui-222f930b8752 mui-27ead27a81df mui-71556df3b421 mui-bbe39cfb5cc6 mui-3b6d5fc7b061 mui-5b272f3c5076 mui-0f9e6672913a", style: cellStyle(column), children: _jsx("div", { className: "mui-2edce89ef182 mui-56bb14cbd1fe mui-7b3cd77eb999 mui-02e603944040 mui-87ecb59f2d2d mui-237747164b60" }) }, column.id))) }, index))) }));
|
|
5
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { CSSProperties, ReactNode } from "react";
|
|
2
|
+
import type { Column, Header, Row } from "@tanstack/react-table";
|
|
3
|
+
import type { DataTableFeatures } from "./features.js";
|
|
4
|
+
import type { DataTableInstance } from "./use-data-table.js";
|
|
5
|
+
export declare function TableHeaderCell<T extends object>({ table, header, index, style, renderHeader, contentClassName, }: {
|
|
6
|
+
table: DataTableInstance<T>;
|
|
7
|
+
header: Header<DataTableFeatures, T, unknown>;
|
|
8
|
+
index: number;
|
|
9
|
+
style: CSSProperties;
|
|
10
|
+
renderHeader?: (header: Header<DataTableFeatures, T, unknown>) => ReactNode;
|
|
11
|
+
contentClassName?: string;
|
|
12
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
13
|
+
export declare function TableBodyRow<T extends object>({ row, rowIndex, start, rowHeight, cellStyle, focusedId, copied, contentClassName, onRowActivate, isRowHighlighted, }: {
|
|
14
|
+
row: Row<DataTableFeatures, T>;
|
|
15
|
+
rowIndex: number;
|
|
16
|
+
start: number;
|
|
17
|
+
rowHeight: number;
|
|
18
|
+
cellStyle: (column: Column<DataTableFeatures, T>) => CSSProperties;
|
|
19
|
+
focusedId?: string;
|
|
20
|
+
copied: boolean;
|
|
21
|
+
contentClassName?: string;
|
|
22
|
+
onRowActivate?: (row: T) => void;
|
|
23
|
+
isRowHighlighted?: (row: T) => boolean;
|
|
24
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { flexRender } from "@tanstack/react-table";
|
|
4
|
+
import { ArrowDown, ArrowUp } from "lucide-react";
|
|
5
|
+
import { Button } from "../primitives/button.js";
|
|
6
|
+
import { cn } from "../utils.js";
|
|
7
|
+
import { interactiveSelector } from "./clipboard.js";
|
|
8
|
+
export function TableHeaderCell({ table, header, index, style, renderHeader, contentClassName, }) {
|
|
9
|
+
const column = header.column;
|
|
10
|
+
const definition = column.columnDef;
|
|
11
|
+
const title = definition.label ?? (typeof definition.header === "string" ? definition.header : column.id);
|
|
12
|
+
const sorted = column.getIsSorted();
|
|
13
|
+
return (_jsxs("div", { role: "columnheader", "aria-colindex": index + 1, "aria-sort": sorted === "asc" ? "ascending" : sorted === "desc" ? "descending" : undefined, style: style, className: "mui-d2d9e1f13413 mui-222f930b8752 mui-27ead27a81df mui-71556df3b421 mui-19e97f3f40e6 mui-3b6d5fc7b061 mui-5b272f3c5076 mui-0f9e6672913a mui-daaac3fbf55e mui-35f35c41d134 mui-beeef6663906", children: [_jsx("div", { className: cn("mui-184ddc11e5f9 mui-7bd5bab6d7f4 mui-d5111d0e9f48", (renderHeader || typeof definition.header === "function") && contentClassName), children: _jsx(TableHeaderContent, { header: header, renderHeader: renderHeader, title: title }) }), column.getCanResize() && _jsx(TableResizeHandle, { table: table, header: header, title: title })] }, column.id));
|
|
14
|
+
}
|
|
15
|
+
export function TableBodyRow({ row, rowIndex, start, rowHeight, cellStyle, focusedId, copied, contentClassName, onRowActivate, isRowHighlighted, }) {
|
|
16
|
+
const cells = [
|
|
17
|
+
...row.getStartVisibleCells(),
|
|
18
|
+
...row.getCenterVisibleCells(),
|
|
19
|
+
...row.getEndVisibleCells(),
|
|
20
|
+
];
|
|
21
|
+
return (_jsx("div", { role: "row", "aria-rowindex": rowIndex + 2, "aria-selected": row.getIsSelected(), "data-highlighted": isRowHighlighted?.(row.original), "data-index": rowIndex, className: "mui-ad936fcbed63 mui-747355bdc2a2 mui-635702706586 mui-98599e4ee250 mui-222f930b8752 mui-2bf6510f1330 mui-63b29dace366 mui-9d7d1760cd8e", style: { height: rowHeight, transform: `translateY(${start}px)` }, children: cells.map((cell, index) => {
|
|
22
|
+
const selected = cell.getIsSelected();
|
|
23
|
+
const definition = cell.column.columnDef;
|
|
24
|
+
const edge = selected ? cell.getSelectionEdges() : null;
|
|
25
|
+
return (_jsx("div", { role: "gridcell", "aria-colindex": index + 1, "aria-selected": selected, "data-row-id": row.id, "data-column-id": cell.column.id, tabIndex: focusedId ? (focusedId === cell.id ? 0 : -1) : rowIndex === 0 && index === 0 ? 0 : -1, style: {
|
|
26
|
+
...cellStyle(cell.column),
|
|
27
|
+
...(selected
|
|
28
|
+
? {
|
|
29
|
+
boxShadow: `${edge?.top ? "inset 0 2px var(--primary)," : ""}${edge?.bottom ? "inset 0 -2px var(--primary)," : ""}${edge?.left ? "inset 2px 0 var(--primary)," : ""}${edge?.right ? "inset -2px 0 var(--primary)," : ""} inset 0 0 0 0 transparent`,
|
|
30
|
+
}
|
|
31
|
+
: {}),
|
|
32
|
+
}, className: cn("mui-222f930b8752 mui-27ead27a81df mui-71556df3b421 mui-d5111d0e9f48 mui-bbe39cfb5cc6 mui-3b6d5fc7b061 mui-5b272f3c5076 mui-0f9e6672913a mui-97b1c00ff005 mui-e6020023567a mui-a47d391255f3 mui-a8719ed8b095 mui-2b1c7d88acff", definition.align === "end" && "mui-307644c34db6 mui-10558dbb3a24", definition.align === "center" && "mui-a503dd374cca mui-05ab12448317", selected && "mui-292affc1f780", selected && copied && "mui-047624ec0c39 mui-d254c96aea74"), onMouseDown: (event) => {
|
|
33
|
+
if (event.button !== 0 ||
|
|
34
|
+
event.detail > 1 ||
|
|
35
|
+
!cell.getCanSelect() ||
|
|
36
|
+
event.target.closest(interactiveSelector) ||
|
|
37
|
+
window.getSelection()?.toString())
|
|
38
|
+
return;
|
|
39
|
+
event.preventDefault();
|
|
40
|
+
event.stopPropagation();
|
|
41
|
+
event.currentTarget.focus({ preventScroll: true });
|
|
42
|
+
cell.getSelectionStartHandler()(event);
|
|
43
|
+
}, onMouseEnter: cell.getSelectionExtendHandler(), onDoubleClick: (event) => {
|
|
44
|
+
if (!event.target.closest(interactiveSelector))
|
|
45
|
+
onRowActivate?.(row.original);
|
|
46
|
+
}, children: _jsx("div", { className: cn("mui-184ddc11e5f9 mui-81b198a2db84 mui-5a3a4ee420c2", contentClassName), children: flexRender(cell.column.columnDef.cell ?? (() => String(cell.getValue() ?? "")), cell.getContext()) }) }, cell.id));
|
|
47
|
+
}) }, row.id));
|
|
48
|
+
}
|
|
49
|
+
function TableResizeHandle({ table, header, title, }) {
|
|
50
|
+
const column = header.column;
|
|
51
|
+
return (_jsx("div", { role: "separator", tabIndex: 0, "aria-label": `Resize ${title}`, "aria-orientation": "vertical", "aria-valuenow": Math.round(column.getSize()), "aria-valuemin": column.columnDef.minSize ?? 48, "aria-valuemax": column.columnDef.maxSize ?? 1200, onDoubleClick: () => column.resetSize(), onMouseDown: header.getResizeHandler(), onTouchStart: header.getResizeHandler(), onKeyDown: (event) => {
|
|
52
|
+
if (event.key === "ArrowLeft" || event.key === "ArrowRight") {
|
|
53
|
+
event.preventDefault();
|
|
54
|
+
event.stopPropagation();
|
|
55
|
+
table.setColumnSizing((sizes) => ({
|
|
56
|
+
...sizes,
|
|
57
|
+
[column.id]: Math.min(column.columnDef.maxSize ?? 1200, Math.max(column.columnDef.minSize ?? 48, column.getSize() + (event.key === "ArrowLeft" ? -10 : 10))),
|
|
58
|
+
}));
|
|
59
|
+
}
|
|
60
|
+
}, className: "mui-747355bdc2a2 mui-eb9e48c5dda9 mui-a74c4477017c mui-418e44ad4f9e mui-d4ea834145ae mui-2194d094b585 mui-739a9a000791 mui-4273133a876a mui-2f669cfad5aa mui-2e357972fc10" }));
|
|
61
|
+
}
|
|
62
|
+
function TableHeaderContent({ header, renderHeader, title, }) {
|
|
63
|
+
const column = header.column;
|
|
64
|
+
const definition = column.columnDef;
|
|
65
|
+
const sorted = column.getIsSorted();
|
|
66
|
+
return renderHeader ? (renderHeader(header)) : typeof definition.header === "function" ? (flexRender(definition.header, header.getContext())) : !column.getCanSort() ? (_jsx("span", { className: "mui-496aca80e4d8 mui-5a3a4ee420c2", title: title, children: title })) : (_jsxs(Button, { variant: "ghost", size: "sm", className: "mui-1fb07b38c63b mui-81b198a2db84 mui-c62ec162662e mui-19e97f3f40e6 mui-16e909e6f01d mui-daaac3fbf55e", onClick: column.getToggleSortingHandler(), title: title, children: [_jsx("span", { className: "mui-5a3a4ee420c2", children: title }), sorted === "asc" ? (_jsx(ArrowUp, { className: "mui-5e34f5313859 mui-27ead27a81df" })) : sorted === "desc" ? (_jsx(ArrowDown, { className: "mui-5e34f5313859 mui-27ead27a81df" })) : null] }));
|
|
67
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { ReactNode, Ref } from "react";
|
|
2
|
+
import type { Header } from "@tanstack/react-table";
|
|
3
|
+
import type { DataTableFeatures } from "./features.js";
|
|
4
|
+
import type { DataTableInstance } from "./use-data-table.js";
|
|
5
|
+
export interface TableDataState {
|
|
6
|
+
status?: "loading" | "ready" | "error";
|
|
7
|
+
error?: ReactNode;
|
|
8
|
+
retry?: () => void;
|
|
9
|
+
refreshing?: boolean;
|
|
10
|
+
loadMore?: {
|
|
11
|
+
available: boolean;
|
|
12
|
+
loading: boolean;
|
|
13
|
+
load: () => unknown;
|
|
14
|
+
error?: ReactNode;
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
export interface TableViewProps<T extends object> extends TableDataState {
|
|
18
|
+
table: DataTableInstance<T>;
|
|
19
|
+
label?: string;
|
|
20
|
+
height?: number | string;
|
|
21
|
+
rowHeight?: number;
|
|
22
|
+
className?: string;
|
|
23
|
+
/** Theme adapter for application-owned renderers, not package controls. */
|
|
24
|
+
contentClassName?: string;
|
|
25
|
+
emptyState?: ReactNode;
|
|
26
|
+
loadingState?: ReactNode;
|
|
27
|
+
scrollRef?: Ref<HTMLDivElement>;
|
|
28
|
+
/** Change when the result set changes, not when another page is appended. */
|
|
29
|
+
queryKey?: string;
|
|
30
|
+
renderHeader?: (header: Header<DataTableFeatures, T, unknown>) => ReactNode;
|
|
31
|
+
onRowActivate?: (row: T) => void;
|
|
32
|
+
isRowHighlighted?: (row: T) => boolean;
|
|
33
|
+
onCopyError?: (error: unknown) => void;
|
|
34
|
+
}
|
|
35
|
+
export declare function TableView<T extends object>({ table, label, height, rowHeight, className, contentClassName, emptyState, loadingState, scrollRef, status, refreshing, error, retry, loadMore, queryKey, renderHeader, onRowActivate, isRowHighlighted, onCopyError, }: TableViewProps<T>): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useCallback, useEffect, useLayoutEffect, useRef } from "react";
|
|
4
|
+
import { useVirtualizer } from "@tanstack/react-virtual";
|
|
5
|
+
import { TableHeaderCell, TableBodyRow } from "./table-parts.js";
|
|
6
|
+
import { Button } from "../primitives/button.js";
|
|
7
|
+
import { cn } from "../utils.js";
|
|
8
|
+
import { useViewportWidth } from "./use-viewport-width.js";
|
|
9
|
+
import { tableLayout } from "./table-layout.js";
|
|
10
|
+
import { TableLoadingRows } from "./table-loading.js";
|
|
11
|
+
import { TableInitialState } from "./table-feedback.js";
|
|
12
|
+
import { useTableInteraction } from "./use-table-interaction.js";
|
|
13
|
+
export function TableView({ table, label = "Data table", height = "min(65dvh, 640px)", rowHeight = 44, className, contentClassName, emptyState, loadingState, scrollRef, status = "ready", refreshing, error, retry, loadMore, queryKey, renderHeader, onRowActivate, isRowHighlighted, onCopyError, }) {
|
|
14
|
+
const container = useRef(null);
|
|
15
|
+
const setContainer = useCallback((node) => {
|
|
16
|
+
container.current = node;
|
|
17
|
+
if (typeof scrollRef === "function")
|
|
18
|
+
scrollRef(node);
|
|
19
|
+
else if (scrollRef)
|
|
20
|
+
scrollRef.current = node;
|
|
21
|
+
}, [scrollRef]);
|
|
22
|
+
const rows = table.getRowModel().rows;
|
|
23
|
+
const virtual = useVirtualizer({
|
|
24
|
+
count: rows.length,
|
|
25
|
+
getScrollElement: () => container.current,
|
|
26
|
+
estimateSize: () => rowHeight,
|
|
27
|
+
getItemKey: (index) => rows[index]?.id ?? index,
|
|
28
|
+
overscan: 8,
|
|
29
|
+
});
|
|
30
|
+
const viewportWidth = useViewportWidth(container);
|
|
31
|
+
const { columns, totalWidth, pinningActive, cellStyle } = tableLayout(table, viewportWidth, rowHeight);
|
|
32
|
+
const { announcement, copied, onKeyDown } = useTableInteraction({
|
|
33
|
+
table,
|
|
34
|
+
pinningActive,
|
|
35
|
+
container,
|
|
36
|
+
queryKey,
|
|
37
|
+
onRowActivate,
|
|
38
|
+
onCopyError,
|
|
39
|
+
scrollToIndex: (index) => virtual.scrollToIndex(index, { align: "auto" }),
|
|
40
|
+
});
|
|
41
|
+
const items = virtual.getVirtualItems();
|
|
42
|
+
const last = items.at(-1)?.index ?? -1;
|
|
43
|
+
const requested = useRef(null);
|
|
44
|
+
const load = useRef(loadMore);
|
|
45
|
+
useLayoutEffect(() => {
|
|
46
|
+
load.current = loadMore;
|
|
47
|
+
}, [loadMore]);
|
|
48
|
+
useEffect(() => {
|
|
49
|
+
const data = load.current;
|
|
50
|
+
const requestKey = `${queryKey}:${rows.length}`;
|
|
51
|
+
if (!data?.available ||
|
|
52
|
+
data.loading ||
|
|
53
|
+
data.error ||
|
|
54
|
+
last < rows.length - 10 ||
|
|
55
|
+
requested.current === requestKey)
|
|
56
|
+
return;
|
|
57
|
+
requested.current = requestKey;
|
|
58
|
+
Promise.resolve()
|
|
59
|
+
.then(() => data.load())
|
|
60
|
+
.catch(() => {
|
|
61
|
+
requested.current = null;
|
|
62
|
+
});
|
|
63
|
+
}, [last, rows.length, queryKey, loadMore?.available, loadMore?.loading, loadMore?.error]);
|
|
64
|
+
const focused = table.getFocusedCell();
|
|
65
|
+
const headersById = new Map(table.getFlatHeaders().map((header) => [header.column.id, header]));
|
|
66
|
+
return (_jsxs("div", { "data-mendy-ui": "", className: "mui-184ddc11e5f9", children: [_jsx("div", { "aria-live": "polite", className: "mui-32fb090591d9", children: announcement }), _jsxs("div", { ref: setContainer, role: "grid", "aria-label": label, "aria-rowcount": rows.length + 1, "aria-colcount": columns.length, "aria-busy": status === "loading" || refreshing, style: { height }, className: cn("mui-d2d9e1f13413 mui-014aadadffad mui-1a26a0d28420 mui-3fa8c572949b mui-4f1a55de40bc mui-5b272f3c5076 mui-c74ab393b96d mui-b30fc56058b6", className), onKeyDown: onKeyDown, children: [_jsx("div", { role: "row", "aria-rowindex": 1, className: "mui-964a9431ff49 mui-98599e4ee250 mui-4e8f0a87a0dc mui-222f930b8752 mui-2bf6510f1330 mui-bbe39cfb5cc6 mui-5b272f3c5076", style: { width: totalWidth }, children: columns.map((column, index) => (_jsx(TableHeaderCell, { table: table, header: headersById.get(column.id), index: index, style: cellStyle(column), renderHeader: renderHeader, contentClassName: contentClassName }, column.id))) }), _jsx(TableInitialState, { status: status, hasRows: rows.length > 0, loadingState: loadingState ?? (_jsx(TableLoadingRows, { columns: columns, width: totalWidth, rowHeight: rowHeight, cellStyle: cellStyle, count: Math.max(1, Math.ceil(((virtual.scrollRect?.height ?? 400) - rowHeight) / rowHeight)) })), emptyState: emptyState, error: error, retry: retry }), _jsx("div", { role: "rowgroup", className: "mui-d2d9e1f13413 mui-2bf6510f1330", style: { height: virtual.getTotalSize(), width: totalWidth }, children: items.map((item) => (_jsx(TableBodyRow, { row: rows[item.index], rowIndex: item.index, start: item.start, rowHeight: rowHeight, cellStyle: cellStyle, focusedId: focused?.id, copied: copied, contentClassName: contentClassName, onRowActivate: onRowActivate, isRowHighlighted: isRowHighlighted }, item.key))) }), rows.length > 0 && status === "error" && (_jsxs("div", { role: "alert", className: "mui-964a9431ff49 mui-30150dd033ab mui-5b272f3c5076 mui-094f5333853b", children: [error ?? "Could not refresh rows.", retry && (_jsx(Button, { onClick: retry, variant: "outline", size: "sm", children: "Retry" }))] })), loadMore?.available && (_jsxs("div", { className: "mui-964a9431ff49 mui-635702706586 mui-222f930b8752 mui-a503dd374cca mui-b97db4a9f432", children: [_jsx(Button, { size: "sm", variant: "ghost", disabled: loadMore.loading, onClick: () => {
|
|
67
|
+
requested.current = null;
|
|
68
|
+
void Promise.resolve()
|
|
69
|
+
.then(() => loadMore.load())
|
|
70
|
+
.catch(() => {
|
|
71
|
+
requested.current = null;
|
|
72
|
+
});
|
|
73
|
+
}, children: loadMore.loading
|
|
74
|
+
? "Loading more…"
|
|
75
|
+
: loadMore.error
|
|
76
|
+
? "Retry loading more"
|
|
77
|
+
: "Load more" }), loadMore.error && _jsx("span", { role: "alert", children: loadMore.error })] }))] })] }));
|
|
78
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { TableOptions, ReactTable } from "@tanstack/react-table";
|
|
2
|
+
import type { TableColumn } from "./columns.js";
|
|
3
|
+
import type { DataTableFeatures } from "./features.js";
|
|
4
|
+
import type { PreferenceStorage, TablePreferences } from "./state.js";
|
|
5
|
+
export type DataTableInstance<T extends object> = ReactTable<DataTableFeatures, T>;
|
|
6
|
+
export interface UseDataTableOptions<T extends object> extends Omit<TableOptions<DataTableFeatures, T>, "features" | "data" | "columns" | "manualFiltering" | "manualSorting" | "manualPagination"> {
|
|
7
|
+
rows: T[];
|
|
8
|
+
columns: TableColumn<T>[];
|
|
9
|
+
getRowId: (row: T, index: number) => string;
|
|
10
|
+
processing?: {
|
|
11
|
+
filtering?: "client" | "external";
|
|
12
|
+
sorting?: "client" | "external";
|
|
13
|
+
pagination?: "client" | "external" | "off";
|
|
14
|
+
};
|
|
15
|
+
preferences?: {
|
|
16
|
+
key: string;
|
|
17
|
+
scope: string;
|
|
18
|
+
storage?: PreferenceStorage;
|
|
19
|
+
onError?: (error: unknown) => void;
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
export declare function captureTablePreferences<T extends object>(table: DataTableInstance<T>): TablePreferences;
|
|
23
|
+
export declare function applyTablePreferences<T extends object>(table: DataTableInstance<T>, input: unknown): void;
|
|
24
|
+
export declare function useDataTable<T extends object>({ rows, columns, processing, preferences, ...options }: UseDataTableOptions<T>): DataTableInstance<T>;
|