@elabs-ai/components-data 4.1.0 → 5.0.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.
Files changed (139) hide show
  1. package/README.md +1 -1
  2. package/dist/chunk-3OVN7MMO.js +2 -0
  3. package/dist/chunk-3OVN7MMO.js.map +1 -0
  4. package/dist/chunk-3UFWYVDS.js +2 -0
  5. package/dist/chunk-3UFWYVDS.js.map +1 -0
  6. package/dist/chunk-47ZBQZYS.js +47 -0
  7. package/dist/chunk-47ZBQZYS.js.map +1 -0
  8. package/dist/chunk-5K2IFOV2.js +2 -0
  9. package/dist/chunk-5K2IFOV2.js.map +1 -0
  10. package/dist/chunk-677ORWI6.js +17 -0
  11. package/dist/chunk-677ORWI6.js.map +1 -0
  12. package/dist/chunk-7ZYUY3UL.js +2 -0
  13. package/dist/chunk-7ZYUY3UL.js.map +1 -0
  14. package/dist/chunk-BEYHV3ZX.js +37 -0
  15. package/dist/chunk-BEYHV3ZX.js.map +1 -0
  16. package/dist/chunk-DBPJ7LVM.js +57 -0
  17. package/dist/chunk-DBPJ7LVM.js.map +1 -0
  18. package/dist/chunk-E3V3SYY7.js +87 -0
  19. package/dist/chunk-E3V3SYY7.js.map +1 -0
  20. package/dist/chunk-GBO56756.js +81 -0
  21. package/dist/chunk-GBO56756.js.map +1 -0
  22. package/dist/chunk-HG2YMSRM.js +95 -0
  23. package/dist/chunk-HG2YMSRM.js.map +1 -0
  24. package/dist/chunk-K2GHSRCP.js +1823 -0
  25. package/dist/chunk-K2GHSRCP.js.map +1 -0
  26. package/dist/chunk-KCB33PD3.js +43 -0
  27. package/dist/chunk-KCB33PD3.js.map +1 -0
  28. package/dist/chunk-MV2YVXEZ.js +67 -0
  29. package/dist/chunk-MV2YVXEZ.js.map +1 -0
  30. package/dist/chunk-NKVJUQYW.js +82 -0
  31. package/dist/chunk-NKVJUQYW.js.map +1 -0
  32. package/dist/chunk-OASM53LL.js +29 -0
  33. package/dist/chunk-OASM53LL.js.map +1 -0
  34. package/dist/chunk-RZ5MYZOA.js +2 -0
  35. package/dist/chunk-RZ5MYZOA.js.map +1 -0
  36. package/dist/chunk-SK6347WB.js +100 -0
  37. package/dist/chunk-SK6347WB.js.map +1 -0
  38. package/dist/chunk-TBB33PHQ.js +58 -0
  39. package/dist/chunk-TBB33PHQ.js.map +1 -0
  40. package/dist/chunk-VCLA45IC.js +150 -0
  41. package/dist/chunk-VCLA45IC.js.map +1 -0
  42. package/dist/chunk-XN6O6Z2N.js +58 -0
  43. package/dist/chunk-XN6O6Z2N.js.map +1 -0
  44. package/dist/chunk-ZFEW36RG.js +77 -0
  45. package/dist/chunk-ZFEW36RG.js.map +1 -0
  46. package/dist/chunk-ZQXJ34VJ.js +34 -0
  47. package/dist/chunk-ZQXJ34VJ.js.map +1 -0
  48. package/dist/chunk-ZUCGV2XD.js +42 -0
  49. package/dist/chunk-ZUCGV2XD.js.map +1 -0
  50. package/dist/column-picker/column-picker.js +8 -0
  51. package/dist/column-picker/column-picker.js.map +1 -0
  52. package/dist/column-picker/index.js +9 -0
  53. package/dist/column-picker/index.js.map +1 -0
  54. package/dist/data-table/card-layout.js +11 -0
  55. package/dist/data-table/card-layout.js.map +1 -0
  56. package/dist/data-table/cell-scales.js +26 -0
  57. package/dist/data-table/cell-scales.js.map +1 -0
  58. package/dist/data-table/cells/bar-cell.js +14 -0
  59. package/dist/data-table/cells/bar-cell.js.map +1 -0
  60. package/dist/data-table/cells/category-legend.js +9 -0
  61. package/dist/data-table/cells/category-legend.js.map +1 -0
  62. package/dist/data-table/cells/columns-cell.js +13 -0
  63. package/dist/data-table/cells/columns-cell.js.map +1 -0
  64. package/dist/data-table/cells/heatmap-cell.js +13 -0
  65. package/dist/data-table/cells/heatmap-cell.js.map +1 -0
  66. package/dist/data-table/cells/markdown-cell.js +13 -0
  67. package/dist/data-table/cells/markdown-cell.js.map +1 -0
  68. package/dist/data-table/cells/sparkline-cell.js +13 -0
  69. package/dist/data-table/cells/sparkline-cell.js.map +1 -0
  70. package/dist/data-table/column-meta.js +12 -0
  71. package/dist/data-table/column-meta.js.map +1 -0
  72. package/dist/data-table/data-table.js +23 -0
  73. package/dist/data-table/data-table.js.map +1 -0
  74. package/dist/data-table/index.js +23 -0
  75. package/dist/data-table/index.js.map +1 -0
  76. package/dist/data-table/ranks-column.js +13 -0
  77. package/dist/data-table/ranks-column.js.map +1 -0
  78. package/dist/data-table/sticky-rows.js +10 -0
  79. package/dist/data-table/sticky-rows.js.map +1 -0
  80. package/dist/data-table/use-table-breakpoint.js +13 -0
  81. package/dist/data-table/use-table-breakpoint.js.map +1 -0
  82. package/dist/facet-filter/facet-filter.js +8 -0
  83. package/dist/facet-filter/facet-filter.js.map +1 -0
  84. package/dist/facet-filter/index.js +9 -0
  85. package/dist/facet-filter/index.js.map +1 -0
  86. package/dist/filter-bar/filter-bar.js +8 -0
  87. package/dist/filter-bar/filter-bar.js.map +1 -0
  88. package/dist/filter-bar/filter-chip.js +9 -0
  89. package/dist/filter-bar/filter-chip.js.map +1 -0
  90. package/dist/filter-bar/index.js +13 -0
  91. package/dist/filter-bar/index.js.map +1 -0
  92. package/dist/index.d.ts +554 -10
  93. package/dist/index.js +102 -1494
  94. package/dist/index.js.map +1 -1
  95. package/dist/search-input/index.js +9 -0
  96. package/dist/search-input/index.js.map +1 -0
  97. package/dist/search-input/search-input.js +9 -0
  98. package/dist/search-input/search-input.js.map +1 -0
  99. package/dist/to-csv.js +10 -0
  100. package/dist/to-csv.js.map +1 -0
  101. package/package.json +7 -7
  102. package/src/__contract__/filter-chip.contract.test.tsx +49 -0
  103. package/src/column-picker/column-picker.tsx +3 -2
  104. package/src/data-table/card-layout.test.tsx +26 -0
  105. package/src/data-table/card-layout.tsx +74 -0
  106. package/src/data-table/cell-scales.test.ts +118 -0
  107. package/src/data-table/cell-scales.ts +246 -0
  108. package/src/data-table/cells/bar-cell.test.tsx +158 -0
  109. package/src/data-table/cells/bar-cell.tsx +135 -0
  110. package/src/data-table/cells/category-legend.test.tsx +37 -0
  111. package/src/data-table/cells/category-legend.tsx +58 -0
  112. package/src/data-table/cells/columns-cell.test.tsx +27 -0
  113. package/src/data-table/cells/columns-cell.tsx +79 -0
  114. package/src/data-table/cells/heatmap-cell.test.tsx +31 -0
  115. package/src/data-table/cells/heatmap-cell.tsx +116 -0
  116. package/src/data-table/cells/markdown-cell.test.tsx +36 -0
  117. package/src/data-table/cells/markdown-cell.tsx +91 -0
  118. package/src/data-table/cells/sparkline-cell.test.tsx +43 -0
  119. package/src/data-table/cells/sparkline-cell.tsx +113 -0
  120. package/src/data-table/column-meta.test.ts +42 -0
  121. package/src/data-table/column-meta.ts +282 -0
  122. package/src/data-table/data-table.stories.tsx +748 -1
  123. package/src/data-table/data-table.test.tsx +547 -11
  124. package/src/data-table/data-table.tsx +1014 -175
  125. package/src/data-table/ranks-column.test.tsx +65 -0
  126. package/src/data-table/ranks-column.tsx +91 -0
  127. package/src/data-table/sticky-rows.test.ts +16 -0
  128. package/src/data-table/sticky-rows.ts +77 -0
  129. package/src/data-table/use-table-breakpoint.test.ts +14 -0
  130. package/src/data-table/use-table-breakpoint.ts +67 -0
  131. package/src/facet-filter/facet-filter.stories.tsx +4 -1
  132. package/src/facet-filter/facet-filter.test.tsx +3 -3
  133. package/src/filter-bar/filter-chip.stories.tsx +1 -0
  134. package/src/index.ts +64 -0
  135. package/src/search-input/search-input.test.tsx +35 -0
  136. package/src/search-input/search-input.tsx +44 -19
  137. package/src/templates-data-app.stories.tsx +7 -2
  138. package/src/to-csv.test.ts +13 -0
  139. package/src/to-csv.ts +7 -30
package/dist/index.d.ts CHANGED
@@ -1,14 +1,200 @@
1
1
  import * as react from 'react';
2
- import { ReactNode, InputHTMLAttributes, ButtonHTMLAttributes, Ref, ReactElement } from 'react';
3
- import { RowData, ColumnDef, Table, SortingState, OnChangeFn, VisibilityState, ColumnFiltersState, PaginationState, ColumnPinningState, ColumnSizingState, RowSelectionState, Row } from '@tanstack/react-table';
2
+ import { CSSProperties, ReactNode, InputHTMLAttributes, ButtonHTMLAttributes, Ref, ReactElement, TdHTMLAttributes, ThHTMLAttributes, LiHTMLAttributes, HTMLAttributes } from 'react';
3
+ import { RowPinningState, RowData, ColumnDef, Table, SortingState, OnChangeFn, VisibilityState, ColumnFiltersState, PaginationState, ColumnPinningState, ColumnSizingState, RowSelectionState, Row } from '@tanstack/react-table';
4
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
+ import { ColorScaleDomain, FilterChipProps as FilterChipProps$1, ColorScale, ColorScaleSpec } from '@elabs-ai/components-ui';
6
6
 
7
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.
8
+ * sticky-rows.ts — `DataTable`'s `stickyRows` (RM-123): rows such as an
9
+ * "average" or "total" that stay at the top or bottom of EVERY page and after
10
+ * every sort.
11
+ *
12
+ * Built on TanStack's own row pinning (`keepPinnedRows`), so the table model is
13
+ * unchanged: a sticky row keeps its id, its selection and its `data` index; it
14
+ * is only rendered outside the sorted / paged centre rows.
15
+ */
16
+ type DataTableStickyRowPosition = "top" | "bottom";
17
+ /** Decides, per record, whether its row sticks to the top or the bottom (or neither). */
18
+ type DataTableStickyRows<TData> = (row: TData, index: number) => DataTableStickyRowPosition | undefined;
19
+ /**
20
+ * The TanStack `rowPinning` state for `stickyRows` over `data`, in data order.
21
+ * Row ids follow TanStack's own rule: `getRowId(row, index)`, else the index.
22
+ */
23
+ declare function stickyRowPinning<TData>(data: readonly TData[], stickyRows: DataTableStickyRows<TData> | undefined, getRowId?: (row: TData, index: number) => string): RowPinningState;
24
+
25
+ /** A table container's width tier. */
26
+ type DataTableBreakpoint = "narrow" | "wide";
27
+ /** A width (CSS px) below `narrow` is narrow. */
28
+ declare const DATA_TABLE_BREAKPOINT_THRESHOLDS: {
29
+ readonly narrow: 450;
30
+ };
31
+ /** The tier for a measured container width. */
32
+ declare function tableBreakpointForWidth(width: number): DataTableBreakpoint;
33
+ /**
34
+ * Measures the element the returned `ref` is attached to with a
35
+ * `ResizeObserver` and returns its tier. Re-renders only when the TIER changes.
36
+ * `enabled: false` skips the observer entirely (a table that uses no
37
+ * breakpoint behaviour pays nothing).
38
+ */
39
+ declare function useTableBreakpoint<E extends Element = HTMLDivElement>(enabled?: boolean): {
40
+ ref: (node: E | null) => void;
41
+ breakpoint: DataTableBreakpoint;
42
+ };
43
+
44
+ /**
45
+ * column-meta.ts — the typed `columnDef.meta` contract `DataTable` reads.
46
+ *
47
+ * `numeric` / `align` (#69) style a column; everything else is the editorial
48
+ * presentation layer (RM-123): an in-cell visual, a number format, categorical
49
+ * colouring, per-breakpoint visibility and column sizing. Every key is optional
50
+ * and a column without them renders exactly as before.
51
+ *
52
+ * `data` never imports `@elabs-ai/components-charts` (one-way dependency
53
+ * graph), so the number-format shape below is a STRUCTURAL twin of charts'
54
+ * `ChartValueFormatSpec` (RM-109): a spec written for a chart type-checks here
55
+ * unchanged, and formats the same way.
56
+ */
57
+ /**
58
+ * A number format — the same object shape as charts' `ChartValueFormatSpec`.
59
+ *
60
+ * - `style`: `"number"` (default), `"currency"`, or `"percent"` (fraction in,
61
+ * `%` out).
62
+ * - `decimals`: maximum fraction digits. `optionalDecimals: false` makes it
63
+ * the minimum too.
64
+ * - `abbreviate`: `true` always compact (`1.5K`), `false` never, `"auto"`
65
+ * (default) from 1,000 up. Percent is never compacted.
66
+ * - `sign`: `"auto"` (minus only), `"always"` (`+`/`-`), `"parens"` (`(12)`).
67
+ * - `prefix` / `suffix`: literal text around the number.
68
+ * - `grouping: false` drops the thousands separator.
69
+ * - `currency`: ISO 4217 code for `style: "currency"` (default `"USD"`).
70
+ */
71
+ interface DataTableValueFormatSpec {
72
+ style?: "number" | "currency" | "percent";
73
+ decimals?: number;
74
+ optionalDecimals?: boolean;
75
+ abbreviate?: boolean | "auto";
76
+ sign?: "auto" | "always" | "parens";
77
+ prefix?: string;
78
+ suffix?: string;
79
+ grouping?: boolean;
80
+ currency?: string;
81
+ }
82
+ /** An in-cell bar: the value as a horizontal bar from zero. */
83
+ interface DataTableBarVisual {
84
+ kind: "bar";
85
+ /** `"regular"` (default) sits beside the value; `"slim"` is a thin rule under it. */
86
+ style?: "regular" | "slim";
87
+ /** Paint the grey remainder of the range behind the bar. */
88
+ track?: boolean;
89
+ /**
90
+ * The range a full-width bar spans. `"column"` (default): this column's own
91
+ * min / max; `"table"`: shared by every bar column with `range: "table"`;
92
+ * `[min, max]`: fixed. Zero is always inside the range.
93
+ */
94
+ range?: readonly [min: number, max: number] | "column" | "table";
95
+ /** Row key whose category picks the bar colour (`--chart-1…12`). */
96
+ colorBy?: string;
97
+ /**
98
+ * The `colorBy` key's title, or `false` to draw no key. Default `true`: a
99
+ * category carried by hue alone is unreadable without one (WCAG 1.4.1), so
100
+ * the key is opt-OUT — turn it off only when the table prints the category
101
+ * in a column of its own.
102
+ */
103
+ legend?: string | boolean;
104
+ /** Paint negative values in the negative token. Default `true`. */
105
+ negative?: boolean;
106
+ }
107
+ /** An in-cell sparkline across several numeric row keys (one point per key). */
108
+ interface DataTableSparklineVisual {
109
+ kind: "sparkline";
110
+ /** Row keys, in x order. */
111
+ keys: readonly string[];
112
+ /** Fill the area under the line. */
113
+ fill?: boolean;
114
+ /** `"ends"` prints the first and last values beside the line. Default `"none"`. */
115
+ labels?: "ends" | "none";
116
+ /** Drawing height in px. Default 24. */
117
+ height?: number;
118
+ /** `"cell"` (default): this row's own min / max; `"column"`: shared by every row. */
119
+ range?: "cell" | "column";
120
+ }
121
+ /** In-cell mini columns across several numeric row keys. */
122
+ interface DataTableColumnsVisual {
123
+ kind: "columns";
124
+ keys: readonly string[];
125
+ /** Drawing height in px. Default 24. */
126
+ height?: number;
127
+ /** `"cell"` (default): this row's own max; `"column"`: shared by every row. */
128
+ range?: "cell" | "column";
129
+ }
130
+ /**
131
+ * A heatmap colour scale. Resolved by `colorScaleFor` (`@elabs-ai/components-ui`),
132
+ * so the colours are `--chart-seq-*` / `--chart-div-*` ramp tokens.
133
+ */
134
+ interface DataTableHeatmapScale {
135
+ /** `"stepped"` cuts countable classes; `"continuous"` snaps to the nearest ramp step. */
136
+ type: "stepped" | "continuous";
137
+ /** How many classes (stepped) or Jenks classes (continuous `jenks`). Default 5. */
138
+ steps?: number;
139
+ /**
140
+ * `"linear"` (default) equal widths; `"quantile"` equal counts; `"jenks"`
141
+ * natural breaks; `"custom"` the `breaks` you pass (always stepped).
142
+ */
143
+ method?: "linear" | "quantile" | "jenks" | "custom";
144
+ /** `method: "custom"` only: the inner thresholds, ascending. */
145
+ breaks?: readonly number[];
146
+ /** `[min, max]`, or `[min, center, max]` to pin the ramp's middle colour. */
147
+ domain?: ColorScaleDomain;
148
+ /** Default `"diverging"` when `domain` has a centre, else `"sequential"`. */
149
+ palette?: "sequential" | "diverging";
150
+ }
151
+ /** The cell's background is the value's ramp colour. */
152
+ interface DataTableHeatmapVisual {
153
+ kind: "heatmap";
154
+ scale: DataTableHeatmapScale;
155
+ /** Hide the printed value visually; screen readers, sorting and copy still read it. */
156
+ hideValue?: boolean;
157
+ /**
158
+ * Print the scale's colour key above the table. `true` titles it with the
159
+ * column header; a string is the title (use it when one heatmap spans several
160
+ * columns — the key is printed once per shared scale).
161
+ */
162
+ legend?: boolean | string;
163
+ }
164
+ type DataTableCellVisual = DataTableBarVisual | DataTableSparklineVisual | DataTableColumnsVisual | DataTableHeatmapVisual;
165
+ /**
166
+ * Categorical conditional formatting: the category in row key `key` picks a
167
+ * `--chart-1…12` colour, painted as a background wash or as text ink, on this
168
+ * column's cell or on the whole row.
169
+ */
170
+ interface DataTableColorBy {
171
+ key: string;
172
+ target: "background" | "text";
173
+ /** Default `"cell"`. */
174
+ scope?: "cell" | "row";
175
+ /**
176
+ * The colour key's title, or `false` to draw none. Default `true` — see
177
+ * `DataTableBarVisual.legend`.
178
+ */
179
+ legend?: string | boolean;
180
+ }
181
+ /**
182
+ * Show a column at a table breakpoint. `true` / `false` for every width, or
183
+ * `{ base, narrow }`: `base` is the wide value, `narrow` applies below 450 px.
184
+ */
185
+ type DataTableShowAt = boolean | {
186
+ base: boolean;
187
+ narrow?: boolean;
188
+ };
189
+ /** `markdown: true` or options. Images are off unless `images: true`. */
190
+ interface DataTableMarkdownOptions {
191
+ images?: boolean;
192
+ }
193
+ /**
194
+ * `DataTable`'s `columnDef.meta` contract, read by the header, body, card and
195
+ * skeleton cell renderers. Set `numeric: true` on a column to get
196
+ * `tabular-nums` + end-alignment on both the `<th>` and every `<td>`
197
+ * (including the loading skeleton) for free.
12
198
  */
13
199
  interface DataTableColumnMeta {
14
200
  /** Numeric column: tabular figures + end alignment on header and cells. */
@@ -20,11 +206,42 @@ interface DataTableColumnMeta {
20
206
  * alignment away from `"end"`.
21
207
  */
22
208
  align?: "start" | "center" | "end";
209
+ /**
210
+ * An in-cell visual (bar, sparkline, mini columns, heatmap). The cell keeps
211
+ * its value as text — visually hidden only with `hideValue` — so sorting,
212
+ * copy and screen readers still read the number. Replaces the column's
213
+ * `cell` renderer.
214
+ */
215
+ visual?: DataTableCellVisual;
216
+ /** Number format for the printed value (the charts `valueFormat` object shape). */
217
+ format?: DataTableValueFormatSpec;
218
+ /** Categorical colouring driven by another row key. */
219
+ colorBy?: DataTableColorBy;
220
+ /** Per-breakpoint visibility; render-only, never written to `columnVisibility`. */
221
+ showAt?: DataTableShowAt;
222
+ /** Column width as a percentage of the table width. */
223
+ width?: number;
224
+ /** Minimum column width in px. */
225
+ minWidth?: number;
226
+ /** Extra inline style on this column's header and body cells. */
227
+ style?: CSSProperties;
228
+ /** Render a string value as safe inline markdown (bold, italic, links, `sup`, `code`). */
229
+ markdown?: boolean | DataTableMarkdownOptions;
23
230
  }
24
231
  declare module "@tanstack/react-table" {
25
232
  interface ColumnMeta<TData extends RowData, TValue> extends DataTableColumnMeta {
26
233
  }
27
234
  }
235
+ /**
236
+ * The printed text for a cell value. Numbers go through `format` (or the
237
+ * locale's plain number format); anything else is stringified; nullish is `""`.
238
+ */
239
+ declare function formatCellValue(value: unknown, format: DataTableValueFormatSpec | undefined, formatNumber: (n: number, opts?: Intl.NumberFormatOptions) => string): string;
240
+ /** Whether a column is shown at `breakpoint` (no `showAt` → always). */
241
+ declare function resolveShowAt(showAt: DataTableShowAt | undefined, breakpoint: DataTableBreakpoint): boolean;
242
+ /** The inline style for `meta.width` (%), `meta.minWidth` (px) and `meta.style`. */
243
+ declare function columnSizeStyle(meta: DataTableColumnMeta | undefined): CSSProperties | undefined;
244
+
28
245
  /** Snapshot of table slice state — used for saved-view serialise/rehydrate. */
29
246
  interface DataTableViewState {
30
247
  sorting: SortingState;
@@ -266,6 +483,61 @@ interface DataTableProps<TData, TValue> extends Omit<React.HTMLAttributes<HTMLDi
266
483
  * (a `border-border-strong` divider between rows, no stripes).
267
484
  */
268
485
  zebra?: boolean;
486
+ /**
487
+ * Draw a quiet `--rule` hairline between columns (header and body). Off by
488
+ * default. Pinned cells keep their own seam and never take a divider.
489
+ */
490
+ columnDividers?: boolean;
491
+ /**
492
+ * `"table"` (default): always a `<table>`. `"cards"`: always one card per
493
+ * row (a `<dl>` of header → cell). `"auto"`: cards while the table's own
494
+ * container is narrower than 450 px, a `<table>` above. Only the markup
495
+ * changes; sorting, filtering, paging and selection use the same table.
496
+ */
497
+ layout?: "auto" | "table" | "cards";
498
+ /**
499
+ * Rows pinned to the top or bottom of every page. An "average" or "total"
500
+ * row stays put through sorting, paging and search. Receives each record
501
+ * and its `data` index.
502
+ */
503
+ stickyRows?: DataTableStickyRows<TData>;
504
+ /**
505
+ * Prepend a 1…n rank column, in `data` order. Sticky rows are not ranked;
506
+ * the rank travels with its row — sorting never renumbers it.
507
+ */
508
+ showRanks?: boolean;
509
+ /**
510
+ * What the rank column is CALLED — its accessible name, its `title`, the
511
+ * term in the cards layout, and the key printed above the table. Defaults to
512
+ * the locale seam (`data.table.rankHeader` / `data.table.rankKey`).
513
+ *
514
+ * The number is the row's position in `data`, not its position on screen, so
515
+ * beside a sorted column it reads 2, 1, 6, 4 on purpose. A column headed by
516
+ * a bare "#" gives a reader no way to know that; this names it.
517
+ */
518
+ rankLabel?: string;
519
+ /** `"compact"` tightens row and header height. Default `"default"`. */
520
+ density?: "default" | "compact";
521
+ /**
522
+ * Span an ungrouped column's header over the empty group rows. With
523
+ * grouped headers, the placeholder cells above a column merge into one.
524
+ */
525
+ mergeEmptyHeaders?: boolean;
526
+ /**
527
+ * `"exact"`: a row matches only when a cell equals the query. Default
528
+ * `"contains"` (TanStack's substring search); both ignore case.
529
+ */
530
+ searchMode?: "contains" | "exact";
531
+ /**
532
+ * Hide the header row visually (a pixel heatmap). The headers stay for
533
+ * screen readers, and a focused sort button still shows itself.
534
+ *
535
+ * Sorting is then KEYBOARD-reachable, not clickable: the band has no height,
536
+ * so it offers no hit area — deliberately, since an invisible click target
537
+ * over the first data row would be worse than none. Tab to the column's sort
538
+ * button (it becomes visible on focus) and press Enter.
539
+ */
540
+ hideHeader?: boolean;
269
541
  /**
270
542
  * Opt-in row drag-reorder. Off by default — an existing table renders
271
543
  * byte-identical markup with no extra DOM per row until this is set.
@@ -283,6 +555,10 @@ interface DataTableProps<TData, TValue> extends Omit<React.HTMLAttributes<HTMLDi
283
555
  * is silently disabled (a dev warning fires) when both are set. Combining
284
556
  * it with active `sorting` also fires a dev warning (both still work, but
285
557
  * a sort re-orders the very rows a drag just moved, which reads as broken).
558
+ *
559
+ * Table-only. The card layout (`layout="cards"`, or `"auto"` at the narrow
560
+ * tier) has no grip column and no row to drop onto, so reorder is a no-op
561
+ * there and `onRowReorder` never fires; a dev warning says so once per mount.
286
562
  */
287
563
  enableRowReorder?: boolean;
288
564
  /**
@@ -366,7 +642,7 @@ declare const DataTableWithRef: <TData, TValue>(props: DataTableProps<TData, TVa
366
642
  interface SearchInputProps extends Omit<InputHTMLAttributes<HTMLInputElement>, "onChange" | "value"> {
367
643
  value: string;
368
644
  onValueChange: (value: string) => void;
369
- /** Visually-hidden accessible label. Defaults to "Search". */
645
+ /** Visually-hidden accessible label. Defaults to the localized "Search" microcopy. */
370
646
  label?: string;
371
647
  containerClassName?: string;
372
648
  }
@@ -379,7 +655,7 @@ interface SearchInputProps extends Omit<InputHTMLAttributes<HTMLInputElement>, "
379
655
  * `<Input>` explicitly AND gates the clear button — while disabled the clear
380
656
  * affordance is hidden so it can't mutate the filter mid-request (#269/#8).
381
657
  */
382
- declare function SearchInput({ value, onValueChange, label, placeholder, className, containerClassName, disabled, ...props }: SearchInputProps): react.JSX.Element;
658
+ declare const SearchInput: react.ForwardRefExoticComponent<SearchInputProps & react.RefAttributes<HTMLInputElement>>;
383
659
 
384
660
  interface FilterBarProps {
385
661
  /** Left cluster: search + facet filters. */
@@ -479,4 +755,272 @@ declare function toCsv<TData extends Record<string, unknown>>(rows: TData[], opt
479
755
  */
480
756
  declare function downloadCsv<TData extends Record<string, unknown>>(rows: TData[], opts?: DownloadCsvOptions<TData>): void;
481
757
 
482
- 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 };
758
+ /**
759
+ * cell-scales.ts — the shared scales behind `DataTable`'s in-cell visuals
760
+ * (RM-123). Pure: no React, no DOM.
761
+ *
762
+ * Every visual cell reads ONE scale per column, computed once over the full
763
+ * data (never the current page, filter or sort), so a bar, a sparkline or a
764
+ * heatmap colour means the same thing on every page and after every sort.
765
+ * Colour decisions (heatmap ramps, categories) come from `colorScaleFor`
766
+ * (`@elabs-ai/components-ui`) — the one value → colour-token scale `charts`,
767
+ * `data` and `maps` share — so colours are `var(--chart-…)` references.
768
+ */
769
+
770
+ /** `[min, max]` of the finite numbers in a set. */
771
+ interface NumericExtent {
772
+ min: number;
773
+ max: number;
774
+ }
775
+ /** The extent of the finite numbers in `values`; `null` when there are none. */
776
+ declare function extentOf(values: readonly unknown[]): NumericExtent | null;
777
+ /** A bar domain always contains zero, so a bar is always measured from zero. */
778
+ declare function barDomain(range: DataTableBarVisual["range"], column: NumericExtent | null, table: NumericExtent | null): readonly [number, number];
779
+ /**
780
+ * Where a bar sits inside its track, as percentages of the track width:
781
+ * `start` is the inline-start edge, `size` the length. Negative values grow
782
+ * toward inline-start from zero. Both are clamped to 0…100.
783
+ */
784
+ declare function barGeometry(value: number, domain: readonly [number, number]): {
785
+ start: number;
786
+ size: number;
787
+ zero: number;
788
+ negative: boolean;
789
+ };
790
+ /** A row's numbers for `keys`, `null` where a key holds no finite number. */
791
+ declare function seriesValues(original: unknown, keys: readonly string[]): (number | null)[];
792
+ /** The `ColorScaleSpec` a heatmap scale asks `colorScaleFor` for. */
793
+ declare function heatmapColorScaleSpec(scale: DataTableHeatmapScale): ColorScaleSpec;
794
+ /** Everything a column's visual cells share. Only the parts the column uses are set. */
795
+ interface DataTableColumnScale {
796
+ /** Extent of the column's own values. */
797
+ extent: NumericExtent | null;
798
+ /** Bar visual: the resolved `[lo, hi]` (always contains zero). */
799
+ barDomain?: readonly [number, number];
800
+ /** Bar `colorBy`: the category → colour scale. */
801
+ barCategory?: ColorScale;
802
+ /** Sparkline / columns: extent of every row's series values. */
803
+ seriesExtent?: NumericExtent | null;
804
+ /**
805
+ * Heatmap: the value → ramp-colour scale. Heatmap columns with an identical
806
+ * `scale` spec SHARE one scale over all their values (one heatmap across
807
+ * several columns); `heatmapGroup` names it.
808
+ */
809
+ heatmap?: ColorScale;
810
+ heatmapGroup?: string;
811
+ /** `meta.colorBy`: the category → colour scale. */
812
+ category?: ColorScale;
813
+ }
814
+ /** A minimal row: what `computeColumnScales` needs from a TanStack row. */
815
+ interface ScaleRow {
816
+ original: unknown;
817
+ getValue: (columnId: string) => unknown;
818
+ }
819
+ /** A minimal column: its id and meta. */
820
+ interface ScaleColumn {
821
+ id: string;
822
+ meta: DataTableColumnMeta | undefined;
823
+ }
824
+ /**
825
+ * One scale per column that needs one (a `visual` or a `colorBy`), over ALL
826
+ * `rows`. Columns with neither are absent from the map.
827
+ */
828
+ declare function computeColumnScales(columns: readonly ScaleColumn[], rows: readonly ScaleRow[]): Map<string, DataTableColumnScale>;
829
+
830
+ /**
831
+ * ranks-column.tsx — `DataTable`'s `showRanks` column (RM-123).
832
+ *
833
+ * A rank belongs to the ROW: it is the row's 1-based position in the `data`
834
+ * you passed (sticky rows excluded), so re-sorting, filtering or paging moves
835
+ * the numbers with their rows and never renumbers them. It is rendered beside
836
+ * the TanStack columns, not as one, so the table model is unchanged.
837
+ */
838
+ /** Row id → rank (1…n in data order), skipping the ids in `exclude`. */
839
+ declare function computeRowRanks(rowIds: readonly string[], exclude?: ReadonlySet<string>): Map<string, number>;
840
+ interface DataTableRankHeaderProps extends ThHTMLAttributes<HTMLTableCellElement> {
841
+ /**
842
+ * The column's accessible NAME, and its `title`. Defaults to the locale
843
+ * seam's `data.table.rankHeader`.
844
+ *
845
+ * The visible glyph stays "#" — it has to, in a 40px column — so the glyph
846
+ * is decorative (`aria-hidden`) and this string is what the column is
847
+ * actually called. Without it the header's accessible name is the single
848
+ * character "#", which says nothing about WHICH position is printed: beside
849
+ * a sorted column the numbers legitimately read 2, 1, 6, 4 and a reader has
850
+ * no way to learn why. `DataTable` prints the same rule visibly, once, above
851
+ * the table (`data-slot="data-table-rank-key"`).
852
+ */
853
+ label?: string;
854
+ }
855
+ /**
856
+ * The ranks column's header cell: "#" as the visible glyph, `label` as the
857
+ * name AT reads.
858
+ */
859
+ declare const DataTableRankHeader: react.ForwardRefExoticComponent<DataTableRankHeaderProps & react.RefAttributes<HTMLTableCellElement>>;
860
+ /** One row's rank cell; `rank` undefined (a sticky row) renders empty. */
861
+ declare const DataTableRankCell: react.ForwardRefExoticComponent<TdHTMLAttributes<HTMLTableCellElement> & {
862
+ rank?: string;
863
+ } & react.RefAttributes<HTMLTableCellElement>>;
864
+
865
+ /**
866
+ * card-layout.tsx — `DataTable`'s transposed card rows (RM-123, `layout`).
867
+ *
868
+ * Under the table's `narrow` breakpoint (`layout="auto"`) or always
869
+ * (`layout="cards"`), each row renders as one card: a `<dl>` whose terms are
870
+ * the column headers and whose details are the row's cells. Only the markup
871
+ * changes — the TanStack model (sorting, filtering, paging, selection) is the
872
+ * same instance the table uses, so the toolbar and pager keep working.
873
+ */
874
+ /** One term / detail pair of a card. */
875
+ interface DataTableCardField {
876
+ /** Stable key: the column id. */
877
+ id: string;
878
+ term: ReactNode;
879
+ value: ReactNode;
880
+ /** Extra classes / style for the detail (alignment, a colour wash). */
881
+ className?: string;
882
+ style?: React.CSSProperties;
883
+ }
884
+ /** The list that holds the cards. Rows are separated by a strong divider. */
885
+ declare const DataTableCardList: react.ForwardRefExoticComponent<HTMLAttributes<HTMLUListElement> & react.RefAttributes<HTMLUListElement>>;
886
+ interface DataTableCardProps extends LiHTMLAttributes<HTMLLIElement> {
887
+ fields: readonly DataTableCardField[];
888
+ /** Content above the fields (the selection checkbox, the rank, the row action). */
889
+ lead?: ReactNode;
890
+ /** `compact` tightens the padding, like the table's `density="compact"`. */
891
+ density?: "default" | "compact";
892
+ }
893
+ /** One row as a card: a `<dl>` of header → cell pairs. */
894
+ declare const DataTableCard: react.ForwardRefExoticComponent<DataTableCardProps & react.RefAttributes<HTMLLIElement>>;
895
+
896
+ interface BarCellProps extends HTMLAttributes<HTMLDivElement> {
897
+ /** The number the bar draws; `null` draws no bar. */
898
+ value: number | null;
899
+ /** The printed (formatted) value. Always in the accessible tree. */
900
+ label: string;
901
+ /** `[lo, hi]` of the track; always contains zero (see `barDomain`). */
902
+ domain: readonly [number, number];
903
+ /** `"regular"` (default): bar beside the value; `"slim"`: a thin bar under it. */
904
+ variant?: "regular" | "slim";
905
+ /** Paint the grey remainder of the track. */
906
+ track?: boolean;
907
+ /** A category colour (`var(--chart-N)`) that replaces the positive fill. */
908
+ fillColor?: string | null;
909
+ /** Paint a negative value in the negative token. Default `true`. */
910
+ negativeColor?: boolean;
911
+ /**
912
+ * Room reserved for the printed value, in `ch` (`labelBoxCh` over the whole
913
+ * column). Every bar in a column then shares one track length, so bar lengths
914
+ * compare down the column and a diverging column's zero rule keeps one x.
915
+ * Unset (or `"slim"`, where the bar sits under the value) leaves the value
916
+ * box to size itself.
917
+ */
918
+ labelWidth?: number;
919
+ }
920
+ /**
921
+ * An in-cell bar (CSS only). The bar grows from zero — toward inline-end for a
922
+ * positive value, toward inline-start for a negative one — inside a track that
923
+ * spans the column's shared domain. The value stays printed beside it.
924
+ */
925
+ declare const BarCell: react.ForwardRefExoticComponent<BarCellProps & react.RefAttributes<HTMLDivElement>>;
926
+
927
+ interface CategoryLegendProps extends HTMLAttributes<HTMLDivElement> {
928
+ /** A categorical scale — its `categories`, in first-seen order. */
929
+ scale: ColorScale;
930
+ /** What the colours divide the rows BY (the `colorBy` key, or a nicer name). */
931
+ title?: string;
932
+ /** Print a category value; defaults to `String`. */
933
+ formatCategory?: (value: string | number) => string;
934
+ }
935
+ /**
936
+ * A `colorBy` column's colour key: one named swatch per category.
937
+ *
938
+ * A categorical fill answers "which group", and unlike a bar's length or a
939
+ * heatmap's ramp there is nothing in the cell to read it off. Without a key the
940
+ * category is carried by hue alone — unreadable to anyone who cannot separate
941
+ * those hues, and unnameable by everyone else (WCAG 1.4.1). This is the same
942
+ * key `HeatmapLegend` gives an ordered scale, for an unordered one.
943
+ */
944
+ declare const CategoryLegend: react.ForwardRefExoticComponent<CategoryLegendProps & react.RefAttributes<HTMLDivElement>>;
945
+
946
+ interface SparklineCellProps extends HTMLAttributes<HTMLDivElement> {
947
+ values: readonly (number | null)[];
948
+ /** The y domain: this row's own extent (`range: "cell"`) or the column's. */
949
+ domain: NumericExtent | null;
950
+ /** Every value as text, for screen readers, sorting and copy. */
951
+ label: string;
952
+ /** Printed first / last values (with `labels: "ends"`). */
953
+ ends?: readonly [first: string, last: string];
954
+ /**
955
+ * Room reserved for those two labels, in `ch` (`labelBoxCh` over the whole
956
+ * column). The drawing takes what they leave over, so one reservation per
957
+ * column keeps every row's line on the same x scale.
958
+ */
959
+ endsWidth?: readonly [first: number, last: number];
960
+ fill?: boolean;
961
+ /** Drawing height in px. Default 24. */
962
+ height?: number;
963
+ }
964
+ /** An in-cell sparkline: a ≤ 40-line inline SVG on a shared or own y domain. */
965
+ declare const SparklineCell: react.ForwardRefExoticComponent<SparklineCellProps & react.RefAttributes<HTMLDivElement>>;
966
+
967
+ interface ColumnsCellProps extends HTMLAttributes<HTMLDivElement> {
968
+ /** One entry per row key, in x order; a `null` value draws no column. */
969
+ values: readonly {
970
+ key: string;
971
+ value: number | null;
972
+ }[];
973
+ /** The value domain: this row's own extent or the column's. Zero is always added. */
974
+ domain: NumericExtent | null;
975
+ /** Every value as text, for screen readers, sorting and copy. */
976
+ label: string;
977
+ /** Drawing height in px. Default 24. */
978
+ height?: number;
979
+ }
980
+ /** In-cell mini columns: a ≤ 40-line inline SVG, columns grow from a zero rule. */
981
+ declare const ColumnsCell: react.ForwardRefExoticComponent<ColumnsCellProps & react.RefAttributes<HTMLDivElement>>;
982
+
983
+ /**
984
+ * The `<td>` style that paints a heatmap cell: the value's ramp colour (a
985
+ * `var(--chart-seq-*)` / `var(--chart-div-*)` reference from `colorScaleFor`),
986
+ * so a theme flip re-colours it with no re-render. `null` (no data) paints
987
+ * nothing.
988
+ */
989
+ declare function heatmapCellStyle(color: string | null): CSSProperties | undefined;
990
+ interface HeatmapCellProps extends HTMLAttributes<HTMLSpanElement> {
991
+ /** The printed (formatted) value. Always in the accessible tree. */
992
+ label: string;
993
+ /** Hide the value visually; it stays for screen readers, sorting and copy. */
994
+ hideValue?: boolean;
995
+ }
996
+ /**
997
+ * A heatmap cell's content. The `<td>` carries the colour
998
+ * ({@link heatmapCellStyle}); a visible value sits on a card plate, so its ink
999
+ * reads on every ramp step in every theme without resolving the colour.
1000
+ */
1001
+ declare const HeatmapCell: react.ForwardRefExoticComponent<HeatmapCellProps & react.RefAttributes<HTMLSpanElement>>;
1002
+ interface HeatmapLegendProps extends HTMLAttributes<HTMLDivElement> {
1003
+ scale: ColorScale;
1004
+ /** What the colours measure (the column's header). */
1005
+ title?: string;
1006
+ formatValue: (value: number) => string;
1007
+ }
1008
+ /**
1009
+ * A heatmap column's colour key: one swatch per class (stepped) or a ramp
1010
+ * strip (continuous), with the class bounds printed in the `text-meta` role.
1011
+ */
1012
+ declare const HeatmapLegend: react.ForwardRefExoticComponent<HeatmapLegendProps & react.RefAttributes<HTMLDivElement>>;
1013
+
1014
+ /** `true` for a URL a cell may link to or load. */
1015
+ declare function isSafeCellUrl(url: string): boolean;
1016
+ /** Parses `text` into React nodes. Exported for tests. */
1017
+ declare function parseCellMarkdown(text: string, images?: boolean, keyPrefix?: string): ReactNode[];
1018
+ interface MarkdownCellProps extends HTMLAttributes<HTMLSpanElement> {
1019
+ text: string;
1020
+ /** Render `![alt](src)` as an image. Default `false` (the alt text prints). */
1021
+ images?: boolean;
1022
+ }
1023
+ /** A cell rendered from safe inline markdown (see {@link parseCellMarkdown}). */
1024
+ declare const MarkdownCell: react.ForwardRefExoticComponent<MarkdownCellProps & react.RefAttributes<HTMLSpanElement>>;
1025
+
1026
+ export { BarCell, type BarCellProps, CategoryLegend, type CategoryLegendProps, ColumnPicker, type ColumnPickerProps, ColumnsCell, type ColumnsCellProps, type CsvColumn, DATA_TABLE_BREAKPOINT_THRESHOLDS, DataTableWithRef as DataTable, type DataTableBarVisual, type DataTableBreakpoint, DataTableCard, type DataTableCardField, DataTableCardList, type DataTableCardProps, type DataTableCellVisual, type DataTableColorBy, type DataTableColumnMeta, type DataTableColumnScale, type DataTableColumnsVisual, type DataTableHeatmapScale, type DataTableHeatmapVisual, type DataTableMarkdownOptions, type DataTableProps, DataTableRankCell, DataTableRankHeader, type DataTableRowClickHandler, type DataTableServerArgs, type DataTableShowAt, type DataTableSparklineVisual, type DataTableStickyRowPosition, type DataTableStickyRows, type DataTableValueFormatSpec, type DataTableViewState, type DownloadCsvOptions, FacetFilter, type FacetFilterProps, type FacetOption, FilterBar, type FilterBarProps, FilterChip, type FilterChipProps, HeatmapCell, type HeatmapCellProps, HeatmapLegend, type HeatmapLegendProps, MarkdownCell, type MarkdownCellProps, type NumericExtent, SearchInput, type SearchInputProps, SparklineCell, type SparklineCellProps, type ToCsvOptions, barDomain, barGeometry, columnSizeStyle, computeColumnScales, computeRowRanks, createSelectionColumn, downloadCsv, extentOf, formatCellValue, heatmapCellStyle, heatmapColorScaleSpec, isSafeCellUrl, parseCellMarkdown, resolveShowAt, seriesValues, stickyRowPinning, tableBreakpointForWidth, toCsv, useTableBreakpoint };