@rovula/ui 0.1.28 → 0.1.29
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/bundle.css +501 -67
- package/dist/cjs/bundle.js +589 -589
- package/dist/cjs/bundle.js.map +1 -1
- package/dist/cjs/types/components/DataTable/DataTable.d.ts +195 -4
- package/dist/cjs/types/components/DataTable/DataTable.editing.d.ts +20 -0
- package/dist/cjs/types/components/DataTable/DataTable.editing.types.d.ts +145 -0
- package/dist/cjs/types/components/DataTable/DataTable.stories.d.ts +268 -6
- package/dist/cjs/types/components/Dropdown/Dropdown.d.ts +22 -0
- package/dist/cjs/types/components/Dropdown/Dropdown.stories.d.ts +4 -0
- package/dist/cjs/types/components/ScrollArea/ScrollArea.d.ts +3 -3
- package/dist/cjs/types/components/ScrollArea/ScrollArea.stories.d.ts +4 -0
- package/dist/cjs/types/components/Table/Table.d.ts +33 -3
- package/dist/cjs/types/components/Table/Table.stories.d.ts +86 -4
- package/dist/cjs/types/components/TextInput/TextInput.stories.d.ts +8 -0
- package/dist/cjs/types/components/TextInput/TextInput.styles.d.ts +1 -0
- package/dist/components/DataTable/DataTable.editing.js +385 -0
- package/dist/components/DataTable/DataTable.editing.types.js +1 -0
- package/dist/components/DataTable/DataTable.js +983 -50
- package/dist/components/DataTable/DataTable.stories.js +1077 -25
- package/dist/components/Dropdown/Dropdown.js +8 -6
- package/dist/components/ScrollArea/ScrollArea.js +2 -2
- package/dist/components/ScrollArea/ScrollArea.stories.js +68 -2
- package/dist/components/Table/Table.js +103 -13
- package/dist/components/Table/Table.stories.js +226 -9
- package/dist/components/TextInput/TextInput.js +6 -4
- package/dist/components/TextInput/TextInput.stories.js +8 -0
- package/dist/components/TextInput/TextInput.styles.js +7 -1
- package/dist/esm/bundle.css +501 -67
- package/dist/esm/bundle.js +1545 -1545
- package/dist/esm/bundle.js.map +1 -1
- package/dist/esm/types/components/DataTable/DataTable.d.ts +195 -4
- package/dist/esm/types/components/DataTable/DataTable.editing.d.ts +20 -0
- package/dist/esm/types/components/DataTable/DataTable.editing.types.d.ts +145 -0
- package/dist/esm/types/components/DataTable/DataTable.stories.d.ts +268 -6
- package/dist/esm/types/components/Dropdown/Dropdown.d.ts +22 -0
- package/dist/esm/types/components/Dropdown/Dropdown.stories.d.ts +4 -0
- package/dist/esm/types/components/ScrollArea/ScrollArea.d.ts +3 -3
- package/dist/esm/types/components/ScrollArea/ScrollArea.stories.d.ts +4 -0
- package/dist/esm/types/components/Table/Table.d.ts +33 -3
- package/dist/esm/types/components/Table/Table.stories.d.ts +86 -4
- package/dist/esm/types/components/TextInput/TextInput.stories.d.ts +8 -0
- package/dist/esm/types/components/TextInput/TextInput.styles.d.ts +1 -0
- package/dist/index.d.ts +493 -122
- package/dist/src/theme/global.css +747 -96
- package/package.json +14 -2
- package/src/components/DataTable/DataTable.editing.tsx +861 -0
- package/src/components/DataTable/DataTable.editing.types.ts +192 -0
- package/src/components/DataTable/DataTable.stories.tsx +2169 -31
- package/src/components/DataTable/DataTable.test.tsx +696 -0
- package/src/components/DataTable/DataTable.tsx +2260 -94
- package/src/components/Dropdown/Dropdown.tsx +22 -6
- package/src/components/ScrollArea/ScrollArea.stories.tsx +146 -3
- package/src/components/ScrollArea/ScrollArea.tsx +6 -6
- package/src/components/Table/Table.stories.tsx +789 -44
- package/src/components/Table/Table.tsx +294 -28
- package/src/components/TextInput/TextInput.stories.tsx +80 -0
- package/src/components/TextInput/TextInput.styles.ts +7 -1
- package/src/components/TextInput/TextInput.tsx +21 -14
- package/src/test/setup.ts +50 -0
- package/src/theme/global.css +81 -42
- package/src/theme/presets/colors.js +12 -0
- package/src/theme/themes/variable.css +27 -28
- package/src/theme/tokens/baseline.css +2 -1
- package/src/theme/tokens/components/scrollbar.css +9 -4
- package/src/theme/tokens/components/table.css +63 -0
|
@@ -1,9 +1,200 @@
|
|
|
1
|
-
import { ColumnDef, SortingState } from "@tanstack/react-table";
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import { Cell, ColumnDef, ExpandedState, Header, PaginationState, Row, RowSelectionState, RowData, SortingState } from "@tanstack/react-table";
|
|
2
|
+
import React from "react";
|
|
3
|
+
declare module "@tanstack/react-table" {
|
|
4
|
+
interface ColumnMeta<TData extends RowData, TValue> {
|
|
5
|
+
exactWidth?: number | string;
|
|
6
|
+
align?: "left" | "center" | "right";
|
|
7
|
+
cellClassName?: string;
|
|
8
|
+
headerCellClassName?: string;
|
|
9
|
+
colSpan?: number | ((row: Row<TData>) => number);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
/** TanStack Table types used by `DataTable` props — import from `@rovula/ui` to stay aligned with this package and `ColumnMeta` augmentation. */
|
|
13
|
+
export type { Cell, ColumnDef, ExpandedState, Header, PaginationState, Row, RowData, RowSelectionState, SortingState, Table as TanstackTable, } from "@tanstack/react-table";
|
|
14
|
+
export type { EditableColumnDef, EditDisplayMode, EditTrigger, DataTableEditingProps, } from "./DataTable.editing.types";
|
|
15
|
+
import type { EditableColumnDef, DataTableEditingProps } from "./DataTable.editing.types";
|
|
16
|
+
export type DataTablePaginationMode = "server" | "client" | "infinite";
|
|
17
|
+
export interface DataTableProps<TData, TValue> extends DataTableEditingProps<TData> {
|
|
18
|
+
columns: (ColumnDef<TData, TValue> | EditableColumnDef<TData, TValue>)[];
|
|
4
19
|
data: TData[];
|
|
5
20
|
manualSorting?: boolean;
|
|
6
21
|
onSorting?: (sorting: SortingState) => void;
|
|
22
|
+
/**
|
|
23
|
+
* "client" – DataTable manages page state internally with TanStack
|
|
24
|
+
* "server" – caller controls page state; pass pageIndex/pageSize/totalCount
|
|
25
|
+
* "infinite" – no pagination bar; fetchMoreData enables infinite scroll
|
|
26
|
+
*/
|
|
27
|
+
paginationMode?: DataTablePaginationMode;
|
|
28
|
+
/** Required for "server" mode */
|
|
29
|
+
totalCount?: number;
|
|
30
|
+
pageIndex?: number;
|
|
31
|
+
pageSize?: number;
|
|
32
|
+
onPaginationChange?: (state: PaginationState) => void;
|
|
33
|
+
pageSizeOptions?: number[];
|
|
34
|
+
/** Called when user scrolls near bottom (infinite mode). */
|
|
7
35
|
fetchMoreData?: () => void;
|
|
36
|
+
/**
|
|
37
|
+
* Threshold in pixels from the bottom of the scroll container at which
|
|
38
|
+
* `fetchMoreData` is triggered. Defaults to 10px.
|
|
39
|
+
*/
|
|
40
|
+
fetchMoreOffset?: number;
|
|
41
|
+
/**
|
|
42
|
+
* When `paginationMode` is `"infinite"`, shows a built-in footer row (spinner +
|
|
43
|
+
* label) and suppresses additional `fetchMoreData` calls until this is false again.
|
|
44
|
+
*/
|
|
45
|
+
fetchingMore?: boolean;
|
|
46
|
+
/** Accessible label next to the spinner (default: "Loading more…"). */
|
|
47
|
+
fetchingMoreLabel?: string;
|
|
48
|
+
/**
|
|
49
|
+
* When there are no rows yet, shows a built-in loading state instead of the
|
|
50
|
+
* empty placeholder. Also suppresses `fetchMoreData` while true (infinite mode).
|
|
51
|
+
*/
|
|
52
|
+
loading?: boolean;
|
|
53
|
+
/** Label for the initial-loading state (default: "Loading…"). */
|
|
54
|
+
loadingLabel?: string;
|
|
55
|
+
/**
|
|
56
|
+
* When true, DataTable will only render a vertical window of rows based on
|
|
57
|
+
* the scroll position of its internal scroll container. This is a basic
|
|
58
|
+
* fixed-row-height virtualizer intended for large but finite lists.
|
|
59
|
+
*
|
|
60
|
+
* Notes / limitations:
|
|
61
|
+
* - Only vertical row virtualization is supported.
|
|
62
|
+
* - Assumes approximately fixed row height; pass `virtualRowEstimate` to tune.
|
|
63
|
+
* - Works with selection, row actions, and basic infinite scroll, but more
|
|
64
|
+
* complex combinations (tree rows, reorder, etc.) may have visual quirks.
|
|
65
|
+
*/
|
|
66
|
+
virtualized?: boolean;
|
|
67
|
+
/**
|
|
68
|
+
* Estimated height in pixels for a single table row when `virtualized` is
|
|
69
|
+
* true. Used to compute how many rows fit in the viewport and the spacer
|
|
70
|
+
* heights above/below the rendered window.
|
|
71
|
+
* @default 40
|
|
72
|
+
*/
|
|
73
|
+
virtualRowEstimate?: number;
|
|
74
|
+
/**
|
|
75
|
+
* Optional row id(s) to highlight visually. Highlighting is purely cosmetic
|
|
76
|
+
* and does not affect selection state.
|
|
77
|
+
*/
|
|
78
|
+
highlightRowId?: string | string[];
|
|
79
|
+
/**
|
|
80
|
+
* When true and `highlightRowId` is set, moving the mouse pointer out of the
|
|
81
|
+
* scroll area will automatically scroll the first highlighted row back into
|
|
82
|
+
* view (centered).
|
|
83
|
+
*/
|
|
84
|
+
scrollToHighlightOnMouseLeave?: boolean;
|
|
85
|
+
selectable?: boolean;
|
|
86
|
+
onRowSelectionChange?: (rows: RowSelectionState) => void;
|
|
87
|
+
/** Render actions into the last fixed-width column. Receives the TanStack Row. */
|
|
88
|
+
rowActions?: (row: Row<TData>) => React.ReactNode;
|
|
89
|
+
/**
|
|
90
|
+
* Enable drag-to-reorder rows. A grip handle column is prepended automatically.
|
|
91
|
+
* Requires each data item to have a unique `id` field (string | number)
|
|
92
|
+
* or supply `getRowId` to derive one.
|
|
93
|
+
*/
|
|
94
|
+
reorderable?: boolean;
|
|
95
|
+
/** Return a unique string id for each row. Defaults to `(row) => row.id`. */
|
|
96
|
+
getRowId?: (row: TData) => string;
|
|
97
|
+
/** Called after the user drops a row into a new position. Receives the reordered data array. */
|
|
98
|
+
onRowReorder?: (data: TData[]) => void;
|
|
99
|
+
/**
|
|
100
|
+
* When `reorderable`, rows that return `true` are not draggable and are
|
|
101
|
+
* omitted from the sortable context (e.g. a pinned footer "add" row).
|
|
102
|
+
* Those rows are also kept **after** all sortable rows whenever column sort
|
|
103
|
+
* is applied (order among locked rows follows original `data` order).
|
|
104
|
+
*/
|
|
105
|
+
isRowReorderLocked?: (row: Row<TData>) => boolean;
|
|
106
|
+
/** Fired when a body row is clicked. */
|
|
107
|
+
onRowClick?: (row: Row<TData>, event: React.MouseEvent) => void;
|
|
108
|
+
/** Fired when an individual body cell is clicked. */
|
|
109
|
+
onCellClick?: (cell: Cell<TData, unknown>, row: Row<TData>, event: React.MouseEvent) => void;
|
|
110
|
+
/** Return child rows to enable tree expansion */
|
|
111
|
+
getSubRows?: (row: TData) => TData[] | undefined;
|
|
112
|
+
/** Initial expanded state — `true` = all expanded, `{}` = all collapsed (uncontrolled) */
|
|
113
|
+
defaultExpanded?: ExpandedState | boolean;
|
|
114
|
+
/** Controlled expanded state — omit to let DataTable manage internally */
|
|
115
|
+
expanded?: ExpandedState;
|
|
116
|
+
onExpandedChange?: (expanded: ExpandedState) => void;
|
|
117
|
+
/**
|
|
118
|
+
* Rounded frame + `border-table-c-border` around the scroll area and pagination
|
|
119
|
+
* (same token as primitive `Table` `bordered`). Set `false` when embedding in
|
|
120
|
+
* another bordered surface.
|
|
121
|
+
*/
|
|
122
|
+
bordered?: boolean;
|
|
123
|
+
/**
|
|
124
|
+
* `"panel"` sets `data-surface="panel"` for table token overrides (modal /
|
|
125
|
+
* drawer). `"default"` leaves surface to ancestors.
|
|
126
|
+
*/
|
|
127
|
+
surface?: "default" | "panel";
|
|
128
|
+
/** Alternate row background colours (RowA / RowB) */
|
|
129
|
+
striped?: boolean;
|
|
130
|
+
/** Add vertical column dividers */
|
|
131
|
+
divided?: boolean;
|
|
132
|
+
/** Return a className string to apply to a specific body row. */
|
|
133
|
+
rowClassName?: (row: Row<TData>, index: number) => string | undefined;
|
|
134
|
+
/** Return a className string to apply to a specific body cell. */
|
|
135
|
+
cellClassName?: (cell: Cell<TData, unknown>, row: Row<TData>) => string | undefined;
|
|
136
|
+
/** Return a className string to apply to a specific column header cell (`<th>`). */
|
|
137
|
+
headerCellClassName?: (header: Header<TData, unknown>) => string | undefined;
|
|
138
|
+
/** Additional className for the header section (`<thead>`). */
|
|
139
|
+
headerClassName?: string;
|
|
140
|
+
/** Additional className for the header row (`<tr>` inside `<thead>`). */
|
|
141
|
+
headerRowClassName?: string;
|
|
142
|
+
/**
|
|
143
|
+
* Controls when the sort indicator button is visible on sortable columns.
|
|
144
|
+
* - `"hover"` – shown only when hovering the column header (default)
|
|
145
|
+
* - `"always"` – always visible
|
|
146
|
+
*/
|
|
147
|
+
sortIndicatorVisibility?: "always" | "hover";
|
|
148
|
+
/**
|
|
149
|
+
* CSS `table-layout` mode.
|
|
150
|
+
* - `"auto"` – browser sizes columns based on content (default)
|
|
151
|
+
* - `"fixed"` – non-`exactWidth` columns use their `size` as pixel widths;
|
|
152
|
+
* the **last** visible non-exact column absorbs leftover width so the table
|
|
153
|
+
* stays full-width. `meta.exactWidth` (px) columns stay locked.
|
|
154
|
+
* - `"equal"` – all visible columns without `meta.exactWidth` share the
|
|
155
|
+
* remaining table width equally via
|
|
156
|
+
* `calc((100% - <sum of exactWidth>px) / <flex column count>)`.
|
|
157
|
+
* Columns with `meta.exactWidth` are still locked to their exact value.
|
|
158
|
+
* With `resizable`, drag-to-resize is disabled for `equal` so widths stay
|
|
159
|
+
* equal; `fixed`/`auto` keep column resize handles.
|
|
160
|
+
*/
|
|
161
|
+
tableLayout?: "auto" | "fixed" | "equal";
|
|
162
|
+
/**
|
|
163
|
+
* Show a column menu (⋮) on each hideable column header: first a dropdown
|
|
164
|
+
* (“Hide … column”, “Manage columns”), then the full manage panel from the
|
|
165
|
+
* second action. Pass `true` or `ColumnManagementOptions` to tune features.
|
|
166
|
+
*/
|
|
167
|
+
columnManagement?: boolean | ColumnManagementOptions;
|
|
168
|
+
/** Allow user to drag-resize column widths */
|
|
169
|
+
resizable?: boolean;
|
|
170
|
+
/**
|
|
171
|
+
* Global minimum column width in pixels when `resizable` is true.
|
|
172
|
+
* Individual columns can override with `minSize` in their column def.
|
|
173
|
+
* @default 60
|
|
174
|
+
*/
|
|
175
|
+
columnMinSize?: number;
|
|
176
|
+
/**
|
|
177
|
+
* Global maximum column width in pixels when `resizable` is true.
|
|
178
|
+
* Individual columns can override with `maxSize` in their column def.
|
|
179
|
+
* @default Number.MAX_SAFE_INTEGER (no limit)
|
|
180
|
+
*/
|
|
181
|
+
columnMaxSize?: number;
|
|
182
|
+
className?: string;
|
|
183
|
+
/**
|
|
184
|
+
* Sets `data-testid` on the root container and derives sub-ids for the
|
|
185
|
+
* table (`{testId}-table`), header (`{testId}-thead`), body (`{testId}-tbody`),
|
|
186
|
+
* rows (`{testId}-row-{rowId}`), and cells (`{testId}-cell-{rowId}-{colId}`).
|
|
187
|
+
*/
|
|
188
|
+
testId?: string;
|
|
189
|
+
}
|
|
190
|
+
export interface ColumnManagementOptions {
|
|
191
|
+
/** Show drag-handle to reorder columns. @default true */
|
|
192
|
+
reorder?: boolean;
|
|
193
|
+
/** Show per-column visibility Switch. @default true */
|
|
194
|
+
visibility?: boolean;
|
|
195
|
+
/** Show "Hide all" button. @default true */
|
|
196
|
+
hideAll?: boolean;
|
|
197
|
+
/** Show "Show all" button. @default true */
|
|
198
|
+
showAll?: boolean;
|
|
8
199
|
}
|
|
9
|
-
export declare function DataTable<TData, TValue>({ data, columns, manualSorting, onSorting, fetchMoreData, }: DataTableProps<TData, TValue>): import("react/jsx-runtime").JSX.Element;
|
|
200
|
+
export declare function DataTable<TData, TValue>({ data, columns, manualSorting, onSorting, paginationMode, totalCount, pageIndex: controlledPageIndex, pageSize: controlledPageSize, onPaginationChange, pageSizeOptions, fetchMoreData, fetchMoreOffset, fetchingMore, fetchingMoreLabel, loading, loadingLabel, highlightRowId, scrollToHighlightOnMouseLeave, selectable, onRowSelectionChange, rowActions, reorderable, getRowId: getRowIdProp, onRowReorder, isRowReorderLocked, onRowClick, onCellClick, getSubRows, defaultExpanded, expanded: controlledExpanded, onExpandedChange, bordered, surface, striped, divided, rowClassName, cellClassName, headerCellClassName, headerClassName, headerRowClassName, sortIndicatorVisibility, tableLayout, columnManagement: columnManagementProp, resizable, columnMinSize, columnMaxSize, virtualized, virtualRowEstimate, className, enableEditing, editDisplayMode, editTrigger, onCellCommit, alwaysEditing, enableCellTabTraversal, editableColumnIds: editableColumnIdsProp, testId, }: DataTableProps<TData, TValue>): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import type { ColumnDef, Row, RowData } from "@tanstack/react-table";
|
|
3
|
+
import type { EditableColumnDef, EditDisplayMode, EditTrigger, EditingState } from "./DataTable.editing.types";
|
|
4
|
+
export declare const EditContext: React.Context<EditingState | null>;
|
|
5
|
+
export declare function useDataTableEditing<TData extends RowData>(opts: {
|
|
6
|
+
enabled: boolean;
|
|
7
|
+
editDisplayMode: EditDisplayMode;
|
|
8
|
+
editTrigger: EditTrigger;
|
|
9
|
+
editableColumnIds: string[];
|
|
10
|
+
}): EditingState;
|
|
11
|
+
type ResolveOpts<TData extends RowData> = {
|
|
12
|
+
editing: EditingState;
|
|
13
|
+
onCellCommit?: (rowId: string, columnId: string, patch: Partial<TData>) => void;
|
|
14
|
+
alwaysEditing?: (row: Row<TData>) => boolean;
|
|
15
|
+
enableCellTabTraversal: boolean;
|
|
16
|
+
editableColumnIds: string[];
|
|
17
|
+
};
|
|
18
|
+
export declare function resolveEditableColumns<TData extends RowData>(columns: EditableColumnDef<TData>[], opts: ResolveOpts<TData>): ColumnDef<TData, unknown>[];
|
|
19
|
+
export declare function detectEditableColumnIds<TData extends RowData>(columns: EditableColumnDef<TData>[]): string[];
|
|
20
|
+
export {};
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import type { ColumnDef, Row, RowData } from "@tanstack/react-table";
|
|
2
|
+
import type { Options } from "@/components/Dropdown/Dropdown";
|
|
3
|
+
export type EditDisplayMode = "cell" | "row";
|
|
4
|
+
export type EditTrigger = "click" | "doubleClick";
|
|
5
|
+
export type EditableColumnDef<TData extends RowData = RowData, TValue = unknown> = ColumnDef<TData, TValue> & {
|
|
6
|
+
/**
|
|
7
|
+
* Enable inline editing for this column.
|
|
8
|
+
* - `true` — always editable
|
|
9
|
+
* - `(row) => boolean` — conditionally editable (e.g. dependent fields)
|
|
10
|
+
*/
|
|
11
|
+
enableEditing?: boolean | ((row: Row<TData>) => boolean);
|
|
12
|
+
/**
|
|
13
|
+
* Which built-in editor to render.
|
|
14
|
+
* - `"text"` — `TextInput` (default when `enableEditing` is truthy)
|
|
15
|
+
* - `"select"` — `Dropdown`
|
|
16
|
+
* - `"number"` — `NumberInput`
|
|
17
|
+
* - `"checkbox"` — `Checkbox`
|
|
18
|
+
* - `"custom"` — user-provided renderer via `editCustomCell`
|
|
19
|
+
*/
|
|
20
|
+
editVariant?: "text" | "select" | "number" | "checkbox" | "custom";
|
|
21
|
+
/** Props forwarded to the `TextInput` editor (variant `"text"`). */
|
|
22
|
+
editTextProps?: {
|
|
23
|
+
placeholder?: string;
|
|
24
|
+
};
|
|
25
|
+
/** Props forwarded to the `Dropdown` editor (variant `"select"`). */
|
|
26
|
+
editSelectProps?: {
|
|
27
|
+
options: Options[] | ((row: Row<TData>) => Options[]);
|
|
28
|
+
placeholder?: string;
|
|
29
|
+
};
|
|
30
|
+
/** Props forwarded to the `NumberInput` editor (variant `"number"`). */
|
|
31
|
+
editNumberProps?: {
|
|
32
|
+
placeholder?: string;
|
|
33
|
+
min?: number;
|
|
34
|
+
max?: number;
|
|
35
|
+
step?: number;
|
|
36
|
+
precision?: number;
|
|
37
|
+
allowDecimal?: boolean;
|
|
38
|
+
allowNegative?: boolean;
|
|
39
|
+
};
|
|
40
|
+
/** Props forwarded to the `Checkbox` editor (variant `"checkbox"`). */
|
|
41
|
+
editCheckboxProps?: {
|
|
42
|
+
label?: string;
|
|
43
|
+
};
|
|
44
|
+
/**
|
|
45
|
+
* Render function for a fully custom edit cell (variant `"custom"`).
|
|
46
|
+
*
|
|
47
|
+
* Receives the current row and a `commit` callback. Call `commit(rawValue)`
|
|
48
|
+
* to persist the change — the engine will run `onCommit` / `buildPatch`
|
|
49
|
+
* as usual.
|
|
50
|
+
*
|
|
51
|
+
* ```tsx
|
|
52
|
+
* editCustomCell: (row, { commit, errorMessage }) => (
|
|
53
|
+
* <MyColorPicker
|
|
54
|
+
* value={row.original.color}
|
|
55
|
+
* onChange={(c) => commit(c)}
|
|
56
|
+
* error={errorMessage}
|
|
57
|
+
* />
|
|
58
|
+
* )
|
|
59
|
+
* ```
|
|
60
|
+
*/
|
|
61
|
+
editCustomCell?: (row: Row<TData>, ctx: {
|
|
62
|
+
/** Persist the edited value — runs `onCommit` / `buildPatch` as usual. */
|
|
63
|
+
commit: (rawValue: string) => void;
|
|
64
|
+
/** Exit edit mode without committing (same as pressing Escape). */
|
|
65
|
+
blur: () => void;
|
|
66
|
+
columnId: string;
|
|
67
|
+
autoFocus: boolean;
|
|
68
|
+
errorMessage?: string;
|
|
69
|
+
onKeyDown?: (e: React.KeyboardEvent<HTMLElement>) => void;
|
|
70
|
+
}) => React.ReactNode;
|
|
71
|
+
/**
|
|
72
|
+
* Return an error message for the current cell value to display inline
|
|
73
|
+
* validation feedback. Return `undefined` or empty string when valid.
|
|
74
|
+
*/
|
|
75
|
+
editError?: (row: Row<TData>) => string | undefined;
|
|
76
|
+
/**
|
|
77
|
+
* Build a custom `Partial<TData>` patch from the raw committed value.
|
|
78
|
+
* When omitted the engine defaults to `{ [columnId]: rawValue }`.
|
|
79
|
+
*
|
|
80
|
+
* Use this for dependent-field resets, value transforms, etc.
|
|
81
|
+
*
|
|
82
|
+
* ```ts
|
|
83
|
+
* onCommit: (_row, value) => ({
|
|
84
|
+
* dataCategory: value,
|
|
85
|
+
* dataType: "", format: "", source: "",
|
|
86
|
+
* })
|
|
87
|
+
* ```
|
|
88
|
+
*/
|
|
89
|
+
onCommit?: (row: Row<TData>, value: string) => Partial<TData>;
|
|
90
|
+
/**
|
|
91
|
+
* When `true`, a cell whose `enableEditing` returns `false` renders a
|
|
92
|
+
* **disabled** TextInput / Dropdown (matching `editVariant`) instead of
|
|
93
|
+
* falling back to `displayCell` or plain text.
|
|
94
|
+
*/
|
|
95
|
+
editShowDisabledField?: boolean;
|
|
96
|
+
/**
|
|
97
|
+
* Custom preview renderer shown when the cell is **not** in edit mode.
|
|
98
|
+
* Defaults to the plain string value of `row.original[key]`.
|
|
99
|
+
*/
|
|
100
|
+
displayCell?: (row: Row<TData>) => React.ReactNode;
|
|
101
|
+
};
|
|
102
|
+
export interface DataTableEditingProps<TData extends RowData = RowData> {
|
|
103
|
+
/** Master switch — set `true` to enable editing features. */
|
|
104
|
+
enableEditing?: boolean;
|
|
105
|
+
/** How editing is activated visually. @default "cell" */
|
|
106
|
+
editDisplayMode?: EditDisplayMode;
|
|
107
|
+
/** Gesture that enters edit mode. @default "click" */
|
|
108
|
+
editTrigger?: EditTrigger;
|
|
109
|
+
/**
|
|
110
|
+
* Called every time a cell commits a new value. The `patch` object is
|
|
111
|
+
* built from the column's `commitResets` / `mapCommitValue` or defaults
|
|
112
|
+
* to `{ [columnId]: rawValue }`.
|
|
113
|
+
*/
|
|
114
|
+
onCellCommit?: (rowId: string, columnId: string, patch: Partial<TData>) => void;
|
|
115
|
+
/**
|
|
116
|
+
* Rows that return `true` render their editable columns in edit mode
|
|
117
|
+
* **permanently** (e.g. an "add" footer row).
|
|
118
|
+
*/
|
|
119
|
+
alwaysEditing?: (row: Row<TData>) => boolean;
|
|
120
|
+
/**
|
|
121
|
+
* When `true` (the default when `enableEditing` is set), Tab / Shift+Tab
|
|
122
|
+
* inside an editing cell moves focus to the next / previous editable
|
|
123
|
+
* column in the same row.
|
|
124
|
+
*/
|
|
125
|
+
enableCellTabTraversal?: boolean;
|
|
126
|
+
/**
|
|
127
|
+
* Explicit ordered list of column ids for Tab traversal. When omitted
|
|
128
|
+
* the engine auto-detects editable columns in definition order.
|
|
129
|
+
*/
|
|
130
|
+
editableColumnIds?: string[];
|
|
131
|
+
}
|
|
132
|
+
export interface EditingState {
|
|
133
|
+
editingRowId: string | null;
|
|
134
|
+
editingCell: {
|
|
135
|
+
rowId: string;
|
|
136
|
+
columnId: string;
|
|
137
|
+
} | null;
|
|
138
|
+
editDisplayMode: EditDisplayMode;
|
|
139
|
+
editTrigger: EditTrigger;
|
|
140
|
+
requestRowEdit: (rowId: string) => void;
|
|
141
|
+
requestCellEdit: (rowId: string, columnId: string) => void;
|
|
142
|
+
clearEdit: () => void;
|
|
143
|
+
onFieldBlur: (rowId: string, columnId: string) => void;
|
|
144
|
+
moveCellEdit: (rowId: string, fromColumnId: string, delta: -1 | 1) => "moved" | "exited";
|
|
145
|
+
}
|
|
@@ -1,5 +1,8 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import type { StoryObj } from "@storybook/react";
|
|
3
|
+
import { ColumnDef, Row } from "@tanstack/react-table";
|
|
1
4
|
import { DataTable } from "./DataTable";
|
|
2
|
-
import {
|
|
5
|
+
import type { EditableColumnDef } from "./DataTable.editing.types";
|
|
3
6
|
declare const meta: {
|
|
4
7
|
title: string;
|
|
5
8
|
component: typeof DataTable;
|
|
@@ -7,16 +10,275 @@ declare const meta: {
|
|
|
7
10
|
parameters: {
|
|
8
11
|
layout: string;
|
|
9
12
|
};
|
|
13
|
+
argTypes: {
|
|
14
|
+
bordered: {
|
|
15
|
+
control: "boolean";
|
|
16
|
+
};
|
|
17
|
+
surface: {
|
|
18
|
+
control: "radio";
|
|
19
|
+
options: string[];
|
|
20
|
+
};
|
|
21
|
+
divided: {
|
|
22
|
+
control: "boolean";
|
|
23
|
+
};
|
|
24
|
+
striped: {
|
|
25
|
+
control: "boolean";
|
|
26
|
+
};
|
|
27
|
+
fetchingMore: {
|
|
28
|
+
control: "boolean";
|
|
29
|
+
};
|
|
30
|
+
loading: {
|
|
31
|
+
control: "boolean";
|
|
32
|
+
};
|
|
33
|
+
};
|
|
10
34
|
decorators: ((Story: import("@storybook/csf").PartialStoryFn<import("@storybook/react").ReactRenderer, {
|
|
11
|
-
columns: ColumnDef<unknown, unknown>[];
|
|
35
|
+
columns: (ColumnDef<unknown, unknown> | EditableColumnDef<unknown, unknown>)[];
|
|
12
36
|
data: unknown[];
|
|
13
37
|
manualSorting?: boolean | undefined;
|
|
14
38
|
onSorting?: ((sorting: import("@tanstack/react-table").SortingState) => void) | undefined;
|
|
39
|
+
paginationMode?: import("./DataTable").DataTablePaginationMode | undefined;
|
|
40
|
+
totalCount?: number | undefined;
|
|
41
|
+
pageIndex?: number | undefined;
|
|
42
|
+
pageSize?: number | undefined;
|
|
43
|
+
onPaginationChange?: ((state: import("@tanstack/react-table").PaginationState) => void) | undefined;
|
|
44
|
+
pageSizeOptions?: number[] | undefined;
|
|
15
45
|
fetchMoreData?: (() => void) | undefined;
|
|
46
|
+
fetchMoreOffset?: number | undefined;
|
|
47
|
+
fetchingMore?: boolean | undefined;
|
|
48
|
+
fetchingMoreLabel?: string | undefined;
|
|
49
|
+
loading?: boolean | undefined;
|
|
50
|
+
loadingLabel?: string | undefined;
|
|
51
|
+
virtualized?: boolean | undefined;
|
|
52
|
+
virtualRowEstimate?: number | undefined;
|
|
53
|
+
highlightRowId?: string | string[] | undefined;
|
|
54
|
+
scrollToHighlightOnMouseLeave?: boolean | undefined;
|
|
55
|
+
selectable?: boolean | undefined;
|
|
56
|
+
onRowSelectionChange?: ((rows: import("@tanstack/react-table").RowSelectionState) => void) | undefined;
|
|
57
|
+
rowActions?: ((row: Row<unknown>) => React.ReactNode) | undefined;
|
|
58
|
+
reorderable?: boolean | undefined;
|
|
59
|
+
getRowId?: ((row: unknown) => string) | undefined;
|
|
60
|
+
onRowReorder?: ((data: unknown[]) => void) | undefined;
|
|
61
|
+
isRowReorderLocked?: ((row: Row<unknown>) => boolean) | undefined;
|
|
62
|
+
onRowClick?: ((row: Row<unknown>, event: React.MouseEvent) => void) | undefined;
|
|
63
|
+
onCellClick?: ((cell: import("@tanstack/react-table").Cell<unknown, unknown>, row: Row<unknown>, event: React.MouseEvent) => void) | undefined;
|
|
64
|
+
getSubRows?: ((row: unknown) => unknown[] | undefined) | undefined;
|
|
65
|
+
defaultExpanded?: (import("@tanstack/react-table").ExpandedState | boolean) | undefined;
|
|
66
|
+
expanded?: import("@tanstack/react-table").ExpandedState | undefined;
|
|
67
|
+
onExpandedChange?: ((expanded: import("@tanstack/react-table").ExpandedState) => void) | undefined;
|
|
68
|
+
bordered?: boolean | undefined;
|
|
69
|
+
surface?: "default" | "panel" | undefined;
|
|
70
|
+
striped?: boolean | undefined;
|
|
71
|
+
divided?: boolean | undefined;
|
|
72
|
+
rowClassName?: ((row: Row<unknown>, index: number) => string | undefined) | undefined;
|
|
73
|
+
cellClassName?: ((cell: import("@tanstack/react-table").Cell<unknown, unknown>, row: Row<unknown>) => string | undefined) | undefined;
|
|
74
|
+
headerCellClassName?: ((header: import("@tanstack/react-table").Header<unknown, unknown>) => string | undefined) | undefined;
|
|
75
|
+
headerClassName?: string | undefined;
|
|
76
|
+
headerRowClassName?: string | undefined;
|
|
77
|
+
sortIndicatorVisibility?: "always" | "hover" | undefined;
|
|
78
|
+
tableLayout?: "auto" | "fixed" | "equal" | undefined;
|
|
79
|
+
columnManagement?: (boolean | import("./DataTable").ColumnManagementOptions) | undefined;
|
|
80
|
+
resizable?: boolean | undefined;
|
|
81
|
+
columnMinSize?: number | undefined;
|
|
82
|
+
columnMaxSize?: number | undefined;
|
|
83
|
+
className?: string | undefined;
|
|
84
|
+
testId?: string | undefined;
|
|
85
|
+
enableEditing?: boolean | undefined;
|
|
86
|
+
editDisplayMode?: import("./DataTable.editing.types").EditDisplayMode | undefined;
|
|
87
|
+
editTrigger?: import("./DataTable.editing.types").EditTrigger | undefined;
|
|
88
|
+
onCellCommit?: ((rowId: string, columnId: string, patch: Partial<unknown>) => void) | undefined;
|
|
89
|
+
alwaysEditing?: ((row: Row<unknown>) => boolean) | undefined;
|
|
90
|
+
enableCellTabTraversal?: boolean | undefined;
|
|
91
|
+
editableColumnIds?: string[] | undefined;
|
|
16
92
|
}>) => import("react/jsx-runtime").JSX.Element)[];
|
|
17
93
|
};
|
|
18
94
|
export default meta;
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
95
|
+
/** Default — striped rows + column dividers, all columns sortable. */
|
|
96
|
+
export declare const Default: StoryObj;
|
|
97
|
+
/** Empty state — displayed when `data` is an empty array. */
|
|
98
|
+
export declare const Empty: StoryObj;
|
|
99
|
+
/** Non-striped with column dividers only. */
|
|
100
|
+
export declare const Divided: StoryObj;
|
|
101
|
+
/**
|
|
102
|
+
* DataTable inside a panel — matches modal spec: horizontal row separators only
|
|
103
|
+
* (no vertical column rules), compact height without a stretched empty body.
|
|
104
|
+
*/
|
|
105
|
+
export declare const OnPanelSurface: StoryObj;
|
|
106
|
+
/** Panel + client pagination — footer sits in the same surface scope as the table body. */
|
|
107
|
+
export declare const OnPanelSurfaceWithPagination: StoryObj;
|
|
108
|
+
/** Client-side pagination — DataTable manages page index & size internally. */
|
|
109
|
+
export declare const WithClientPagination: StoryObj;
|
|
110
|
+
/**
|
|
111
|
+
* Server-side pagination — the caller controls `pageIndex` / `pageSize` and
|
|
112
|
+
* slices data accordingly. `totalCount` drives the page-count calculation.
|
|
113
|
+
*/
|
|
114
|
+
export declare const WithServerPagination: StoryObj;
|
|
115
|
+
/**
|
|
116
|
+
* Infinite mode (default) — no pagination bar; the table body scrolls inside a
|
|
117
|
+
* fixed height. Use when the caller already passes the full (or windowed) dataset.
|
|
118
|
+
*/
|
|
119
|
+
export declare const WithInfiniteScrollStatic: StoryObj;
|
|
120
|
+
/**
|
|
121
|
+
* Infinite scroll + load more — DataTable calls{" "}
|
|
122
|
+
* <code className="typography-mono">fetchMoreData</code> when the scroll position
|
|
123
|
+
* is within 10px of the bottom (see <code className="typography-mono">DataTable</code>{" "}
|
|
124
|
+
* <code className="typography-mono">useEffect</code> on <code className="typography-mono">scrollRef</code>).
|
|
125
|
+
* This story simulates an async page append.
|
|
126
|
+
*/
|
|
127
|
+
export declare const WithInfiniteScrollLoadMore: StoryObj;
|
|
128
|
+
/**
|
|
129
|
+
* Initial load — pass `loading={true}` with `data={[]}` until the first page
|
|
130
|
+
* arrives; then set `loading={false}` and pass rows. Infinite `fetchMoreData`
|
|
131
|
+
* does not run while `loading` is true.
|
|
132
|
+
*/
|
|
133
|
+
export declare const WithInfiniteScrollInitialLoading: StoryObj;
|
|
134
|
+
/**
|
|
135
|
+
* **What this library does *not* do:** `DataTable` has **no built-in row
|
|
136
|
+
* virtualization** — every row in `data` is rendered in the DOM (TanStack
|
|
137
|
+
* `getRowModel`). Very large `data.length` will cost more paint/layout work.
|
|
138
|
+
*
|
|
139
|
+
* **What to use instead:**
|
|
140
|
+
* - `paginationMode="client"` or `"server"` to cap rows per page
|
|
141
|
+
* - `paginationMode="infinite"` + `fetchMoreData` and **slice/window data in the parent**
|
|
142
|
+
* - Or wrap your own virtualizer (e.g. `@tanstack/react-virtual`) if you need
|
|
143
|
+
* millions of rows in one scroll view
|
|
144
|
+
*
|
|
145
|
+
* This story mounts **800** synthetic rows so you can feel scroll/render cost
|
|
146
|
+
* in Storybook (dev builds are slower than production).
|
|
147
|
+
*/
|
|
148
|
+
export declare const PerformanceLargeDataset: StoryObj;
|
|
149
|
+
/**
|
|
150
|
+
* Built-in **`virtualized`** — only a vertical window of rows is mounted; the
|
|
151
|
+
* rest are represented by spacer rows. Tune **`virtualRowEstimate`** (px) to
|
|
152
|
+
* match your row height so scroll position stays aligned.
|
|
153
|
+
*
|
|
154
|
+
* Limitations: fixed-height assumption; incompatible with **`reorderable`** in
|
|
155
|
+
* the virtualized path (use non-virtualized mode for drag-reorder).
|
|
156
|
+
*/
|
|
157
|
+
export declare const WithVirtualizedRows: StoryObj;
|
|
158
|
+
/** Checkbox selection — header checkbox selects all; row checkboxes select individually. */
|
|
159
|
+
export declare const WithSelection: StoryObj;
|
|
160
|
+
/**
|
|
161
|
+
* Row highlight + scroll — use `highlightRowId` to visually mark important rows
|
|
162
|
+
* (uses the same token as selected rows) and automatically scroll them into view.
|
|
163
|
+
*/
|
|
164
|
+
export declare const WithRowHighlightAndScroll: StoryObj;
|
|
165
|
+
/**
|
|
166
|
+
* Censor-style list — large, virtualized table where `highlightRowId` moves
|
|
167
|
+
* automatically over time (e.g. matching the latest event / time).
|
|
168
|
+
*/
|
|
169
|
+
export declare const WithVirtualizedCensorTimeline: StoryObj;
|
|
170
|
+
/** Fixed actions column at the far right — rendered via `rowActions` prop. */
|
|
171
|
+
export declare const WithRowActions: StoryObj;
|
|
172
|
+
/**
|
|
173
|
+
* Uncontrolled tree rows — `defaultExpanded={true}` opens all nodes on mount.
|
|
174
|
+
* The user can collapse/expand rows freely; state is managed internally.
|
|
175
|
+
*/
|
|
176
|
+
export declare const WithExpandableRows: StoryObj;
|
|
177
|
+
/**
|
|
178
|
+
* Controlled expand — the caller owns `expanded` state via `expanded` +
|
|
179
|
+
* `onExpandedChange`. Collapsing a row in the table calls `onExpandedChange`;
|
|
180
|
+
* if the parent doesn't update the state, the row "springs back" open.
|
|
181
|
+
*/
|
|
182
|
+
export declare const WithControlledExpand: StoryObj;
|
|
183
|
+
/**
|
|
184
|
+
* Column management panel — click ⋮ on any column header.
|
|
185
|
+
* - Toggle per-column visibility with the Switch
|
|
186
|
+
* - "Hide all" hides everything except one column
|
|
187
|
+
* - "Show all" restores all columns
|
|
188
|
+
* - Drag the ⠿ grip to reorder columns
|
|
189
|
+
*/
|
|
190
|
+
export declare const WithColumnManagement: StoryObj;
|
|
191
|
+
/**
|
|
192
|
+
* Column management with restricted options:
|
|
193
|
+
* - `reorder: false` — drag handle hidden, column order is fixed
|
|
194
|
+
* - `hideAll: false` — "Hide all" button removed
|
|
195
|
+
*
|
|
196
|
+
* Pass any subset of `ColumnManagementOptions` to tailor the panel.
|
|
197
|
+
*/
|
|
198
|
+
export declare const WithColumnManagementOptions: StoryObj;
|
|
199
|
+
/**
|
|
200
|
+
* Resizable columns — drag the right edge of any column header.
|
|
201
|
+
*
|
|
202
|
+
* Global bounds via `columnMinSize` / `columnMaxSize`.
|
|
203
|
+
* Per-column overrides in the column def (`minSize`, `maxSize`):
|
|
204
|
+
* - "Asset name": `minSize: 120`
|
|
205
|
+
* - "KPI (%)": `maxSize: 160`
|
|
206
|
+
*/
|
|
207
|
+
export declare const WithColumnResize: StoryObj;
|
|
208
|
+
/**
|
|
209
|
+
* All features combined:
|
|
210
|
+
* - Checkbox selection
|
|
211
|
+
* - Sortable columns
|
|
212
|
+
* - Client-side pagination
|
|
213
|
+
* - Column management (visibility + reorder)
|
|
214
|
+
* - Column resize
|
|
215
|
+
* - Row actions
|
|
216
|
+
*/
|
|
217
|
+
export declare const KitchenSink: StoryObj;
|
|
218
|
+
/**
|
|
219
|
+
* Drag-to-reorder rows — a grip handle is prepended automatically.
|
|
220
|
+
* `onRowReorder` fires with the new data array after a drop.
|
|
221
|
+
*/
|
|
222
|
+
export declare const WithRowReorder: StoryObj;
|
|
223
|
+
/** Row click — clicking any row fires `onRowClick`. Rows show `cursor-pointer`. */
|
|
224
|
+
export declare const WithRowClick: StoryObj;
|
|
225
|
+
/**
|
|
226
|
+
* Cell click — `onCellClick` fires for individual cells.
|
|
227
|
+
* Use `event.stopPropagation()` to prevent `onRowClick` from also firing.
|
|
228
|
+
*/
|
|
229
|
+
export declare const WithCellClick: StoryObj;
|
|
230
|
+
/**
|
|
231
|
+
* Sort indicator always visible — `sortIndicatorVisibility="always"` shows the
|
|
232
|
+
* sort icon on every sortable column, not just on hover.
|
|
233
|
+
*/
|
|
234
|
+
export declare const SortIndicatorAlwaysVisible: StoryObj;
|
|
235
|
+
/**
|
|
236
|
+
* Side-by-side comparison of `tableLayout="auto"` vs `"fixed"` vs `"equal"`.
|
|
237
|
+
*
|
|
238
|
+
* - **auto** — browser distributes width based on cell content; `size` is ignored.
|
|
239
|
+
* - **fixed** — `size` is used as px per column; the last non-exact column grows
|
|
240
|
+
* so the table stays full-width (here: Status absorbs the remainder).
|
|
241
|
+
* - **equal** — all columns without `meta.exactWidth` share the remaining width equally.
|
|
242
|
+
* With `resizable`, drag handles are off for this row so equal widths are preserved.
|
|
243
|
+
*/
|
|
244
|
+
export declare const TableLayoutComparison: StoryObj;
|
|
245
|
+
/**
|
|
246
|
+
* `rowClassName` and `cellClassName` let you style individual rows and cells
|
|
247
|
+
* based on their data.
|
|
248
|
+
*
|
|
249
|
+
* - Rows with status **"Completed"** get a green-tinted background.
|
|
250
|
+
* - Rows with status **"To do"** get a subtle white/opacity background.
|
|
251
|
+
* - The **"Status"** column cells are bold.
|
|
252
|
+
* - The **"Type"** cells for ROV are highlighted with an info tint.
|
|
253
|
+
*/
|
|
254
|
+
export declare const WithCustomRowAndCellClassName: StoryObj;
|
|
255
|
+
/**
|
|
256
|
+
* Data management table — Figma **Xspector-New**:
|
|
257
|
+
* [full frame](https://www.figma.com/design/99rq6FbfPx6hgPS0VCvHJh/Xspector-New?node-id=11965-17125),
|
|
258
|
+
* [row / field spec](https://www.figma.com/design/99rq6FbfPx6hgPS0VCvHJh/Xspector-New?node-id=11965-17883).
|
|
259
|
+
*
|
|
260
|
+
* Uses the **DataTable editing API** (`enableEditing` + `editDisplayMode` +
|
|
261
|
+
* `editVariant` per column). Edit scope and trigger are toggleable via the
|
|
262
|
+
* toolbar at the top of the story.
|
|
263
|
+
*/
|
|
264
|
+
export declare const DataManagement: StoryObj;
|
|
265
|
+
/** Data Management — empty state when no columns have been configured yet. */
|
|
266
|
+
export declare const DataManagementEmpty: StoryObj;
|
|
267
|
+
/**
|
|
268
|
+
* **Editing Field Showcase** — demonstrates every editing feature:
|
|
269
|
+
*
|
|
270
|
+
* | Feature | Where |
|
|
271
|
+
* |---|---|
|
|
272
|
+
* | `editVariant: "text"` | Product name, Notes |
|
|
273
|
+
* | `editVariant: "select"` | Category |
|
|
274
|
+
* | `editVariant: "number"` | Quantity, Price |
|
|
275
|
+
* | `editVariant: "checkbox"` | Active |
|
|
276
|
+
* | `editVariant: "custom"` + `editCustomCell` | Priority (inline button picker → Badge display) |
|
|
277
|
+
* | `editError` | Name (required + min length), Category (required), Price (> 0) |
|
|
278
|
+
* | `displayCell` | Price (formatted), Active (badge), Notes (italic placeholder) |
|
|
279
|
+
* | `testId` | Root container has `data-testid="edit-showcase"` |
|
|
280
|
+
* | Row / Cell mode toggle | Toolbar radio buttons |
|
|
281
|
+
* | Click / Double-click trigger | Toolbar radio buttons |
|
|
282
|
+
* | Tab traversal | Tab/Shift+Tab moves between editable fields |
|
|
283
|
+
*/
|
|
284
|
+
export declare const EditingFieldShowcase: StoryObj;
|