@elabs-ai/components-data 4.0.0 → 4.2.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/README.md +7 -8
- package/dist/index.d.ts +199 -5
- package/dist/index.js +764 -166
- package/dist/index.js.map +1 -1
- package/package.json +10 -7
- package/src/__contract__/filter-chip.contract.test.tsx +49 -0
- package/src/column-picker/column-picker.tsx +6 -3
- package/src/data-table/data-table.stories.tsx +496 -2
- package/src/data-table/data-table.test.tsx +1431 -17
- package/src/data-table/data-table.tsx +1284 -39
- package/src/data-table/index.ts +10 -0
- package/src/facet-filter/facet-filter.stories.tsx +4 -1
- package/src/facet-filter/facet-filter.test.tsx +3 -3
- package/src/facet-filter/facet-filter.tsx +5 -1
- package/src/filter-bar/filter-chip.stories.tsx +144 -0
- package/src/filter-bar/filter-chip.test.tsx +137 -0
- package/src/filter-bar/filter-chip.tsx +92 -0
- package/src/filter-bar/index.ts +1 -0
- package/src/index.ts +10 -1
- package/src/search-input/search-input.stories.tsx +3 -0
- package/src/search-input/search-input.test.tsx +35 -0
- package/src/search-input/search-input.tsx +45 -20
- package/src/templates-data-app.stories.tsx +6 -4
- package/src/to-csv.test.ts +13 -0
- package/src/to-csv.ts +7 -30
package/README.md
CHANGED
|
@@ -6,15 +6,14 @@
|
|
|
6
6
|
> TanStack DataTable, FilterBar, SearchInput, FacetFilter, ColumnPicker.
|
|
7
7
|
|
|
8
8
|
Part of **brand-ui**, a source-owned, token-driven React component system.
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
Published to the **public npm registry** under the `@elabs-ai` scope — it
|
|
10
|
+
installs like any other npm dependency, with no registry configuration and
|
|
11
|
+
no token required. See `docs/CONSUMING.md`.
|
|
11
12
|
|
|
12
13
|
## Install
|
|
13
14
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
```json
|
|
17
|
-
"@elabs-ai/components-data": "workspace:*"
|
|
15
|
+
```bash
|
|
16
|
+
pnpm add @elabs-ai/components-tokens @elabs-ai/components-data
|
|
18
17
|
```
|
|
19
18
|
|
|
20
19
|
## Set up styling (do not skip)
|
|
@@ -39,7 +38,7 @@ import { ThemeProvider } from "@elabs-ai/components-tokens";
|
|
|
39
38
|
|
|
40
39
|
## What's in it
|
|
41
40
|
|
|
42
|
-
|
|
41
|
+
6 exported components — including `ColumnPicker`, `DataTable`, `FacetFilter`, `FilterBar`, `FilterChip`.
|
|
43
42
|
|
|
44
43
|
Don't guess the API — ask the CLI:
|
|
45
44
|
|
|
@@ -69,6 +68,6 @@ prompt for migrating an existing project: `docs/CONSUMING.md`.
|
|
|
69
68
|
|
|
70
69
|
## License
|
|
71
70
|
|
|
72
|
-
|
|
71
|
+
MIT
|
|
73
72
|
|
|
74
73
|
<!-- brand-ui:gen:readme:end -->
|
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,30 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
2
|
import { ReactNode, InputHTMLAttributes, ButtonHTMLAttributes, Ref, ReactElement } from 'react';
|
|
3
|
-
import { ColumnDef, Table, SortingState, OnChangeFn, VisibilityState, ColumnFiltersState, PaginationState, ColumnPinningState, Row } from '@tanstack/react-table';
|
|
4
|
-
export { CellContext, ColumnDef, ColumnPinningState, Row, Table } from '@tanstack/react-table';
|
|
3
|
+
import { RowData, ColumnDef, Table, SortingState, OnChangeFn, VisibilityState, ColumnFiltersState, PaginationState, ColumnPinningState, ColumnSizingState, RowSelectionState, Row } from '@tanstack/react-table';
|
|
4
|
+
export { CellContext, ColumnDef, ColumnMeta, ColumnPinningState, ColumnSizingState, Row, RowSelectionState, Table } from '@tanstack/react-table';
|
|
5
|
+
import { FilterChipProps as FilterChipProps$1 } from '@elabs-ai/components-ui';
|
|
5
6
|
|
|
7
|
+
/**
|
|
8
|
+
* `DataTable`'s `columnDef.meta` contract, read by the header/body/skeleton
|
|
9
|
+
* cell renderers. Set `numeric: true` on a column to get `tabular-nums` +
|
|
10
|
+
* end-alignment on both the `<th>` and every `<td>` (including the loading
|
|
11
|
+
* skeleton) for free.
|
|
12
|
+
*/
|
|
13
|
+
interface DataTableColumnMeta {
|
|
14
|
+
/** Numeric column: tabular figures + end alignment on header and cells. */
|
|
15
|
+
numeric?: boolean;
|
|
16
|
+
/**
|
|
17
|
+
* Explicit alignment override for when `numeric` isn't the right cue (or
|
|
18
|
+
* to align a non-numeric column). Independent of `numeric` — `numeric`
|
|
19
|
+
* alone still drives `tabular-nums` even when `align` overrides the
|
|
20
|
+
* alignment away from `"end"`.
|
|
21
|
+
*/
|
|
22
|
+
align?: "start" | "center" | "end";
|
|
23
|
+
}
|
|
24
|
+
declare module "@tanstack/react-table" {
|
|
25
|
+
interface ColumnMeta<TData extends RowData, TValue> extends DataTableColumnMeta {
|
|
26
|
+
}
|
|
27
|
+
}
|
|
6
28
|
/** Snapshot of table slice state — used for saved-view serialise/rehydrate. */
|
|
7
29
|
interface DataTableViewState {
|
|
8
30
|
sorting: SortingState;
|
|
@@ -16,6 +38,17 @@ interface DataTableViewState {
|
|
|
16
38
|
* that already constructs a `DataTableViewState` literal.
|
|
17
39
|
*/
|
|
18
40
|
columnPinning?: ColumnPinningState;
|
|
41
|
+
/**
|
|
42
|
+
* Which rows are checked (#11), keyed by row id — see `getRowId`. OPTIONAL
|
|
43
|
+
* like `columnPinning`, for the same reason: the other members predate it.
|
|
44
|
+
*/
|
|
45
|
+
rowSelection?: RowSelectionState;
|
|
46
|
+
/**
|
|
47
|
+
* Per-column widths after resizing (#12), keyed by column id. OPTIONAL like
|
|
48
|
+
* `columnPinning`/`rowSelection`, for the same reason: the other members
|
|
49
|
+
* predate it.
|
|
50
|
+
*/
|
|
51
|
+
columnSizing?: ColumnSizingState;
|
|
19
52
|
}
|
|
20
53
|
/**
|
|
21
54
|
* Argument object fired by `onServerChange` whenever a manual slice changes.
|
|
@@ -93,6 +126,81 @@ interface DataTableProps<TData, TValue> extends Omit<React.HTMLAttributes<HTMLDi
|
|
|
93
126
|
*/
|
|
94
127
|
columnPinning?: ColumnPinningState;
|
|
95
128
|
onColumnPinningChange?: OnChangeFn<ColumnPinningState>;
|
|
129
|
+
/**
|
|
130
|
+
* Opt in to column resizing (#12): a drag handle renders on every
|
|
131
|
+
* resizable column's trailing edge — pointer-draggable (TanStack's own
|
|
132
|
+
* `header.getResizeHandler()`) and keyboard-operable (ArrowLeft/ArrowRight
|
|
133
|
+
* on the focused handle, per the WAI-ARIA separator-as-slider practice).
|
|
134
|
+
* Default `false` so a table that doesn't opt in renders byte-identical
|
|
135
|
+
* markup to before this feature existed — no handle, no per-cell width
|
|
136
|
+
* styling.
|
|
137
|
+
*/
|
|
138
|
+
enableColumnResizing?: boolean;
|
|
139
|
+
/**
|
|
140
|
+
* When `columnSizing` updates: `"onChange"` (default here — TanStack's own
|
|
141
|
+
* default is `"onEnd"`) live-updates while dragging; `"onEnd"` updates once
|
|
142
|
+
* on release. Only meaningful when `enableColumnResizing` is set.
|
|
143
|
+
*/
|
|
144
|
+
columnResizeMode?: "onChange" | "onEnd";
|
|
145
|
+
/**
|
|
146
|
+
* Controlled column-widths state (#12), keyed by column id — the SAME
|
|
147
|
+
* controlled/uncontrolled shape as `columnPinning`/`rowSelection`.
|
|
148
|
+
* Uncontrolled sizing can be seeded once via `initialView.columnSizing`.
|
|
149
|
+
*
|
|
150
|
+
* A pinned column's sticky offset (`getStart("left")`/`getAfter("right")`)
|
|
151
|
+
* already sums `column.getSize()`, which folds in a `columnSizing`
|
|
152
|
+
* override automatically — so pinning and resizing compose with no extra
|
|
153
|
+
* wiring once this state reaches the table.
|
|
154
|
+
*
|
|
155
|
+
* Sizing is a LAYOUT concern, like `columnPinning`/`rowSelection` — it is
|
|
156
|
+
* client-only and never joins `DataTableServerArgs` / `onServerChange`.
|
|
157
|
+
*/
|
|
158
|
+
columnSizing?: ColumnSizingState;
|
|
159
|
+
onColumnSizingChange?: OnChangeFn<ColumnSizingState>;
|
|
160
|
+
/**
|
|
161
|
+
* Controlled row-selection state (#11) — which rows are checked, keyed by
|
|
162
|
+
* row id (see `getRowId`). When provided the component is
|
|
163
|
+
* selection-controlled; otherwise it manages the slice internally and can
|
|
164
|
+
* be seeded once via `initialView.rowSelection`. Pair it with a selection
|
|
165
|
+
* column built by `createSelectionColumn` (or drive it yourself off the
|
|
166
|
+
* `table` instance handed to `toolbar`).
|
|
167
|
+
*
|
|
168
|
+
* Selection is a LAYOUT/UI concern, not a query concern — like
|
|
169
|
+
* `columnPinning`, it is client-only and never joins `DataTableServerArgs` /
|
|
170
|
+
* `onServerChange`.
|
|
171
|
+
*/
|
|
172
|
+
rowSelection?: RowSelectionState;
|
|
173
|
+
onRowSelectionChange?: OnChangeFn<RowSelectionState>;
|
|
174
|
+
/**
|
|
175
|
+
* Which rows can be selected: `true`/`false` for all rows, or a predicate
|
|
176
|
+
* evaluated per row. Passed straight through to `useReactTable`. Default
|
|
177
|
+
* (TanStack's own): `true`.
|
|
178
|
+
*/
|
|
179
|
+
enableRowSelection?: boolean | ((row: Row<TData>) => boolean);
|
|
180
|
+
/**
|
|
181
|
+
* Allow more than one row to be selected at once. Default (TanStack's own):
|
|
182
|
+
* `true`. Set `false` for single-select (radio-style) behaviour.
|
|
183
|
+
*/
|
|
184
|
+
enableMultiRowSelection?: boolean;
|
|
185
|
+
/**
|
|
186
|
+
* Stable row id, independent of row INDEX. TanStack's default id is set
|
|
187
|
+
* ONCE per row object when the core row model is built, then reused by
|
|
188
|
+
* reference through sorting/filtering — so a client-side sort or filter
|
|
189
|
+
* does NOT disturb selection identity even without this prop. The real
|
|
190
|
+
* hazard is a `data` array replacement: when the app passes NEW object
|
|
191
|
+
* references (a re-fetch, an optimistic update), TanStack rebuilds the
|
|
192
|
+
* core row model from scratch and reassigns default (index-based) ids, so a
|
|
193
|
+
* row that kept its position but got a new object still keeps its
|
|
194
|
+
* selection — but one that MOVED position silently inherits whatever
|
|
195
|
+
* selection belonged to the id now sitting at its old index. This is
|
|
196
|
+
* unavoidable under `manualPagination`: each page IS a fresh `data` array,
|
|
197
|
+
* so the default index-based id restarts at `0` on every page and a
|
|
198
|
+
* selection made on one page can collide with a different record on the
|
|
199
|
+
* next. Supply `getRowId` whenever `data` can be replaced with new object
|
|
200
|
+
* references (including every server-paginated table) so identity survives
|
|
201
|
+
* the replacement instead of falling back to index.
|
|
202
|
+
*/
|
|
203
|
+
getRowId?: (row: TData, index: number) => string;
|
|
96
204
|
/**
|
|
97
205
|
* One-shot rehydrate for uncontrolled slices only (ignored for any slice
|
|
98
206
|
* whose corresponding controlled prop is set). Maps to `useReactTable`'s
|
|
@@ -158,6 +266,51 @@ interface DataTableProps<TData, TValue> extends Omit<React.HTMLAttributes<HTMLDi
|
|
|
158
266
|
* (a `border-border-strong` divider between rows, no stripes).
|
|
159
267
|
*/
|
|
160
268
|
zebra?: boolean;
|
|
269
|
+
/**
|
|
270
|
+
* Draw a quiet `--rule` hairline between columns (header and body). Off by
|
|
271
|
+
* default. Pinned cells keep their own seam and never take a divider.
|
|
272
|
+
*/
|
|
273
|
+
columnDividers?: boolean;
|
|
274
|
+
/**
|
|
275
|
+
* Opt-in row drag-reorder. Off by default — an existing table renders
|
|
276
|
+
* byte-identical markup with no extra DOM per row until this is set.
|
|
277
|
+
* Fully controlled like every other slice: the component never mutates
|
|
278
|
+
* `data` itself, it only reports the move via `onRowReorder`; the caller
|
|
279
|
+
* re-orders `data` in response.
|
|
280
|
+
*
|
|
281
|
+
* Keyboard-operable out of the box (`@dnd-kit`'s default keyboard sensor):
|
|
282
|
+
* Space/Enter picks a row up, Arrow Up/Down moves it, Space/Enter drops it,
|
|
283
|
+
* Escape cancels. Every position change is announced through a live region
|
|
284
|
+
* (WCAG 4.1.3).
|
|
285
|
+
*
|
|
286
|
+
* Mutually exclusive with `enableRowVirtualization` — a windowed table
|
|
287
|
+
* can't keep dnd-kit's sortable list and a virtualizer in sync, so reorder
|
|
288
|
+
* is silently disabled (a dev warning fires) when both are set. Combining
|
|
289
|
+
* it with active `sorting` also fires a dev warning (both still work, but
|
|
290
|
+
* a sort re-orders the very rows a drag just moved, which reads as broken).
|
|
291
|
+
*/
|
|
292
|
+
enableRowReorder?: boolean;
|
|
293
|
+
/**
|
|
294
|
+
* Fires when a row is dropped in a new position. `from`/`to` are indices
|
|
295
|
+
* into the **`data` array you passed in** — never into the sorted, filtered
|
|
296
|
+
* or paginated view the table renders — so they are safe to use directly
|
|
297
|
+
* with `arrayMove`/`slice`+`splice`/immer against your own `data`, unchanged
|
|
298
|
+
* by an active sort or by client-side pagination (the dragged row's true
|
|
299
|
+
* index in the full array, not its index on the current page). Under
|
|
300
|
+
* `manualPagination`, `data` IS the current page, so `from`/`to` are
|
|
301
|
+
* page-relative — reorder that page's own array with them. `row` is the
|
|
302
|
+
* moved record (`data[from]`).
|
|
303
|
+
*/
|
|
304
|
+
onRowReorder?: (from: number, to: number, row: TData) => void;
|
|
305
|
+
/**
|
|
306
|
+
* Where the drag activator lives. `"cell"` (default) renders a dedicated
|
|
307
|
+
* grip-handle column so the rest of the row keeps its ordinary click/
|
|
308
|
+
* keyboard behavior untouched. `"row"` makes the whole row itself the drag
|
|
309
|
+
* activator (no extra column) — reach for this only when the row has no
|
|
310
|
+
* other primary interaction (e.g. no `onRowClick`), since a whole-row
|
|
311
|
+
* activator and a row click target the same surface.
|
|
312
|
+
*/
|
|
313
|
+
rowReorderHandle?: "cell" | "row";
|
|
161
314
|
/**
|
|
162
315
|
* Fires when a row is activated (#337). Setting it adds ONE activation
|
|
163
316
|
* target per row: a visually-hidden `<button>` rendered inside the row's
|
|
@@ -198,6 +351,19 @@ interface DataTableProps<TData, TValue> extends Omit<React.HTMLAttributes<HTMLDi
|
|
|
198
351
|
emptyMessage?: ReactNode;
|
|
199
352
|
className?: string;
|
|
200
353
|
}
|
|
354
|
+
/**
|
|
355
|
+
* Ready-made checkbox selection column (#11): header select-all (with a real
|
|
356
|
+
* `indeterminate` state for a partial page selection) + a per-row checkbox,
|
|
357
|
+
* both built on `@elabs-ai/components-ui`'s `Checkbox` — never hand-roll one.
|
|
358
|
+
*
|
|
359
|
+
* Add it to `columns` and pair it with `rowSelection` / `onRowSelectionChange`
|
|
360
|
+
* (or leave both uncontrolled and read `table.getSelectedRowModel()` from a
|
|
361
|
+
* `toolbar` render-prop to build a bulk-action bar).
|
|
362
|
+
*
|
|
363
|
+
* Declares an explicit `size` (40px) so it plays nicely if a caller pins it —
|
|
364
|
+
* every pinned column must declare one (#333) — without the dev warning.
|
|
365
|
+
*/
|
|
366
|
+
declare function createSelectionColumn<TData>(): ColumnDef<TData>;
|
|
201
367
|
declare const DataTableWithRef: <TData, TValue>(props: DataTableProps<TData, TValue> & {
|
|
202
368
|
ref?: React.Ref<HTMLDivElement>;
|
|
203
369
|
}) => React.ReactElement | null;
|
|
@@ -205,7 +371,7 @@ declare const DataTableWithRef: <TData, TValue>(props: DataTableProps<TData, TVa
|
|
|
205
371
|
interface SearchInputProps extends Omit<InputHTMLAttributes<HTMLInputElement>, "onChange" | "value"> {
|
|
206
372
|
value: string;
|
|
207
373
|
onValueChange: (value: string) => void;
|
|
208
|
-
/** Visually-hidden accessible label. Defaults to "Search". */
|
|
374
|
+
/** Visually-hidden accessible label. Defaults to the localized "Search" microcopy. */
|
|
209
375
|
label?: string;
|
|
210
376
|
containerClassName?: string;
|
|
211
377
|
}
|
|
@@ -218,7 +384,7 @@ interface SearchInputProps extends Omit<InputHTMLAttributes<HTMLInputElement>, "
|
|
|
218
384
|
* `<Input>` explicitly AND gates the clear button — while disabled the clear
|
|
219
385
|
* affordance is hidden so it can't mutate the filter mid-request (#269/#8).
|
|
220
386
|
*/
|
|
221
|
-
declare
|
|
387
|
+
declare const SearchInput: react.ForwardRefExoticComponent<SearchInputProps & react.RefAttributes<HTMLInputElement>>;
|
|
222
388
|
|
|
223
389
|
interface FilterBarProps {
|
|
224
390
|
/** Left cluster: search + facet filters. */
|
|
@@ -230,6 +396,34 @@ interface FilterBarProps {
|
|
|
230
396
|
/** Horizontal toolbar that groups table filters and actions. */
|
|
231
397
|
declare function FilterBar({ children, actions, className }: FilterBarProps): react.JSX.Element;
|
|
232
398
|
|
|
399
|
+
interface FilterChipProps extends Omit<FilterChipProps$1, "label" | "trailing"> {
|
|
400
|
+
/**
|
|
401
|
+
* Label-in-value text — `"Status: Failed"`, never `"Status = failed"` and
|
|
402
|
+
* never a bare `"Failed"`. Same contract as the base `FilterChip`.
|
|
403
|
+
*/
|
|
404
|
+
label: string;
|
|
405
|
+
/**
|
|
406
|
+
* How many records this active filter excluded (or matched) — rendered as a
|
|
407
|
+
* secondary, locale-formatted segment alongside `label`. Omit for a bare
|
|
408
|
+
* chip with no count.
|
|
409
|
+
*/
|
|
410
|
+
count?: number;
|
|
411
|
+
/**
|
|
412
|
+
* The word placed before the formatted count, e.g. `"excluded"` →
|
|
413
|
+
* `"excluded 1,204"`. Omitted by default: a bare `count` renders as just the
|
|
414
|
+
* formatted number.
|
|
415
|
+
*/
|
|
416
|
+
countLabel?: string;
|
|
417
|
+
}
|
|
418
|
+
/**
|
|
419
|
+
* A removable active-filter chip with an optional secondary count.
|
|
420
|
+
*
|
|
421
|
+
* `onRemove` stays REQUIRED (inherited from the base `FilterChip`, diverging
|
|
422
|
+
* from this item's spec draft) — the whole chip IS the remove control, so a
|
|
423
|
+
* chip with no removal affordance is a plain `Badge`, not this component.
|
|
424
|
+
*/
|
|
425
|
+
declare const FilterChip: react.ForwardRefExoticComponent<FilterChipProps & react.RefAttributes<HTMLButtonElement>>;
|
|
426
|
+
|
|
233
427
|
interface FacetOption {
|
|
234
428
|
label: string;
|
|
235
429
|
value: string;
|
|
@@ -290,4 +484,4 @@ declare function toCsv<TData extends Record<string, unknown>>(rows: TData[], opt
|
|
|
290
484
|
*/
|
|
291
485
|
declare function downloadCsv<TData extends Record<string, unknown>>(rows: TData[], opts?: DownloadCsvOptions<TData>): void;
|
|
292
486
|
|
|
293
|
-
export { ColumnPicker, type ColumnPickerProps, type CsvColumn, DataTableWithRef as DataTable, type DataTableProps, type DataTableRowClickHandler, type DataTableServerArgs, type DataTableViewState, type DownloadCsvOptions, FacetFilter, type FacetFilterProps, type FacetOption, FilterBar, type FilterBarProps, SearchInput, type SearchInputProps, type ToCsvOptions, downloadCsv, toCsv };
|
|
487
|
+
export { ColumnPicker, type ColumnPickerProps, type CsvColumn, DataTableWithRef as DataTable, type DataTableColumnMeta, type DataTableProps, type DataTableRowClickHandler, type DataTableServerArgs, type DataTableViewState, type DownloadCsvOptions, FacetFilter, type FacetFilterProps, type FacetOption, FilterBar, type FilterBarProps, FilterChip, type FilterChipProps, SearchInput, type SearchInputProps, type ToCsvOptions, createSelectionColumn, downloadCsv, toCsv };
|