@stealthscale/component-tables 0.1.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/LICENSE +21 -0
- package/README.md +520 -0
- package/dist/cyclonedx/bom.json +1 -0
- package/dist/index.d.ts +867 -0
- package/dist/index.js +6001 -0
- package/dist/recipe-DKCE3olL.js +488 -0
- package/dist/rolldown-runtime-D7D4PA-g.js +13 -0
- package/dist/theme.d.ts +11 -0
- package/dist/theme.js +16 -0
- package/package.json +91 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Stealth Scale B.V.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,520 @@
|
|
|
1
|
+
# @stealthscale/component-tables
|
|
2
|
+
|
|
3
|
+
A data table in this package is a table of records over TanStack Table 9, rendered with the
|
|
4
|
+
collections table's parts and looks. An application's compiler reads the recipes from the preset
|
|
5
|
+
under `./theme`.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pnpm add @stealthscale/component-tables
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
The package peers on `react`, `react-dom`, `@stealthscale/hooks`, `@stealthscale/theme`,
|
|
14
|
+
`@stealthscale/provider-locale`, `@stealthscale/component-a11y` and
|
|
15
|
+
`@stealthscale/component-primitives`. It depends on `@tanstack/react-table` 9.2.4,
|
|
16
|
+
`@tanstack/react-virtual` 3.14.13 and the collections, data, forms, navigation, actions, disclosure
|
|
17
|
+
and feedback component packages.
|
|
18
|
+
|
|
19
|
+
| Export | What it renders |
|
|
20
|
+
| ----------- | ----------------------------------------------------------------------- |
|
|
21
|
+
| `DataTable` | A table of records, the hook that creates its state, and column helpers |
|
|
22
|
+
|
|
23
|
+
## DataTable
|
|
24
|
+
|
|
25
|
+
`DataTable.useDataTable` creates a table from columns written with `DataTable.createColumnHelper`.
|
|
26
|
+
`DataTable.Root` provides the table to the parts, and `DataTable.Table` renders it.
|
|
27
|
+
|
|
28
|
+
```tsx
|
|
29
|
+
import { ArrowUpIcon } from "lucide-react";
|
|
30
|
+
|
|
31
|
+
import { DataTable } from "@stealthscale/component-tables";
|
|
32
|
+
|
|
33
|
+
const column = DataTable.createColumnHelper<Payout>();
|
|
34
|
+
|
|
35
|
+
const columns = column.columns([
|
|
36
|
+
column.accessor("account", { header: "Account", meta: { rowHeader: true } }),
|
|
37
|
+
column.accessor("amount", { header: "Amount", meta: { numeric: true } }),
|
|
38
|
+
]);
|
|
39
|
+
|
|
40
|
+
const table = DataTable.useDataTable({ columns, data, getRowId: (row) => row.id });
|
|
41
|
+
|
|
42
|
+
<DataTable.Root table={table}>
|
|
43
|
+
<DataTable.Table
|
|
44
|
+
caption="Payouts this quarter"
|
|
45
|
+
sortIndicator={<ArrowUpIcon size="1em" />}
|
|
46
|
+
variant="surface"
|
|
47
|
+
/>
|
|
48
|
+
</DataTable.Root>;
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### The hook
|
|
52
|
+
|
|
53
|
+
`useDataTable` takes TanStack's table options without `features`. The kit registers all 17 stock
|
|
54
|
+
features of TanStack Table 9 with their row models, and every stock sort, filter and aggregation
|
|
55
|
+
function TanStack exports: 6, 22 and 11. A column's `sortFn`, `filterFn` and `aggregationFn` take a
|
|
56
|
+
stock function's name, such as `"greaterThan"` or `"sum"`. A stated option applies over the kit's
|
|
57
|
+
defaults:
|
|
58
|
+
|
|
59
|
+
| Option | Default | Why |
|
|
60
|
+
| ------------------------ | ---------------------------------------------- | ------------------------------------------------------------ |
|
|
61
|
+
| `sortDescFirst` | `false` | A column sorts ascending on its first press |
|
|
62
|
+
| `enableColumnResizing` | `false` | A resizable column's separator is a tab stop |
|
|
63
|
+
| `enableGrouping` | `false` | A column's menu offers grouping only where the caller asks |
|
|
64
|
+
| `columnResizeMode` | `"onChange"` | A column resizes while the pointer drags |
|
|
65
|
+
| `columnResizeDirection` | the `LocaleProvider`'s direction, else `"ltr"` | The drag, the resize keys and the row arrows follow the text |
|
|
66
|
+
| `autoResetCellSelection` | `false` | A grid keeps its selection when an edit changes `data` |
|
|
67
|
+
| page size | `Infinity` | A table shows every row until the caller states a page size |
|
|
68
|
+
|
|
69
|
+
The hook renders the calling component again on every change of the table's state and returns a new
|
|
70
|
+
table each time. Pass that table to `DataTable.Root`. Every other option is TanStack's own:
|
|
71
|
+
controlled `state` with its handlers, `initialState`, `getRowId`, `getSubRows`,
|
|
72
|
+
`enableRowSelection`, `getRowCanExpand`, `enableRowPinning`, `keepPinnedRows`, `manualPagination`,
|
|
73
|
+
`rowCount` and the rest. A grouping stated in `state` or `initialState` groups the rows whatever
|
|
74
|
+
`enableGrouping` is. The package exports TanStack's `SortingState`, `PaginationState`,
|
|
75
|
+
`ColumnFiltersState`, `ExpandedState` and `GroupingState` for a caller that keeps the state.
|
|
76
|
+
|
|
77
|
+
### Parts
|
|
78
|
+
|
|
79
|
+
| Part | Element | What it renders |
|
|
80
|
+
| --------------- | ------- | ----------------------------------------------------------------------------- |
|
|
81
|
+
| `Root` | `div` | A flex column that stacks the table and its controls, and provides the table |
|
|
82
|
+
| `Table` | `div` | The collections table's scroller around the table, its header, body, footer |
|
|
83
|
+
| `Search` | `div` | The forms `SearchInput` on TanStack's global filter |
|
|
84
|
+
| `ColumnFilter` | `div` | A filter button in a column's header and its panel |
|
|
85
|
+
| `ColumnMenu` | `div` | A menu button in a column's header with the sort, group, pin and hide actions |
|
|
86
|
+
| `ColumnManager` | `div` | The columns a person can hide, in a list they reorder, and a reset button |
|
|
87
|
+
| `Pagination` | `nav` | The navigation `Pagination` on TanStack's page state |
|
|
88
|
+
| `PageSize` | `div` | The forms `NativeSelect` of page sizes, after its label |
|
|
89
|
+
|
|
90
|
+
`DataTable.Table` takes the collections table's axes (`variant`, `size`, `rules`, `striped`,
|
|
91
|
+
`radius`, `palette`, `stickyHeader`, `stickyColumn`, `align`, `banded`, `interactive` and `layout`)
|
|
92
|
+
and passes them to its scroller. It also takes these props, each with English words by default:
|
|
93
|
+
|
|
94
|
+
| Prop | Default | What it sets |
|
|
95
|
+
| ---------------------- | --------------------- | ---------------------------------------------------------------------------- |
|
|
96
|
+
| `caption` | none | The caption under the table, which names the table and its region |
|
|
97
|
+
| `columnActions` | none | The controls a column's header renders beside its name, per column |
|
|
98
|
+
| `empty` | "No rows" | The content of the full-width row a table without rows renders |
|
|
99
|
+
| `sortIndicator` | none | The glyph beside a sortable column's name, pointing up |
|
|
100
|
+
| `sortedAnnouncement` | "Sorted by Amount, …" | The announcement of a sort, from the column's name and the direction |
|
|
101
|
+
| `unsortedAnnouncement` | "Not sorted" | The announcement of a table sorted by no column |
|
|
102
|
+
| `filteredAnnouncement` | "12 of 40 rows" | The announcement of the rows that match the filters, from both counts |
|
|
103
|
+
| `resizeLabel` | "Resize Amount" | The name of a column's resize separator |
|
|
104
|
+
| `resizeValue` | "150 pixels" | A column's size in words, the separator's `aria-valuetext` |
|
|
105
|
+
| `expandIndicator` | none | The glyph in a row's toggle, pointing to the inline end |
|
|
106
|
+
| `expandLabel` | "Expand" | The name of the toggle of a closed row |
|
|
107
|
+
| `collapseLabel` | "Collapse" | The name of the toggle of an open row |
|
|
108
|
+
| `loadingLabel` | "Loading…" | The words under an open row whose sub-rows have not arrived |
|
|
109
|
+
| `windowed` | `false` | Whether the body renders only the rows in or near the viewport |
|
|
110
|
+
| `estimateSize` | 40 | The height in pixels a windowed table takes for a row before measuring |
|
|
111
|
+
| `overscan` | 8 | The rows a windowed table renders beyond each end of the viewport |
|
|
112
|
+
| `onEndReached` | none | Runs when a windowed table's last row comes within the overscan |
|
|
113
|
+
| `grid` | `false` | Whether the table is a `grid` of cells a person selects and copies |
|
|
114
|
+
| `onCellEdit` | none | Receives each edit a person saves in a grid cell |
|
|
115
|
+
| `editLabel` | "Edit Amount" | The name of a cell's editor, from its column's name |
|
|
116
|
+
| `unsaved` | none | Whether a grid cell has a change the caller has not saved, by row and column |
|
|
117
|
+
| `unsavedLabel` | "Unsaved change" | The words a cell with an unsaved change renders for assistive technology |
|
|
118
|
+
|
|
119
|
+
### Column meta
|
|
120
|
+
|
|
121
|
+
A column states these fields in `meta`:
|
|
122
|
+
|
|
123
|
+
| Field | What it sets |
|
|
124
|
+
| ----------- | ---------------------------------------------------------------------------------- |
|
|
125
|
+
| `numeric` | The cells and the header align to the end, the cells in tabular figures |
|
|
126
|
+
| `rowHeader` | The cells render as row headers, the `th` a screen reader names each row by |
|
|
127
|
+
| `label` | The column's name in words, which the announcements and the controls read |
|
|
128
|
+
| `filter` | The filter `ColumnFilter` offers: `text`, `select` or `range` |
|
|
129
|
+
| `fit` | The column is as narrow as its content while the table lays out columns by content |
|
|
130
|
+
| `detail` | The detail row under an expanded row. `expandColumn` sets it |
|
|
131
|
+
| `selects` | The column's cells select their rows, and each row states `aria-selected` |
|
|
132
|
+
| `edit` | How a grid edits the column's cells. A column without it is read-only |
|
|
133
|
+
|
|
134
|
+
### Column helpers
|
|
135
|
+
|
|
136
|
+
| Helper | Column id | What its cells render |
|
|
137
|
+
| -------------- | --------- | --------------------------------------------------------------------------- |
|
|
138
|
+
| `selectColumn` | `select` | A checkbox per row, named by `label(record)`, and one in the header for all |
|
|
139
|
+
| `expandColumn` | `expand` | A button per row that opens a detail row under it, named by `label(record)` |
|
|
140
|
+
| `pinColumn` | `pin` | A toggle per row that pins it to the top, or to the bottom with `position` |
|
|
141
|
+
|
|
142
|
+
Each helper's column neither sorts, filters, hides, groups nor resizes. A grid's cell selection
|
|
143
|
+
leaves its cells out, and a table laid out by its content fits the column to its controls. In a
|
|
144
|
+
table with pinned or resizable columns the select column is 60 pixels wide, and the expand and pin
|
|
145
|
+
columns 72 pixels. A theme or density that widens the cells spreads the definition with a larger
|
|
146
|
+
`size`, `minSize` and `maxSize`. A pin toggle's row moves to another region on a press, and focus
|
|
147
|
+
moves with it to the toggle at its new place. The negative block margin of a detail button, a pin
|
|
148
|
+
toggle and a row's toggle keeps a row with a button as tall as a row of text. A box one line tall
|
|
149
|
+
centres each checkbox and keeps a row or a header with a checkbox as tall as one with text alone.
|
|
150
|
+
|
|
151
|
+
A row's box is checked while the row or every row under it is selected, and partly checked with
|
|
152
|
+
`indeterminateIndicator` while some rows under it are. A row with sub-rows opens them through its
|
|
153
|
+
toggle, so `expandColumn` renders no detail button on it.
|
|
154
|
+
|
|
155
|
+
### Sizes and pinned columns
|
|
156
|
+
|
|
157
|
+
A table with `enableColumnResizing` or a pinned column lays out every column at its TanStack `size`,
|
|
158
|
+
in a fixed layout as wide as the sizes together, inside a scroller as wide as the table and never
|
|
159
|
+
wider than its room. A column pinned with `columnPinning` sticks to the start or the end of the
|
|
160
|
+
scroll region, on the panel under its row's fill. The last column of a region rules its side next to
|
|
161
|
+
the scrolling columns.
|
|
162
|
+
|
|
163
|
+
A resizable column's header ends in a separator:
|
|
164
|
+
|
|
165
|
+
- A pointer drags it, and a double click restores the size the column states.
|
|
166
|
+
- ArrowLeft and ArrowRight narrow and widen the column by 16 pixels, mirrored under right-to-left.
|
|
167
|
+
- Home and End set the column's `minSize` and `maxSize`. End changes nothing on a column that states
|
|
168
|
+
no maximum.
|
|
169
|
+
- Enter restores the size the column states.
|
|
170
|
+
|
|
171
|
+
### Pinned rows, spans and totals
|
|
172
|
+
|
|
173
|
+
- A row pinned with `rowPinning` renders at the top or the bottom of the body whatever the sort, the
|
|
174
|
+
filters or the page. The last row of a region another region follows rules its end at the header
|
|
175
|
+
rule's width, the detail row where that row is expanded.
|
|
176
|
+
- A column's `spanRows: true` merges adjacent cells of equal value into one cell that spans their
|
|
177
|
+
rows, and `spanColumns` spans a cell across columns. The body renders the spans and skips the
|
|
178
|
+
cells they cover. A cell whose span ends on the body's last row drops its rule before a footer, as
|
|
179
|
+
the last row's cells do.
|
|
180
|
+
- A column's `footer` renders in a footer row. A total is TanStack's aggregation: the column states
|
|
181
|
+
`aggregationFn: "sum"`, and its footer renders `column.getAggregationValue()`, which follows the
|
|
182
|
+
filters. A row-header column without a footer renders an empty data cell in the footer row.
|
|
183
|
+
|
|
184
|
+
### Pivots
|
|
185
|
+
|
|
186
|
+
`DataTable.pivot` turns flat records into the columns, rows and row ids of a cross-tab, which
|
|
187
|
+
`useDataTable` takes:
|
|
188
|
+
|
|
189
|
+
```tsx
|
|
190
|
+
const table = DataTable.useDataTable(
|
|
191
|
+
DataTable.pivot(orders, {
|
|
192
|
+
column: "quarter",
|
|
193
|
+
format: { currency: "EUR", style: "currency" },
|
|
194
|
+
headers: { product: "Product", region: "Region" },
|
|
195
|
+
rows: ["region", "product"],
|
|
196
|
+
value: "value",
|
|
197
|
+
}),
|
|
198
|
+
);
|
|
199
|
+
|
|
200
|
+
<DataTable.Root table={table}>
|
|
201
|
+
<DataTable.Table align="start" caption="Order value by region and product, per quarter" />
|
|
202
|
+
</DataTable.Root>;
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
| Option | Default | What it sets |
|
|
206
|
+
| -------------- | ---------- | -------------------------------------------------------------------------- |
|
|
207
|
+
| `rows` | required | The fields whose values become the rows, the outermost first |
|
|
208
|
+
| `column` | required | The field whose values become the columns, in the order they first appear |
|
|
209
|
+
| `value` | none | The field the built-in aggregates measure. `count` reads none |
|
|
210
|
+
| `aggregate` | `"sum"` | `"sum"`, `"average"`, `"min"`, `"max"`, `"count"` or a function of records |
|
|
211
|
+
| `format` | none | The `Intl.NumberFormat` options every figure is written with |
|
|
212
|
+
| `headers` | the field | Each row dimension's header, by field |
|
|
213
|
+
| `totals` | `true` | Whether the table renders a totals column and a footer row |
|
|
214
|
+
| `totalLabel` | "Total" | The totals column's header and the footer row's name |
|
|
215
|
+
| `missingLabel` | "No value" | The words assistive technology reads in a cell with nothing to measure |
|
|
216
|
+
|
|
217
|
+
- A cell aggregates the records of its row path and its column value. Every total is aggregated from
|
|
218
|
+
the records, never from the cells. An average's total is the average of its records, which differs
|
|
219
|
+
from the mean of the cells beside it when the cells aggregate different numbers of records.
|
|
220
|
+
- A cell without records, or whose records contain no finite figure, is empty. Assistive technology
|
|
221
|
+
reads `missingLabel` in it, so it never reads as zero.
|
|
222
|
+
- The rows follow each dimension's first-seen order, level by level, so the rows of a group are
|
|
223
|
+
adjacent, and an outer dimension's cell spans them. Every dimension's cell is a row header.
|
|
224
|
+
`align="start"` puts an outer name on the line of its group's first row.
|
|
225
|
+
- A figure renders through the data package's `Format.Number`, in the locale in scope.
|
|
226
|
+
- The column ids are the dimensions' fields, `quarter:Q1` for a value's column and `total` for the
|
|
227
|
+
totals column. A row's id is its path in JSON.
|
|
228
|
+
- The columns neither sort, group, filter nor hide. Filter the records before the pivot, because a
|
|
229
|
+
total counts every record. A dimension's value is the record's text, so translate the records
|
|
230
|
+
before the pivot.
|
|
231
|
+
|
|
232
|
+
### Rows with levels
|
|
233
|
+
|
|
234
|
+
A table that states `getSubRows` or groups its rows by a column renders as a `treegrid`. Each record
|
|
235
|
+
row states its `aria-level`, its `aria-posinset` and `aria-setsize` among its siblings, and
|
|
236
|
+
`aria-expanded` while it opens rows under it.
|
|
237
|
+
|
|
238
|
+
```tsx
|
|
239
|
+
const table = DataTable.useDataTable({
|
|
240
|
+
columns,
|
|
241
|
+
data: budgets,
|
|
242
|
+
getRowId: (budget) => budget.centre,
|
|
243
|
+
getSubRows: (budget) => budget.teams,
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
<DataTable.Table
|
|
247
|
+
caption="Budgets this quarter"
|
|
248
|
+
expandIndicator={<ChevronRightIcon size="1em" />}
|
|
249
|
+
/>;
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
- The tree column is the first visible row-header column, else the first visible column with an
|
|
253
|
+
accessor. Its cells indent `spacing.6` a level below the grouping and lead with the row's toggle,
|
|
254
|
+
or with an empty box of the toggle's width on a row that opens nothing.
|
|
255
|
+
- A column in `grouping` groups the rows by its value. Each group is a row whose grouped cell
|
|
256
|
+
renders the toggle, the value and the number of records in the group, "(12)". That cell is the
|
|
257
|
+
group row's row header. An aggregated cell renders the column's `aggregatedCell`, which TanStack
|
|
258
|
+
writes as a string unless the column states one, so a column that formats figures states
|
|
259
|
+
`aggregatedCell` as it states `cell`. Every other cell of a group row is empty. TanStack moves the
|
|
260
|
+
grouped columns to the start.
|
|
261
|
+
- A row that `getRowCanExpand` lets open before its sub-rows exist renders a row with the feedback
|
|
262
|
+
`Loader` and `loadingLabel` under it while it is open without them. Load the sub-rows in
|
|
263
|
+
`onExpandedChange`, pass them back in new `data`, and state `autoResetExpanded: false`, or
|
|
264
|
+
TanStack closes every row when the data changes.
|
|
265
|
+
- A row renders its detail only while it has no sub-rows.
|
|
266
|
+
- Selecting a row selects the rows under it, which is TanStack's `enableSubRowSelection`.
|
|
267
|
+
- A windowed table keeps the row with the tab stop rendered while it scrolls out of view, and
|
|
268
|
+
renders a row out of view before a key focuses it.
|
|
269
|
+
|
|
270
|
+
`DataTable.ColumnMenu` offers "Group by column" on a column that can group while the table states
|
|
271
|
+
`enableGrouping`, and "Ungroup" on a column that groups the rows.
|
|
272
|
+
|
|
273
|
+
### Grids
|
|
274
|
+
|
|
275
|
+
`grid` makes a table a `grid` of cells over TanStack's cell selection, as in a spreadsheet. A table
|
|
276
|
+
with levels ignores it and remains a `treegrid`.
|
|
277
|
+
|
|
278
|
+
```tsx
|
|
279
|
+
<DataTable.Table caption="Operating budget, by department and quarter" grid />
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
- One cell is in the tab order: TanStack's focused cell, the anchor of the last range, else the
|
|
283
|
+
first cell of the first row. Tab moves focus out of the grid.
|
|
284
|
+
- The arrow keys move the focused cell, left and right swapped under right-to-left. Home and End go
|
|
285
|
+
to the row's first and last cell, Control or Meta with Home or End to the grid's first and last
|
|
286
|
+
cell, and Page Up and Page Down move ten rows.
|
|
287
|
+
- Shift with an arrow extends the range from the focused cell and scrolls the range's moving corner
|
|
288
|
+
into view. Control or Meta with A selects every cell, and Escape collapses the range to the
|
|
289
|
+
focused cell. With `enableCellRangeSelection: false`, Shift with an arrow moves the focused cell.
|
|
290
|
+
- A press selects a cell, a drag selects the cells it crosses, Shift with a press extends the range,
|
|
291
|
+
and Control or Meta with a press adds a range. These are TanStack's handlers. A press starts no
|
|
292
|
+
text selection.
|
|
293
|
+
- A copy while a cell of the grid has focus writes the selection as tab-separated text: a tab
|
|
294
|
+
between values, a line break between rows and a blank line between ranges, from TanStack's raw
|
|
295
|
+
values. A text value that opens with `=`, `+`, `-` or `@` takes a leading quote, so a spreadsheet
|
|
296
|
+
does not run it as a formula. A value with a tab, a line break or a quote is quoted.
|
|
297
|
+
- A selected cell fills with the palette's subtle role, and a line in the palette's solid runs along
|
|
298
|
+
each range's outer edges.
|
|
299
|
+
|
|
300
|
+
### Editing cells
|
|
301
|
+
|
|
302
|
+
A column of a grid that states `meta.edit` edits its cells in place. `onCellEdit` receives each
|
|
303
|
+
value a person saves, and the caller writes it into `data`:
|
|
304
|
+
|
|
305
|
+
```tsx
|
|
306
|
+
const columns = column.columns([
|
|
307
|
+
column.accessor("id", { header: "Claim", meta: { rowHeader: true } }),
|
|
308
|
+
column.accessor("description", {
|
|
309
|
+
header: "Description",
|
|
310
|
+
meta: {
|
|
311
|
+
edit: { validate: (text) => (text.trim() === "" ? "Enter a description" : undefined) },
|
|
312
|
+
},
|
|
313
|
+
}),
|
|
314
|
+
]);
|
|
315
|
+
|
|
316
|
+
<DataTable.Table
|
|
317
|
+
caption="Expense claims"
|
|
318
|
+
grid
|
|
319
|
+
onCellEdit={({ rowId, value }) => {
|
|
320
|
+
setClaims((claims) =>
|
|
321
|
+
claims.map((claim) => (claim.id === rowId ? { ...claim, description: value } : claim)),
|
|
322
|
+
);
|
|
323
|
+
}}
|
|
324
|
+
/>;
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
- Enter, F2 and a double click open the editor with the cell's value and the caret after it. A
|
|
328
|
+
character opens it with the character in place of the value, and Backspace and Delete open it
|
|
329
|
+
empty.
|
|
330
|
+
- Tab saves and moves to the next cell, Shift with Tab to the previous one, and Escape drops the
|
|
331
|
+
edit. In the text field Enter saves and moves down. An arrow moves the caret in an editor that
|
|
332
|
+
Enter, F2 or a double click opened, and saves and moves in one that a character, Backspace or
|
|
333
|
+
Delete opened. Focus leaving the editor saves it.
|
|
334
|
+
- After a key saves or drops the edit, focus returns to the grid's focused cell. Focus a person
|
|
335
|
+
moves to another element remains there.
|
|
336
|
+
- Saving runs `validate(text, record)`. A reason keeps the editor open, with the reason under the
|
|
337
|
+
cell, or above it in a body's last row, as the control's description. A valid text that differs
|
|
338
|
+
from the cell's value goes to `onCellEdit` as `{ columnId, record, rowId, value }`. The value is
|
|
339
|
+
text, which the caller parses.
|
|
340
|
+
- The editor covers the cell at the cell's size, with its text where the cell's text starts. Its
|
|
341
|
+
control is a forms `Input` at the table's size unless the column states `editor`.
|
|
342
|
+
- In a grid with an editable column, every cell of another column states `aria-readonly`. The
|
|
343
|
+
editor's control is named by `editLabel`: "Edit Description".
|
|
344
|
+
- `unsaved(rowId, columnId)` marks each cell whose change the caller has not saved: a bar in the
|
|
345
|
+
warning palette's solid at the cell's inline start, and `unsavedLabel` after the cell's content
|
|
346
|
+
for assistive technology.
|
|
347
|
+
|
|
348
|
+
`meta.edit.editor` renders another control from `DataTable.EditorProps`: the draft in `value`,
|
|
349
|
+
`setValue`, `commit`, `cancel`, the `id` that takes focus when the editor opens, `aria-label`,
|
|
350
|
+
`aria-invalid`, `aria-describedby` and the table's `size`.
|
|
351
|
+
|
|
352
|
+
```tsx
|
|
353
|
+
column.accessor("category", {
|
|
354
|
+
header: "Category",
|
|
355
|
+
meta: {
|
|
356
|
+
edit: {
|
|
357
|
+
editor: (props) => (
|
|
358
|
+
<NativeSelect.Root size={props.size}>
|
|
359
|
+
<NativeSelect.Field
|
|
360
|
+
aria-label={props["aria-label"]}
|
|
361
|
+
id={props.id}
|
|
362
|
+
onChange={(event) => {
|
|
363
|
+
props.commit(event.currentTarget.value);
|
|
364
|
+
}}
|
|
365
|
+
value={props.value}
|
|
366
|
+
>
|
|
367
|
+
{CATEGORIES.map((category) => (
|
|
368
|
+
<option key={category}>{category}</option>
|
|
369
|
+
))}
|
|
370
|
+
</NativeSelect.Field>
|
|
371
|
+
</NativeSelect.Root>
|
|
372
|
+
),
|
|
373
|
+
},
|
|
374
|
+
},
|
|
375
|
+
});
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
- A control that knows its value when a person picks it passes the value to `commit`.
|
|
379
|
+
- A control that handles Enter, Tab or Escape itself cancels the key's default, and the editor
|
|
380
|
+
leaves the key to it.
|
|
381
|
+
- Focus that moves into a popup a control inside the editor names in `aria-controls` keeps the
|
|
382
|
+
editor open.
|
|
383
|
+
- The editor renders the control as a component, so the control may call hooks.
|
|
384
|
+
|
|
385
|
+
The `DataTable` namespace exports the `CellEdit`, `EditorProps` and `CellEditEvent` types.
|
|
386
|
+
|
|
387
|
+
### Search and column filters
|
|
388
|
+
|
|
389
|
+
`DataTable.Search` filters the rows by any value through TanStack's global filter. It takes the
|
|
390
|
+
forms `SearchInput`'s props, so Escape clears it, and its name through `aria-label`. The table
|
|
391
|
+
announces the count of matching rows 500ms after the last change of the search or a filter.
|
|
392
|
+
|
|
393
|
+
`columnActions` returns the controls a column's header renders beside its name. They follow the
|
|
394
|
+
name, and precede it in a numeric column. A header over two or more columns renders none.
|
|
395
|
+
|
|
396
|
+
```tsx
|
|
397
|
+
<DataTable.Table
|
|
398
|
+
caption="Recent transfers"
|
|
399
|
+
columnActions={(column) => (
|
|
400
|
+
<DataTable.ColumnFilter
|
|
401
|
+
checkIndicator={<CheckIcon />}
|
|
402
|
+
column={column}
|
|
403
|
+
indicator={<ListFilterIcon size="1em" />}
|
|
404
|
+
/>
|
|
405
|
+
)}
|
|
406
|
+
/>
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
`DataTable.ColumnFilter` renders a button for a column that states `meta.filter`. The button opens a
|
|
410
|
+
popover at size `sm` under the header's start, or its end in a numeric column. The panel is
|
|
411
|
+
`sizes.60` wide and named by its title, "Filter Amount", with the filter at size `sm` and a Clear
|
|
412
|
+
button at its end:
|
|
413
|
+
|
|
414
|
+
| `meta.filter` | Fields | Filter value | `filterFn` |
|
|
415
|
+
| ------------- | --------------------------------------------- | ---------------------------- | --------------------------------------- |
|
|
416
|
+
| `text` | A field the values contain | A string | `includesString`, TanStack's for text |
|
|
417
|
+
| `select` | A box per value, its count of rows at its end | The values checked | `"arrHas"`, which the column states |
|
|
418
|
+
| `range` | A minimum and a maximum, extremes as hints | Two figures, open when empty | `inNumberRange`, TanStack's for figures |
|
|
419
|
+
|
|
420
|
+
The values and the extremes come from TanStack's faceting: the rows the other filters and the search
|
|
421
|
+
leave. A checked value no row has any more remains in the list with a count of zero. While the
|
|
422
|
+
column filters, the button renders the button's on state and is named "Filter Amount, active".
|
|
423
|
+
|
|
424
|
+
### Column menus and the column manager
|
|
425
|
+
|
|
426
|
+
`DataTable.ColumnMenu` in `columnActions` renders a menu button named "Options for Amount". The menu
|
|
427
|
+
opens under the header's start, or its end in a numeric column, and lists only the actions that
|
|
428
|
+
change the column:
|
|
429
|
+
|
|
430
|
+
- The sort directions the column is not sorted in, and Clear sort while it is sorted.
|
|
431
|
+
- Group by column while the column can group, and Ungroup while it groups the rows.
|
|
432
|
+
- The regions the column is not pinned to, and Unpin while it is pinned.
|
|
433
|
+
- Hide column, while another column a person can hide is visible.
|
|
434
|
+
|
|
435
|
+
`actionIndicators` gives each action's row a leading glyph, by the action: `ascending`,
|
|
436
|
+
`descending`, `unsorted`, `grouped`, `ungrouped`, `start`, `end`, `unpinned` and `hide`. A hide
|
|
437
|
+
applies after the menu closes, and focus moves to the menu button of the column that takes the
|
|
438
|
+
hidden column's place.
|
|
439
|
+
|
|
440
|
+
`DataTable.ColumnManager` lists the columns a person can hide in the collections `Sortable`, in its
|
|
441
|
+
plain look. A row's box shows or hides its column, and the last visible column's box is disabled. A
|
|
442
|
+
person moves a column by its handle, with a pointer or with Space and the arrow keys, and each move
|
|
443
|
+
is announced. Reset, at the manager's end, restores the table's initial order and visibility. The
|
|
444
|
+
caller places the manager, such as in a disclosure `Popover`. The manager writes `columnOrder` and
|
|
445
|
+
`columnVisibility`.
|
|
446
|
+
|
|
447
|
+
### Pages
|
|
448
|
+
|
|
449
|
+
A table pages its rows once the caller states a page size in `initialState.pagination`.
|
|
450
|
+
`DataTable.Pagination` is the navigation `Pagination` root on TanStack's page state: the caller
|
|
451
|
+
composes its triggers, pages and page text inside it. A table that shows every row counts one page.
|
|
452
|
+
TanStack returns the table to its first page after a change of the filters or the sort.
|
|
453
|
+
|
|
454
|
+
`DataTable.PageSize` sets the page size from `sizes`, one of which is the table's page size. With
|
|
455
|
+
`label` it renders the words in a `label` before the select. Without `label`, the label of a `Field`
|
|
456
|
+
around the part is the select's accessible name.
|
|
457
|
+
|
|
458
|
+
```tsx
|
|
459
|
+
<DataTable.PageSize label="Rows per page" sizes={[10, 20, 50]} />
|
|
460
|
+
<DataTable.Pagination>
|
|
461
|
+
<Pagination.PrevTrigger label="Previous page">
|
|
462
|
+
<ChevronLeftIcon />
|
|
463
|
+
</Pagination.PrevTrigger>
|
|
464
|
+
<Pagination.Items />
|
|
465
|
+
<Pagination.NextTrigger label="Next page">
|
|
466
|
+
<ChevronRightIcon />
|
|
467
|
+
</Pagination.NextTrigger>
|
|
468
|
+
</DataTable.Pagination>
|
|
469
|
+
```
|
|
470
|
+
|
|
471
|
+
A server that sorts, filters and pages the rows takes `manualSorting`, `manualFiltering` and
|
|
472
|
+
`manualPagination`, controlled `state` for the sort, the search and the page, and `rowCount` from
|
|
473
|
+
the server, from which the pagination counts the pages. Pass `aria-busy` to `DataTable.Table` while
|
|
474
|
+
a page loads.
|
|
475
|
+
|
|
476
|
+
### Accessibility
|
|
477
|
+
|
|
478
|
+
- A sortable column's name is a `button` inside its `th`. The `th` states `aria-sort` only while its
|
|
479
|
+
column is sorted, the rule of the WAI-ARIA sortable table example. The table announces each change
|
|
480
|
+
of the sort politely. It announces nothing on the first render.
|
|
481
|
+
- The sort glyph is hidden from assistive technology. The recipe turns it half a circle for a
|
|
482
|
+
descending sort. On a column that is not sorted the glyph shows faintly while the pointer is over
|
|
483
|
+
the button or the button has keyboard focus. In a column of figures the glyph comes before the
|
|
484
|
+
name, so the name ends where the figures end.
|
|
485
|
+
- A header over two or more columns takes `scope="colgroup"`. A column outside every group renders
|
|
486
|
+
one header cell across every header row.
|
|
487
|
+
- A resize separator is an `hr` in the tab order, the `separator` role of the WAI-ARIA window
|
|
488
|
+
splitter pattern, named "Resize" and the column's name, with the size in pixels as its value. A
|
|
489
|
+
header with a separator or column actions is named by the element that contains its name through
|
|
490
|
+
`aria-labelledby`, so the separator's value and the actions' names are not part of the header's
|
|
491
|
+
name.
|
|
492
|
+
- Each checkbox, detail button and pin toggle is named by its row. A detail button states
|
|
493
|
+
`aria-expanded`, and `aria-controls` while its detail is open. A pin toggle states `aria-pressed`.
|
|
494
|
+
- Each filter and menu button is named by its column. A filter is a popover, because a menu contains
|
|
495
|
+
no fields.
|
|
496
|
+
- A windowed table states the count of every row in `aria-rowcount` and each rendered row's place in
|
|
497
|
+
`aria-rowindex`, the rule of the WAI-ARIA grid and table properties for a table with only some of
|
|
498
|
+
its rows in the document. Its spacer rows are hidden from assistive technology.
|
|
499
|
+
- In a `treegrid` one record row is in the tab order: the row last focused, else the first. With a
|
|
500
|
+
row focused, ArrowDown and ArrowUp move between the rows, Home and End go to the first and the
|
|
501
|
+
last, ArrowRight opens a closed row and then moves into it, ArrowLeft closes an open row and then
|
|
502
|
+
moves to its parent, and Enter and Space open and close it. The arrows swap under right-to-left. A
|
|
503
|
+
focused row rings its edge inside. Controls in the cells keep their own tab stops.
|
|
504
|
+
- A row's toggle is out of the tab order, because the row takes its keys. A press opens or closes
|
|
505
|
+
the row and focuses it. The table states `aria-multiselectable` while a column selects rows.
|
|
506
|
+
- In a `grid` one cell is in the tab order, and each cell a person can select states
|
|
507
|
+
`aria-selected`. The table states `aria-multiselectable` unless both of TanStack's range options
|
|
508
|
+
are off. The cell with keyboard focus rings its edge inside.
|
|
509
|
+
- The scroller of a `grid` or a `treegrid` takes no tab stop, because its cells or its rows take
|
|
510
|
+
focus and scroll into view. `focusable={true}` gives it one.
|
|
511
|
+
- The caption is the accessible name of the table, and of the scroller's region while the table
|
|
512
|
+
overflows.
|
|
513
|
+
- Under forced colours a selected row fills with `Highlight`, and its cells' text and rules take
|
|
514
|
+
`HighlightText`, pinned cells included. A selected grid cell fills the same way, and its line
|
|
515
|
+
takes `HighlightText`. An open editor takes the forced colours of a field, and an unsaved cell's
|
|
516
|
+
bar takes `CanvasText`. An active filter's button fills with `Highlight`.
|
|
517
|
+
|
|
518
|
+
## Licence
|
|
519
|
+
|
|
520
|
+
MIT
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"$schema":"http://cyclonedx.org/schema/bom-1.7.schema.json","bomFormat":"CycloneDX","specVersion":"1.7","version":1,"metadata":{"lifecycles":[{"phase":"build"}],"tools":{"components":[{"type":"application","name":"rolldown","version":"1.2.11"},{"type":"application","name":"vite","version":"8.3.1"},{"type":"library","name":"core","group":"@arethetypeswrong","version":"0.18.5","bom-ref":"pkg:npm/%40arethetypeswrong/core@0.18.5","author":"Andrew Branch","description":"The API behind arethetypeswrong.github.io","hashes":[{"alg":"SHA-512","content":"f72b63cc6c318e6f54cfb23d6af7dbb79be542deae93db914444b3263aabce797a58abc8e9dc184e8faf277534d96aeafe64778aa97f3f386f1e129d2480230d"}],"licenses":[{"license":{"id":"MIT"}}],"purl":"pkg:npm/%40arethetypeswrong/core@0.18.5","externalReferences":[{"url":"https://github.com/arethetypeswrong/arethetypeswrong.github.io.git#packages/core","type":"vcs","comment":"as detected from PackageJson property \"repository.url\" and \"repository.directory\""}],"properties":[{"name":"cdx:npm:package:constraint:engine:node","value":">=20"}]},{"type":"library","name":"component-a11y","group":"@stealthscale","version":"0.1.0","bom-ref":"pkg:npm/%40stealthscale/component-a11y@0.1.0","description":"Draws what a keyboard and a screen reader need, and an eye does not.","licenses":[{"license":{"id":"MIT"}}],"purl":"pkg:npm/%40stealthscale/component-a11y@0.1.0","externalReferences":[{"url":"https://github.com/stealth-scale/scale/issues","type":"issue-tracker","comment":"as detected from PackageJson property \"bugs\""},{"url":"git+https://github.com/stealth-scale/scale.git#components/a11y","type":"vcs","comment":"as detected from PackageJson property \"repository.url\" and \"repository.directory\""},{"url":"https://github.com/stealth-scale/scale/tree/main/components/a11y#readme","type":"website","comment":"as detected from PackageJson property \"homepage\""}],"properties":[{"name":"cdx:npm:package:constraint:engine:node","value":">=26.0.0"}]},{"type":"library","name":"component-primitives","group":"@stealthscale","version":"0.1.0","bom-ref":"pkg:npm/%40stealthscale/component-primitives@0.1.0","description":"Decides what is drawn and where, and draws nothing itself.","licenses":[{"license":{"id":"MIT"}}],"purl":"pkg:npm/%40stealthscale/component-primitives@0.1.0","externalReferences":[{"url":"https://github.com/stealth-scale/scale/issues","type":"issue-tracker","comment":"as detected from PackageJson property \"bugs\""},{"url":"git+https://github.com/stealth-scale/scale.git#components/primitives","type":"vcs","comment":"as detected from PackageJson property \"repository.url\" and \"repository.directory\""},{"url":"https://github.com/stealth-scale/scale/tree/main/components/primitives#readme","type":"website","comment":"as detected from PackageJson property \"homepage\""}],"properties":[{"name":"cdx:npm:package:constraint:engine:node","value":">=26.0.0"}]},{"type":"library","name":"hooks","group":"@stealthscale","version":"0.1.0","bom-ref":"pkg:npm/%40stealthscale/hooks@0.1.0","description":"The React hooks a component uses to read the page it draws into: how the page is read, what it measures, and what a value was on the previous render.","licenses":[{"license":{"id":"MIT"}}],"purl":"pkg:npm/%40stealthscale/hooks@0.1.0","externalReferences":[{"url":"https://github.com/stealth-scale/scale/issues","type":"issue-tracker","comment":"as detected from PackageJson property \"bugs\""},{"url":"git+https://github.com/stealth-scale/scale.git#foundations/hooks","type":"vcs","comment":"as detected from PackageJson property \"repository.url\" and \"repository.directory\""},{"url":"https://github.com/stealth-scale/scale/tree/main/foundations/hooks#readme","type":"website","comment":"as detected from PackageJson property \"homepage\""}],"properties":[{"name":"cdx:npm:package:constraint:engine:node","value":">=26.0.0"}]},{"type":"library","name":"provider-locale","group":"@stealthscale","version":"0.1.0","bom-ref":"pkg:npm/%40stealthscale/provider-locale@0.1.0","description":"Decides which locale a page is read in, remembers the choice, and writes the direction that follows.","licenses":[{"license":{"id":"MIT"}}],"purl":"pkg:npm/%40stealthscale/provider-locale@0.1.0","externalReferences":[{"url":"https://github.com/stealth-scale/scale/issues","type":"issue-tracker","comment":"as detected from PackageJson property \"bugs\""},{"url":"git+https://github.com/stealth-scale/scale.git#foundations/providers/locale","type":"vcs","comment":"as detected from PackageJson property \"repository.url\" and \"repository.directory\""},{"url":"https://github.com/stealth-scale/scale/tree/main/foundations/providers/locale#readme","type":"website","comment":"as detected from PackageJson property \"homepage\""}],"properties":[{"name":"cdx:npm:package:constraint:engine:node","value":">=26.0.0"}]},{"type":"library","name":"theme","group":"@stealthscale","version":"0.3.0","bom-ref":"pkg:npm/%40stealthscale/theme@0.3.0","description":"Publishes the foundation every recipe is written against, the runtime every component binds its recipe with, and the vocabulary a theme is written in.","licenses":[{"license":{"id":"MIT"}}],"purl":"pkg:npm/%40stealthscale/theme@0.3.0","externalReferences":[{"url":"https://github.com/stealth-scale/scale/issues","type":"issue-tracker","comment":"as detected from PackageJson property \"bugs\""},{"url":"git+https://github.com/stealth-scale/scale.git#foundations/theme","type":"vcs","comment":"as detected from PackageJson property \"repository.url\" and \"repository.directory\""},{"url":"https://github.com/stealth-scale/scale/tree/main/foundations/theme#readme","type":"website","comment":"as detected from PackageJson property \"homepage\""}],"properties":[{"name":"cdx:npm:package:constraint:engine:node","value":">=26.0.0"}]},{"type":"library","name":"vite-config-i18n","group":"@stealthscale","version":"0.1.0","bom-ref":"pkg:npm/%40stealthscale/vite-config-i18n@0.1.0","description":"Configures a package or an application with words: the catalogue plugin, and the words in scope for its specifications.","licenses":[{"license":{"id":"MIT"}}],"purl":"pkg:npm/%40stealthscale/vite-config-i18n@0.1.0","externalReferences":[{"url":"https://github.com/stealth-scale/scale/issues","type":"issue-tracker","comment":"as detected from PackageJson property \"bugs\""},{"url":"git+https://github.com/stealth-scale/scale.git#packages/vite-config-i18n","type":"vcs","comment":"as detected from PackageJson property \"repository.url\" and \"repository.directory\""},{"url":"https://github.com/stealth-scale/scale/tree/main/packages/vite-config-i18n#readme","type":"website","comment":"as detected from PackageJson property \"homepage\""}],"properties":[{"name":"cdx:npm:package:constraint:engine:node","value":">=26.0.0"}]},{"type":"library","name":"vite-config-react","group":"@stealthscale","version":"0.8.0","bom-ref":"pkg:npm/%40stealthscale/vite-config-react@0.8.0","description":"Adds the JSX transform, the React lint rules, a test document and an MDX compiler to a package that renders.","licenses":[{"license":{"id":"MIT"}}],"purl":"pkg:npm/%40stealthscale/vite-config-react@0.8.0","externalReferences":[{"url":"https://github.com/stealth-scale/scale/issues","type":"issue-tracker","comment":"as detected from PackageJson property \"bugs\""},{"url":"git+https://github.com/stealth-scale/scale.git#packages/vite-config-react","type":"vcs","comment":"as detected from PackageJson property \"repository.url\" and \"repository.directory\""},{"url":"https://github.com/stealth-scale/scale/tree/main/packages/vite-config-react#readme","type":"website","comment":"as detected from PackageJson property \"homepage\""}],"properties":[{"name":"cdx:npm:package:constraint:engine:node","value":">=26.0.0"}]},{"type":"library","name":"vite-config-specimen","group":"@stealthscale","version":"0.1.0","bom-ref":"pkg:npm/%40stealthscale/vite-config-specimen@0.1.0","description":"Configures an application that shows a catalogue, and the workspace root the specimens sit under.","licenses":[{"license":{"id":"MIT"}}],"purl":"pkg:npm/%40stealthscale/vite-config-specimen@0.1.0","externalReferences":[{"url":"https://github.com/stealth-scale/scale/issues","type":"issue-tracker","comment":"as detected from PackageJson property \"bugs\""},{"url":"git+https://github.com/stealth-scale/scale.git#packages/vite-config-specimen","type":"vcs","comment":"as detected from PackageJson property \"repository.url\" and \"repository.directory\""},{"url":"https://github.com/stealth-scale/scale/tree/main/packages/vite-config-specimen#readme","type":"website","comment":"as detected from PackageJson property \"homepage\""}],"properties":[{"name":"cdx:npm:package:constraint:engine:node","value":">=26.0.0"}]},{"type":"library","name":"vite-config-theme","group":"@stealthscale","version":"0.1.0","bom-ref":"pkg:npm/%40stealthscale/vite-config-theme@0.1.0","description":"Adds the runtime generator to a design-system package and the stylesheet compiler to an application.","licenses":[{"license":{"id":"MIT"}}],"purl":"pkg:npm/%40stealthscale/vite-config-theme@0.1.0","externalReferences":[{"url":"https://github.com/stealth-scale/scale/issues","type":"issue-tracker","comment":"as detected from PackageJson property \"bugs\""},{"url":"git+https://github.com/stealth-scale/scale.git#packages/vite-config-theme","type":"vcs","comment":"as detected from PackageJson property \"repository.url\" and \"repository.directory\""},{"url":"https://github.com/stealth-scale/scale/tree/main/packages/vite-config-theme#readme","type":"website","comment":"as detected from PackageJson property \"homepage\""}],"properties":[{"name":"cdx:npm:package:constraint:engine:node","value":">=26.0.0"}]},{"type":"library","name":"vite-config-typescript","group":"@stealthscale","version":"0.3.0","bom-ref":"pkg:npm/%40stealthscale/vite-config-typescript@0.3.0","description":"Sets the TypeScript options every stealthscale package compiles with.","licenses":[{"license":{"id":"MIT"}}],"purl":"pkg:npm/%40stealthscale/vite-config-typescript@0.3.0","externalReferences":[{"url":"https://github.com/stealth-scale/scale/issues","type":"issue-tracker","comment":"as detected from PackageJson property \"bugs\""},{"url":"git+https://github.com/stealth-scale/scale.git#packages/vite-config-typescript","type":"vcs","comment":"as detected from PackageJson property \"repository.url\" and \"repository.directory\""},{"url":"https://github.com/stealth-scale/scale/tree/main/packages/vite-config-typescript#readme","type":"website","comment":"as detected from PackageJson property \"homepage\""}],"properties":[{"name":"cdx:npm:package:constraint:engine:node","value":">=26.0.0"}]},{"type":"library","name":"vite-config","group":"@stealthscale","version":"0.6.0","bom-ref":"pkg:npm/%40stealthscale/vite-config@0.6.0","description":"Configures a package from five Vite+ tiers and a named layer for every block.","licenses":[{"license":{"id":"MIT"}}],"purl":"pkg:npm/%40stealthscale/vite-config@0.6.0","externalReferences":[{"url":"https://github.com/stealth-scale/scale/issues","type":"issue-tracker","comment":"as detected from PackageJson property \"bugs\""},{"url":"git+https://github.com/stealth-scale/scale.git#packages/vite-config","type":"vcs","comment":"as detected from PackageJson property \"repository.url\" and \"repository.directory\""},{"url":"https://github.com/stealth-scale/scale/tree/main/packages/vite-config#readme","type":"website","comment":"as detected from PackageJson property \"homepage\""}],"properties":[{"name":"cdx:npm:package:constraint:engine:node","value":">=26.0.0"}]},{"type":"library","name":"react","group":"@testing-library","version":"16.3.3","bom-ref":"pkg:npm/%40testing-library/react@16.3.3","author":"Kent C. Dodds <me@kentcdodds.com> (https://kentcdodds.com)","description":"Simple and complete React DOM testing utilities that encourage good testing practices.","hashes":[{"alg":"SHA-512","content":"528d7ddcd8106cf333ea5aeb86d45040570c0ba45efc42cb15b6ee54bdf4583959c65a597fdfe53074c05713d12f0d6ad62bbd389991d5cd812e210d46cb5d06"}],"licenses":[{"license":{"id":"MIT"}}],"purl":"pkg:npm/%40testing-library/react@16.3.3","externalReferences":[{"url":"https://github.com/testing-library/react-testing-library/issues","type":"issue-tracker","comment":"as detected from PackageJson property \"bugs.url\""},{"url":"https://github.com/testing-library/react-testing-library","type":"vcs","comment":"as detected from PackageJson property \"repository.url\""},{"url":"https://github.com/testing-library/react-testing-library#readme","type":"website","comment":"as detected from PackageJson property \"homepage\""}],"properties":[{"name":"cdx:npm:package:constraint:engine:node","value":">=18"}]},{"type":"library","name":"react-dom","group":"@types","version":"19.3.0","bom-ref":"pkg:npm/%40types/react-dom@19.3.0","description":"TypeScript definitions for react-dom","hashes":[{"alg":"SHA-512","content":"648edb538da66575ca1e7fea34b130d88adb888354ed7e7ebdf81d8b10479023696165e32a07d0fcffaec866f90a338b07d51c9d37a0debca542d5768729b8e1"}],"licenses":[{"license":{"id":"MIT"}}],"purl":"pkg:npm/%40types/react-dom@19.3.0","externalReferences":[{"url":"https://github.com/DefinitelyTyped/DefinitelyTyped.git#types/react-dom","type":"vcs","comment":"as detected from PackageJson property \"repository.url\" and \"repository.directory\""},{"url":"https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/react-dom","type":"website","comment":"as detected from PackageJson property \"homepage\""}]},{"type":"library","name":"react","group":"@types","version":"19.3.0","bom-ref":"pkg:npm/%40types/react@19.3.0","description":"TypeScript definitions for react","hashes":[{"alg":"SHA-512","content":"374ac50ae1fd628c46f7f9bad65f4c7e924a7e62ce554d1e9bb8a9233e9346049292f45e2c1f6f2fce4607ecabf01b3995eaa9be0f7a252c05e194b5b38be242"}],"licenses":[{"license":{"id":"MIT"}}],"purl":"pkg:npm/%40types/react@19.3.0","externalReferences":[{"url":"https://github.com/DefinitelyTyped/DefinitelyTyped.git#types/react","type":"vcs","comment":"as detected from PackageJson property \"repository.url\" and \"repository.directory\""},{"url":"https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/react","type":"website","comment":"as detected from PackageJson property \"homepage\""}]},{"type":"library","name":"happy-dom","version":"20.14.5","bom-ref":"pkg:npm/happy-dom@20.14.5","author":"David Ortner","description":"Happy DOM is a JavaScript implementation of a web browser without its graphical user interface. It includes many web standards from WHATWG DOM and HTML.","hashes":[{"alg":"SHA-512","content":"c7f47392958ee346d38c8a13df4890b6deb814b2e61ff89171408dd97fff6cbc7b1f789f91d7cf5f2aacae8fce62dab32008622cc300ee69a2391f42dcd38a8d"}],"licenses":[{"license":{"id":"MIT"}}],"purl":"pkg:npm/happy-dom@20.14.5","externalReferences":[{"url":"https://github.com/capricorn86/happy-dom","type":"vcs","comment":"as detected from PackageJson property \"repository\""},{"url":"https://github.com/capricorn86/happy-dom","type":"website","comment":"as detected from PackageJson property \"homepage\""}],"properties":[{"name":"cdx:npm:package:constraint:engine:node","value":">=20.0.0"}]},{"type":"library","name":"lucide-react","version":"1.48.0","bom-ref":"pkg:npm/lucide-react@1.48.0","author":"Eric Fennis","description":"A Lucide icon library package for React applications.","hashes":[{"alg":"SHA-512","content":"474088298fdf5e20bacbdc510400e0b0af955b6a7fa5c4c938c84b65fd53fae1bebc952fc94474da718e8262080bb30f92234ae0ec7c4039dba85f2b25858f65"}],"licenses":[{"license":{"id":"ISC"}}],"purl":"pkg:npm/lucide-react@1.48.0","externalReferences":[{"url":"https://github.com/lucide-icons/lucide/issues","type":"issue-tracker","comment":"as detected from PackageJson property \"bugs\""},{"url":"https://github.com/lucide-icons/lucide.git#packages/lucide-react","type":"vcs","comment":"as detected from PackageJson property \"repository.url\" and \"repository.directory\""},{"url":"https://lucide.dev","type":"website","comment":"as detected from PackageJson property \"homepage\""}]},{"type":"library","name":"oxc-transform-react","version":"0.151.0","bom-ref":"pkg:npm/oxc-transform-react@0.151.0","author":"Boshen and oxc contributors","description":"Oxc React Compiler Node API","hashes":[{"alg":"SHA-512","content":"60ed7f62cdc5202fee060ca37b1bc6b084d05085c318d9b1272eafa906ddf1663b263ccc0f5faf095879f65cdaf7f3f4dbf4d135a517c94eceaf6c70c7f29b77"}],"licenses":[{"license":{"id":"MIT"}}],"purl":"pkg:npm/oxc-transform-react@0.151.0","externalReferences":[{"url":"https://github.com/oxc-project/oxc/issues","type":"issue-tracker","comment":"as detected from PackageJson property \"bugs\""},{"url":"git+https://github.com/oxc-project/oxc.git#napi/transform-react","type":"vcs","comment":"as detected from PackageJson property \"repository.url\" and \"repository.directory\""},{"url":"https://oxc.rs","type":"website","comment":"as detected from PackageJson property \"homepage\""}],"properties":[{"name":"cdx:npm:package:constraint:engine:node","value":"^20.19.0 || >=22.12.0"}]},{"type":"library","name":"react-dom","version":"19.3.0","bom-ref":"pkg:npm/react-dom@19.3.0","description":"React package for working with the DOM.","hashes":[{"alg":"SHA-512","content":"24393c76089fe753a31680c4ef4f8e4fd202c98afeebd1e59a284dc29d7e36c7db9dadede6c22209af59264b43990e3fd5bfeb9f6ea1200476b985c332be6bd1"}],"licenses":[{"license":{"id":"MIT"}}],"purl":"pkg:npm/react-dom@19.3.0","externalReferences":[{"url":"https://github.com/react/react/issues","type":"issue-tracker","comment":"as detected from PackageJson property \"bugs.url\""},{"url":"https://github.com/react/react.git#packages/react-dom","type":"vcs","comment":"as detected from PackageJson property \"repository.url\" and \"repository.directory\""},{"url":"https://react.dev/","type":"website","comment":"as detected from PackageJson property \"homepage\""}]},{"type":"library","name":"react","version":"19.3.0","bom-ref":"pkg:npm/react@19.3.0","description":"React is a JavaScript library for building user interfaces.","hashes":[{"alg":"SHA-512","content":"13c2d471bb415addb46db9766281dfc7864305dc554df38ab42667f5c0d227897afe7ba8029729201723e3bb76c19a155fc836647b8c1db892860091d53e52a2"}],"licenses":[{"license":{"id":"MIT"}}],"purl":"pkg:npm/react@19.3.0","externalReferences":[{"url":"https://github.com/react/react/issues","type":"issue-tracker","comment":"as detected from PackageJson property \"bugs\""},{"url":"https://github.com/react/react.git#packages/react","type":"vcs","comment":"as detected from PackageJson property \"repository.url\" and \"repository.directory\""},{"url":"https://react.dev/","type":"website","comment":"as detected from PackageJson property \"homepage\""}],"properties":[{"name":"cdx:npm:package:constraint:engine:node","value":">=0.10.0"}]},{"type":"library","name":"typescript","version":"7.0.2","bom-ref":"pkg:npm/typescript@7.0.2","author":"Microsoft Corp.","description":"TypeScript is a language for application scale JavaScript development","hashes":[{"alg":"SHA-512","content":"f0561abbdea8dcd28e85b8ca8bfa8dbc6fd6e63873c646dd9b9b23f406d9ff94f9b16aa7de12602df1b1dbbb11299593bf2cc23fc74b4414dfe6d05349155434"}],"licenses":[{"license":{"id":"Apache-2.0"}}],"purl":"pkg:npm/typescript@7.0.2","externalReferences":[{"url":"https://github.com/microsoft/TypeScript/issues","type":"issue-tracker","comment":"as detected from PackageJson property \"bugs.url\""},{"url":"https://github.com/microsoft/TypeScript.git","type":"vcs","comment":"as detected from PackageJson property \"repository.url\""},{"url":"https://www.typescriptlang.org/","type":"website","comment":"as detected from PackageJson property \"homepage\""}],"properties":[{"name":"cdx:npm:package:constraint:engine:node","value":">=16.20.0"}]},{"type":"library","name":"vitest","version":"5.0.1","bom-ref":"pkg:npm/vitest@5.0.1","author":"Anthony Fu <anthonyfu117@hotmail.com>","description":"Next generation testing framework powered by Vite","hashes":[{"alg":"SHA-512","content":"880f799506ca124bebb5391d0209d66d77db8a95a28967bf8439763f9b4c8ba585c03efa1b43715c01a9fccf4439c62ea5e18946beb0a6931cec2a00e354d08e"}],"licenses":[{"license":{"id":"MIT"}}],"purl":"pkg:npm/vitest@5.0.1","externalReferences":[{"url":"https://github.com/vitest-dev/vitest/issues","type":"issue-tracker","comment":"as detected from PackageJson property \"bugs.url\""},{"url":"git+https://github.com/vitest-dev/vitest.git#packages/vitest","type":"vcs","comment":"as detected from PackageJson property \"repository.url\" and \"repository.directory\""},{"url":"https://vitest.dev","type":"website","comment":"as detected from PackageJson property \"homepage\""}],"properties":[{"name":"cdx:npm:package:constraint:engine:node","value":"^22.12.0 || ^24.0.0 || >=26.0.0"}]}]},"component":{"type":"library","name":"component-tables","group":"@stealthscale","version":"0.1.0","bom-ref":"pkg:npm/%40stealthscale/component-tables@0.1.0","description":"Renders records in data tables over TanStack Table: sorting, filtering, selection, pages, column sizes and pins, rows with levels, windowed rows, pivots and editable grids.","licenses":[{"license":{"id":"MIT"}}],"purl":"pkg:npm/%40stealthscale/component-tables@0.1.0","externalReferences":[{"url":"https://github.com/stealth-scale/scale/issues","type":"issue-tracker","comment":"as detected from PackageJson property \"bugs\""},{"url":"git+https://github.com/stealth-scale/scale.git#components/tables","type":"vcs","comment":"as detected from PackageJson property \"repository.url\" and \"repository.directory\""},{"url":"https://github.com/stealth-scale/scale/tree/main/components/tables#readme","type":"website","comment":"as detected from PackageJson property \"homepage\""}],"properties":[{"name":"cdx:npm:package:constraint:engine:node","value":">=26.0.0"}],"evidence":{"licenses":[{"license":{"name":"file: LICENSE","text":{"content":"TUlUIExpY2Vuc2UKCkNvcHlyaWdodCAoYykgMjAyNiBTdGVhbHRoIFNjYWxlIEIuVi4KClBlcm1pc3Npb24gaXMgaGVyZWJ5IGdyYW50ZWQsIGZyZWUgb2YgY2hhcmdlLCB0byBhbnkgcGVyc29uIG9idGFpbmluZyBhIGNvcHkKb2YgdGhpcyBzb2Z0d2FyZSBhbmQgYXNzb2NpYXRlZCBkb2N1bWVudGF0aW9uIGZpbGVzICh0aGUgIlNvZnR3YXJlIiksIHRvIGRlYWwKaW4gdGhlIFNvZnR3YXJlIHdpdGhvdXQgcmVzdHJpY3Rpb24sIGluY2x1ZGluZyB3aXRob3V0IGxpbWl0YXRpb24gdGhlIHJpZ2h0cwp0byB1c2UsIGNvcHksIG1vZGlmeSwgbWVyZ2UsIHB1Ymxpc2gsIGRpc3RyaWJ1dGUsIHN1YmxpY2Vuc2UsIGFuZC9vciBzZWxsCmNvcGllcyBvZiB0aGUgU29mdHdhcmUsIGFuZCB0byBwZXJtaXQgcGVyc29ucyB0byB3aG9tIHRoZSBTb2Z0d2FyZSBpcwpmdXJuaXNoZWQgdG8gZG8gc28sIHN1YmplY3QgdG8gdGhlIGZvbGxvd2luZyBjb25kaXRpb25zOgoKVGhlIGFib3ZlIGNvcHlyaWdodCBub3RpY2UgYW5kIHRoaXMgcGVybWlzc2lvbiBub3RpY2Ugc2hhbGwgYmUgaW5jbHVkZWQgaW4gYWxsCmNvcGllcyBvciBzdWJzdGFudGlhbCBwb3J0aW9ucyBvZiB0aGUgU29mdHdhcmUuCgpUSEUgU09GVFdBUkUgSVMgUFJPVklERUQgIkFTIElTIiwgV0lUSE9VVCBXQVJSQU5UWSBPRiBBTlkgS0lORCwgRVhQUkVTUyBPUgpJTVBMSUVELCBJTkNMVURJTkcgQlVUIE5PVCBMSU1JVEVEIFRPIFRIRSBXQVJSQU5USUVTIE9GIE1FUkNIQU5UQUJJTElUWSwKRklUTkVTUyBGT1IgQSBQQVJUSUNVTEFSIFBVUlBPU0UgQU5EIE5PTklORlJJTkdFTUVOVC4gSU4gTk8gRVZFTlQgU0hBTEwgVEhFCkFVVEhPUlMgT1IgQ09QWVJJR0hUIEhPTERFUlMgQkUgTElBQkxFIEZPUiBBTlkgQ0xBSU0sIERBTUFHRVMgT1IgT1RIRVIKTElBQklMSVRZLCBXSEVUSEVSIElOIEFOIEFDVElPTiBPRiBDT05UUkFDVCwgVE9SVCBPUiBPVEhFUldJU0UsIEFSSVNJTkcgRlJPTSwKT1VUIE9GIE9SIElOIENPTk5FQ1RJT04gV0lUSCBUSEUgU09GVFdBUkUgT1IgVEhFIFVTRSBPUiBPVEhFUiBERUFMSU5HUyBJTiBUSEUKU09GVFdBUkUuCg==","contentType":"text/plain","encoding":"base64"}}}]}},"supplier":{"name":"Stealth Scale B.V.","url":["https://stealthscale.io"]}},"components":[],"dependencies":[{"ref":"pkg:npm/%40stealthscale/component-tables@0.1.0"}]}
|