@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 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
- These packages are **private** and are not published to any registry they are
10
- consumed from this workspace. See `docs/CONSUMING.md`.
9
+ Published to the **public npm registry** under the `@elabs-ai` scopeit
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
- Inside this monorepo the packages resolve as workspace dependencies:
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
- 5 exported components — including `ColumnPicker`, `DataTable`, `FacetFilter`, `FilterBar`, `SearchInput`.
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
- UNLICENSED — private.
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 function SearchInput({ value, onValueChange, label, placeholder, className, containerClassName, disabled, ...props }: SearchInputProps): react.JSX.Element;
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 };