@tensor_1/react 2.49.1 → 2.51.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.
@@ -2,6 +2,7 @@ import * as react from 'react';
2
2
  import { HTMLAttributes, ReactNode } from 'react';
3
3
  import { UixRenderLink } from '../link.js';
4
4
  import { FilterField, FilterValue, SummarizeFilterOptions } from '../filter-model.js';
5
+ import { PaginationLabels } from './Pagination.js';
5
6
  import { TableDensity } from './Table.js';
6
7
  import { SavedViewMenuProps, ViewMenuProps } from './TableControls.js';
7
8
 
@@ -37,6 +38,18 @@ interface DataWorkspaceLabels {
37
38
  filteredEmptyDetail: string;
38
39
  error: string;
39
40
  retry: string;
41
+ /** A sorted header's place among two or more sort keys (HAR-2623): `{n}`. */
42
+ sortPriority: string;
43
+ /** A group header's row count (HAR-2625): `{count}`. */
44
+ groupCount: string;
45
+ /** The same for one row. */
46
+ groupCountOne: string;
47
+ /** A group header's toggle while the group is open: `{name}`. */
48
+ collapseGroup: string;
49
+ /** The same while it is collapsed. */
50
+ expandGroup: string;
51
+ /** A group header's checkbox: `{name}`. */
52
+ selectGroup: string;
40
53
  }
41
54
  declare const DEFAULT_DATA_WORKSPACE_LABELS: DataWorkspaceLabels;
42
55
  interface DataWorkspaceColumn<T> {
@@ -48,23 +61,105 @@ interface DataWorkspaceColumn<T> {
48
61
  align?: 'start' | 'end';
49
62
  /** A CSS width for the column, e.g. `12rem`. */
50
63
  width?: string;
64
+ /**
65
+ * The column's floor, e.g. `14rem` (HAR-2624). In a fixed layout the table's own min-width is
66
+ * the sum of its columns' floors, so a squeezed table scrolls rather than narrow them.
67
+ */
68
+ minWidth?: string;
69
+ /**
70
+ * A class on the column's header and body cells, e.g. `uix-col--truncate` (HAR-2624). A width
71
+ * tier (`uix-col--w-md`) or `uix-col--flex` also sizes the column's `<col>` in a fixed layout.
72
+ */
73
+ className?: string;
74
+ /**
75
+ * The responsive ladder (`mobile`, HAR-2622): `low` columns go below 900 px of workspace width,
76
+ * `mid` ones below 600 px; `high` (the default) stay.
77
+ */
78
+ priority?: 'high' | 'mid' | 'low';
79
+ /** The column's name in words: a card's label under `mobile="cards"`. Default: `header` when it is a string. */
80
+ label?: string;
51
81
  }
52
82
  interface DataWorkspaceSort {
53
83
  columnId: string;
54
84
  direction: 'ascending' | 'descending';
55
85
  }
86
+ /** `sort` as a product passes it: one key (or none), or an ordered list of keys (HAR-2623). */
87
+ type DataWorkspaceSortState = DataWorkspaceSort | readonly DataWorkspaceSort[] | null;
88
+ /** What `onSortChange` receives: a list when `sort` is a list, else one key or `null`. */
89
+ type DataWorkspaceSortValue<S> = S extends readonly DataWorkspaceSort[] ? DataWorkspaceSort[] : DataWorkspaceSort | null;
56
90
  /** The record-id column: the first column, the row's way into its record. */
57
91
  interface DataWorkspaceIdColumn<T> {
58
92
  header: ReactNode;
59
93
  /** The id as shown, e.g. "INC-20418". Also names the row's checkbox and ↗ button. */
60
94
  text: (row: T) => string;
95
+ /**
96
+ * The cell's content instead of `text` (HAR-2622): e.g. a strong title over a muted line, with
97
+ * the search match marked. It sits inside the same button or link; `text` still names the
98
+ * checkbox and the ↗.
99
+ */
100
+ render?: (row: T) => ReactNode;
61
101
  /** The id button opens a peek of the record. */
62
102
  onPeek?: (row: T) => void;
63
103
  /** The ↗ link goes to the full record. */
64
104
  href?: (row: T) => string;
105
+ /**
106
+ * Without `onPeek`: the whole id is the link to `href`, with the ↗ inside it, for a row whose
107
+ * own record is the way in (HAR-2622). Off: the id is text and the ↗ is the link.
108
+ */
109
+ linkId?: boolean;
110
+ /** The column's width in a fixed layout (`fixed`), e.g. `16rem`. */
111
+ width?: string;
112
+ /** The column's floor in a fixed layout. Default: the `md` column tier. */
113
+ minWidth?: string;
114
+ /**
115
+ * The sort key the id column answers to in `sort` / `onSortChange` (HAR-2631), e.g. `title`
116
+ * when the id shows the title. Default `'id'`. Keep it distinct from the data columns' ids.
117
+ */
118
+ id?: string;
119
+ /**
120
+ * The id header sorts like a data column (HAR-2631): a click cycles it, Shift + click (or
121
+ * Shift + Enter / Space) adds it as a further key in a multi-sort, with its ordinal and
122
+ * `aria-sort`. Off: the header is plain, as in 2.50.0.
123
+ */
124
+ sortable?: boolean;
65
125
  renderLink?: UixRenderLink;
66
126
  }
67
- type DataWorkspacePagination = {
127
+ /** A group of rows under one header row (HAR-2625). The product computes the groups. */
128
+ interface DataWorkspaceGroup<T> {
129
+ id: string;
130
+ /** The group's value as shown, e.g. a status pill. */
131
+ label: ReactNode;
132
+ /** The group's name in words, for its toggle and checkbox. Default: `label` when a string, else `id`. */
133
+ name?: string;
134
+ rows: readonly T[];
135
+ /** The group's total, when it holds more than the rows on this page. Default: `rows.length`. */
136
+ count?: number;
137
+ /** Aggregated cells in the header row, by column id (a sum, a range), each under its column. */
138
+ aggregates?: Readonly<Record<string, ReactNode>>;
139
+ }
140
+ /** Where a row's actions are shown (HAR-2634), the second argument of `rowActions`. */
141
+ interface DataWorkspaceRowActionsContext {
142
+ /** The ladder's step: `null` for the full table (or without `mobile`). */
143
+ ladder: 'cards' | 'priority' | 'priority-mid' | null;
144
+ /** The narrowest table step (`priority-mid`): render only the always-on control. Cards keep the full set, on a line of their own. */
145
+ compact: boolean;
146
+ }
147
+ /** Windowing for a long unpaged list (HAR-2626), over `useVirtualRows`. */
148
+ interface DataWorkspaceVirtualize {
149
+ /** Window only above this many rows. Default 100. */
150
+ threshold?: number;
151
+ /** The first paint's row height in px; the mounted rows are measured after. Default: by density (44, 56, 68). */
152
+ estimatedRowHeight?: number;
153
+ /** The scrolling body's height cap in px. Default 640. */
154
+ maxHeight?: number;
155
+ /** Rows rendered beyond each edge. Default 8. */
156
+ overscan?: number;
157
+ }
158
+ /**
159
+ * The footer's pager. `labels` words the pager itself; without them it takes `UixLabelsProvider`
160
+ * `pagination`, then English (HAR-2631).
161
+ */
162
+ type DataWorkspacePagination = ({
68
163
  mode: 'offset';
69
164
  page: number;
70
165
  pageSize: number;
@@ -83,38 +178,87 @@ type DataWorkspacePagination = {
83
178
  pageSize?: number;
84
179
  pageSizes?: readonly number[];
85
180
  onPageSizeChange?: (size: number) => void;
181
+ }) & {
182
+ labels?: Partial<PaginationLabels>;
86
183
  };
87
- interface DataWorkspaceProps<T> extends Omit<HTMLAttributes<HTMLElement>, 'children'> {
184
+ interface DataWorkspaceProps<T, S extends DataWorkspaceSortState = DataWorkspaceSort | null> extends Omit<HTMLAttributes<HTMLElement>, 'children'> {
88
185
  /** Names the workspace region and the table. */
89
186
  label: string;
90
187
  rows: readonly T[];
91
188
  getRowId: (row: T) => string;
189
+ /**
190
+ * A row's name in words, where the kit names a row (its checkbox, HAR-2632) and there is no
191
+ * `idColumn.text`: a title or a person, never a key. Without it the row id is used, which is
192
+ * a UUID on many tables. Keep it stable (`useCallback`), as rows are memoised.
193
+ */
194
+ rowLabel?: (row: T) => string;
92
195
  columns: readonly DataWorkspaceColumn<T>[];
93
196
  idColumn?: DataWorkspaceIdColumn<T>;
94
- sort?: DataWorkspaceSort | null;
95
- onSortChange?: (sort: DataWorkspaceSort | null) => void;
197
+ /**
198
+ * The sort: one key, or an ordered list of keys (HAR-2623). With a list, Shift + click (or
199
+ * Shift + Enter / Space) on a header adds, turns or removes that column as a further key, and
200
+ * each sorted header shows its place once two or more keys are set.
201
+ */
202
+ sort?: S;
203
+ /** Receives the same shape `sort` holds: a list for a list, else one key or `null`. */
204
+ onSortChange?: (sort: DataWorkspaceSortValue<S>) => void;
205
+ /**
206
+ * List mode even while `sort` is `null` or one key, for a product whose sort state starts
207
+ * empty. Type `sort` as a list (`[]` for none): `onSortChange` then receives a list.
208
+ */
209
+ multiSort?: boolean;
96
210
  /** The selected row ids. Kept by id, so a refresh or a new page keeps them. */
97
211
  selection?: ReadonlySet<string>;
98
212
  onSelectionChange?: (selection: Set<string>) => void;
213
+ /** At most this many rows can be selected: the unchecked boxes are disabled at the cap (HAR-2622). */
214
+ maxSelection?: number;
99
215
  /** The bulk bar's actions for the selection (a `Menu` for the overflow). */
100
216
  bulkActions?: (selectedIds: string[]) => ReactNode;
101
217
  /**
102
218
  * A row's actions (a `Menu`, quick buttons), in a column frozen at the right edge and sized to
103
219
  * them. Return `null` for a row without actions; with none on the page there is no column.
220
+ * The second argument says where the row is shown (HAR-2634): at the narrowest step of the
221
+ * ladder (`compact`, a workspace under 600 px with `mobile`), render only the always-on control,
222
+ * the peek or a kebab; the peek or the record carries the verbs. A frozen actions cell is for
223
+ * compact controls: word verbs (wider than a quarter of the table) unfreeze and scroll with
224
+ * their row, at any width.
104
225
  */
105
- rowActions?: (row: T) => ReactNode;
226
+ rowActions?: (row: T, context: DataWorkspaceRowActionsContext) => ReactNode;
106
227
  /** More props for a row, e.g. a data attribute that marks it changed (see ChangeHighlight, PR #164). */
107
228
  getRowProps?: (row: T) => HTMLAttributes<HTMLTableRowElement> & Record<`data-${string}`, string | undefined>;
108
229
  density?: TableDensity;
230
+ /** Zebra rows (the `Table` default, on). The View menu's toggle (HAR-2624). */
231
+ zebra?: boolean;
232
+ /**
233
+ * Freeze the first column without an id column; with selection the checkbox is frozen too
234
+ * (HAR-2624). With `mobile`, nothing is frozen from the narrowest step (under 600 px).
235
+ */
236
+ freezeFirstColumn?: boolean;
237
+ /**
238
+ * `table-layout: fixed` with a `<colgroup>` from the columns' widths and tiers, and the table's
239
+ * min-width at the sum of the columns' floors (TENSOR HAR-997), so a truncating column clips
240
+ * instead of growing. Default: on when a column has `className` or `minWidth`. Not in cards.
241
+ */
242
+ fixed?: boolean;
243
+ /**
244
+ * The responsive ladder, from the workspace's own width (HAR-2622): `priority` drops `low`
245
+ * columns below 900 px and `mid` ones below 600 px; `cards` turns the rows into cards below
246
+ * 768 px (each value labelled by its column) and drops low-priority columns up to 900 px.
247
+ */
248
+ mobile?: 'priority' | 'cards';
109
249
  /**
110
250
  * Toolbar: the search field. Ctrl/⌘ + / focuses it from inside the workspace, or from the page
111
- * when nothing has focus and this is the page's first workspace.
251
+ * when nothing has focus and this is the page's first workspace. `onSubmit` (Enter), `onClear`
252
+ * (the × or Escape) and `maxLength` go to the `SearchBar`.
112
253
  */
113
254
  search?: {
114
255
  value: string;
115
256
  onChange: (value: string) => void;
116
257
  label?: string;
117
258
  placeholder?: string;
259
+ onSubmit?: (value: string) => void;
260
+ onClear?: () => void;
261
+ maxLength?: number;
118
262
  };
119
263
  /**
120
264
  * Toolbar: filter chips and "+ Filter" over `FilterEditor`. A chip whose value holds ids
@@ -127,11 +271,24 @@ interface DataWorkspaceProps<T> extends Omit<HTMLAttributes<HTMLElement>, 'child
127
271
  /** Rows matching each filter, shown on its chip. */
128
272
  counts?: Readonly<Record<string, number>>;
129
273
  summary?: SummarizeFilterOptions;
274
+ /**
275
+ * Clear every filter in one change (HAR-2622), instead of one `onChange` per field: a product
276
+ * whose state is the URL would otherwise navigate once per field from the same stale state.
277
+ * `search: true` from "Nothing matches" while searching: clear the search too (the kit then
278
+ * does not call `search.onChange('')`).
279
+ */
280
+ onClearAll?: (scope: {
281
+ search: boolean;
282
+ }) => void;
130
283
  };
131
- /** Toolbar: the saved-view trigger, named by the current view, opening a `SavedViewMenu`. */
284
+ /**
285
+ * Toolbar: the saved-view trigger, named by the current view, opening a `SavedViewMenu`.
286
+ * `note` sits above the list ("Saved views could not be loaded").
287
+ */
132
288
  savedViews?: {
133
289
  current: ReactNode;
134
290
  menu: SavedViewMenuProps;
291
+ note?: ReactNode;
135
292
  };
136
293
  /** Toolbar: the View button, opening a `ViewMenu` (columns, density). */
137
294
  viewMenu?: ViewMenuProps;
@@ -139,17 +296,63 @@ interface DataWorkspaceProps<T> extends Omit<HTMLAttributes<HTMLElement>, 'child
139
296
  viewChanged?: boolean;
140
297
  onSaveView?: () => void;
141
298
  onSaveViewAs?: () => void;
299
+ /** When Save as shows: `changed` (default) with `viewChanged`; `always`, to keep any view under a name (HAR-2622). */
300
+ saveViewAs?: 'changed' | 'always';
142
301
  /** Toolbar: a maximize toggle; Escape restores, unless an open popover or menu takes it first. */
143
302
  maximized?: boolean;
144
303
  onMaximizedChange?: (maximized: boolean) => void;
145
304
  /** Toolbar: the product's own actions at the end ("New incident"). */
146
305
  toolbarEnd?: ReactNode;
306
+ /** Between the toolbar (and bulk bar) and the rows: notices, a "3 new — show" pill, a truncation warning. */
307
+ beforeBody?: ReactNode;
147
308
  pagination?: DataWorkspacePagination;
148
309
  /** `loading`: the first load (skeleton rows). `error`: with `onRetry`. */
149
310
  status?: 'ready' | 'loading' | 'error';
150
311
  /** A refetch (filter, sort, page, saved view): the rows stay under `TableWrap refreshing`. */
151
312
  refreshing?: boolean;
152
313
  onRetry?: () => void;
314
+ /** Skeleton rows on a first load. Default 6. */
315
+ loadingRows?: number;
316
+ /** The empty body in place of the default one (HAR-2624), e.g. an `EmptyState` with the first action. */
317
+ empty?: ReactNode;
318
+ /** The default empty body's action ("Add the first book"). */
319
+ emptyAction?: ReactNode;
320
+ /** The filtered-empty body in place of the default one. */
321
+ filteredEmpty?: ReactNode;
322
+ /** A further action in the default filtered-empty body, after its Clear filters. */
323
+ filteredEmptyAction?: ReactNode;
324
+ /** The error body in place of the default one (a problem's title, a diagnostic line, Retry). */
325
+ error?: ReactNode;
326
+ /**
327
+ * A product state in place of the rows, whatever `status` says (a page past the end: "Go to the
328
+ * first page"). The toolbar stays; the footer goes. A refetch shows it busy.
329
+ */
330
+ stateOverride?: ReactNode;
331
+ /** Rows in groups under collapsible header rows, in place of `rows` (HAR-2625). Not windowed. */
332
+ groups?: readonly DataWorkspaceGroup<T>[];
333
+ /** The collapsed group ids. Without it the workspace keeps them itself. */
334
+ collapsedGroups?: ReadonlySet<string>;
335
+ /** A group was collapsed or opened: the new set of collapsed ids. */
336
+ onCollapsedGroupsChange?: (collapsed: Set<string>) => void;
337
+ /**
338
+ * Window a long unpaged list (HAR-2626): only the rows in view are mounted, in a body that
339
+ * scrolls under its sticky header. Off with `pagination`, `groups` and the cards ladder.
340
+ */
341
+ virtualize?: boolean | DataWorkspaceVirtualize;
342
+ /**
343
+ * The keyboard row model (HAR-2626): one row in the tab order, ↑ / ↓ / Home / End move between
344
+ * rows, Enter opens the row (`onRowActivate`, else its id: the peek or the link), Space does not
345
+ * scroll the page. Default: on with `onRowActivate`.
346
+ */
347
+ rowNavigation?: boolean;
348
+ /** A row's primary action: Enter on the row, and a click on the row (not on a control in it or its cell). Turns on `rowNavigation`. */
349
+ onRowActivate?: (row: T) => void;
350
+ /** `false`: only Enter activates a row, not a pointer click. Default `true`. */
351
+ activateOnClick?: boolean;
352
+ /** The row with the tab stop, to drive it from the product's own shortcuts (j / k). Changing it focuses that row. */
353
+ activeRowId?: string | null;
354
+ /** The tab stop moved to another row (a key, a click, focus entering a row). */
355
+ onActiveRowChange?: (rowId: string) => void;
153
356
  labels?: Partial<DataWorkspaceLabels>;
154
357
  }
155
358
  /**
@@ -161,7 +364,11 @@ interface DataWorkspaceProps<T> extends Omit<HTMLAttributes<HTMLElement>, 'child
161
364
  * footer with rows per page and `Pagination` (offset or cursor); first-load, empty,
162
365
  * filtered-empty and error states, and a refetch that keeps the rows. Prop-driven: the product
163
366
  * keeps its surface registry, URL state, data fetching and permissions.
367
+ *
368
+ * 2.50 (HAR-2622..2626, ADR-0005 addendum): a responsive ladder (`mobile`, `column.priority`), a
369
+ * rich id cell, multi-column sort, the product's state bodies and `beforeBody`, row groups,
370
+ * windowing (`virtualize`) and a keyboard row model (`onRowActivate`).
164
371
  */
165
- declare function DataWorkspace<T>({ label, rows, getRowId, columns, idColumn, sort, onSortChange, selection, onSelectionChange, bulkActions, rowActions, getRowProps, density, search, filters, savedViews, viewMenu, viewChanged, onSaveView, onSaveViewAs, maximized, onMaximizedChange, toolbarEnd, pagination, status, refreshing, onRetry, labels: labelOverrides, className, ...props }: DataWorkspaceProps<T>): react.JSX.Element;
372
+ declare function DataWorkspace<T, S extends DataWorkspaceSortState = DataWorkspaceSort | null>({ label, rows, getRowId, rowLabel, columns, idColumn, sort, onSortChange, selection, onSelectionChange, maxSelection, bulkActions, rowActions, getRowProps, density, zebra, freezeFirstColumn, fixed, mobile, search, filters, savedViews, viewMenu, viewChanged, onSaveView, onSaveViewAs, saveViewAs, maximized, onMaximizedChange, toolbarEnd, beforeBody, pagination, status, refreshing, onRetry, loadingRows, empty, emptyAction, filteredEmpty, filteredEmptyAction, error, stateOverride, groups, collapsedGroups, onCollapsedGroupsChange, virtualize, rowNavigation, onRowActivate, activateOnClick, activeRowId, onActiveRowChange, multiSort, labels: labelOverrides, className, ...props }: DataWorkspaceProps<T, S>): react.JSX.Element;
166
373
 
167
- export { DEFAULT_DATA_WORKSPACE_LABELS, DataWorkspace, type DataWorkspaceColumn, type DataWorkspaceIdColumn, type DataWorkspaceLabels, type DataWorkspacePagination, type DataWorkspaceProps, type DataWorkspaceSort };
374
+ export { DEFAULT_DATA_WORKSPACE_LABELS, DataWorkspace, type DataWorkspaceColumn, type DataWorkspaceGroup, type DataWorkspaceIdColumn, type DataWorkspaceLabels, type DataWorkspacePagination, type DataWorkspaceProps, type DataWorkspaceRowActionsContext, type DataWorkspaceSort, type DataWorkspaceSortState, type DataWorkspaceSortValue, type DataWorkspaceVirtualize };