@rivinia/table 0.0.0-stage → 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.
@@ -0,0 +1,494 @@
1
+ import { Signal, WritableSignal } from "@angular/core";
2
+ /**
3
+ * The public vocabulary of `@rivinia/table`.
4
+ *
5
+ * Everything here is data: no behaviour, no Angular imports. The controller
6
+ * and the UI both read these shapes, which is what keeps the headless core
7
+ * testable without a DOM and the UI replaceable without touching the logic.
8
+ */
9
+ /** Rows are identified by a stable id, never by an array position. */
10
+ type TableRowId = string | number;
11
+ type SortDirection = 'asc' | 'desc';
12
+ /** One active sort level. Multi-sort is an ordered list of these. */
13
+ interface SortState {
14
+ readonly column: string;
15
+ readonly direction: SortDirection;
16
+ }
17
+ /** One active column filter. The value shape belongs to the app's filter. */
18
+ interface FilterState {
19
+ readonly column: string;
20
+ readonly value: unknown;
21
+ }
22
+ /** A row after the pipeline has given it an id and a position. */
23
+ interface TableRow<T> {
24
+ /** Stable across sorting and paging: what selection and actions refer to. */
25
+ readonly id: TableRowId;
26
+ readonly data: T;
27
+ /** Position in `rows`, i.e. after filtering and sorting. */
28
+ readonly index: number;
29
+ }
30
+ /** A rectangle of a grid, expressed in cell coordinates. */
31
+ interface CellSpan {
32
+ readonly rowSpan?: number;
33
+ readonly colSpan?: number;
34
+ }
35
+ /** What a `span()` / `colSpan()` callback is given. */
36
+ interface SpanContext<T> {
37
+ readonly row: TableRow<T>;
38
+ readonly column: TableColumn<T>;
39
+ readonly value: unknown;
40
+ readonly rowIndex: number;
41
+ readonly columnIndex: number;
42
+ }
43
+ /** A span anchored at one cell; the cells it covers are not rendered. */
44
+ interface SpanCell {
45
+ readonly row: number;
46
+ readonly column: number;
47
+ readonly rowSpan: number;
48
+ readonly colSpan: number;
49
+ }
50
+ /**
51
+ * The resolved span grid for the current rows and columns.
52
+ *
53
+ * Every query is O(1): the model is a dense matrix of anchors built once per
54
+ * (rows, columns) pair, so rendering 10,000 rows does not re-walk the options.
55
+ */
56
+ interface SpanModel {
57
+ /** Anchors only, in reading order. Covered cells appear nowhere. */
58
+ readonly cells: readonly SpanCell[];
59
+ /** True when a span STARTS at this position. */
60
+ at(row: number, column: number): SpanCell | null;
61
+ /** The span COVERING this position, including the anchor itself. */
62
+ owner(row: number, column: number): SpanCell | null;
63
+ /** One step of keyboard navigation that respects spans. */
64
+ move(row: number, column: number, dRow: number, dColumn: number): {
65
+ readonly row: number;
66
+ readonly column: number;
67
+ };
68
+ }
69
+ /** A value accessor: a key of the row, or a function for computed cells. */
70
+ /**
71
+ * `V` defaults to `any`, not `unknown`, on purpose.
72
+ *
73
+ * A column array holds columns of DIFFERENT value types, so it is written
74
+ * `TableColumn<Invoice>[]`. With `V = unknown` that annotation means
75
+ * `TableColumn<Invoice, unknown>`, and every `accessor('amount', …)` — which
76
+ * infers `V = number` — is rejected, because `format(value: number)` cannot
77
+ * accept an `unknown` parameter. `any` keeps the inferred type at the call site
78
+ * (`accessor('amount').format` still gets `number`) while letting the array
79
+ * annotation erase it.
80
+ */
81
+ type TableColumnAccessor<T, V = any> = (keyof T & string) | ((row: T) => V);
82
+ /** How a cell's text behaves when it does not fit. See the text section. */
83
+ type TextOverflow = 'nowrap' | 'wrap' | 'clamp';
84
+ interface TableColumn<T, V = any> {
85
+ readonly id: string;
86
+ readonly header?: string;
87
+ readonly accessor?: TableColumnAccessor<T, V>;
88
+ readonly sortable?: boolean;
89
+ readonly width?: string;
90
+ /** 0 = fixed; > 0 absorbs free space. */
91
+ readonly flex?: number;
92
+ readonly pinned?: 'start' | 'end';
93
+ readonly align?: 'start' | 'center' | 'end';
94
+ readonly format?: (value: V, row: T, locale: string) => string;
95
+ readonly comparator?: (a: V, b: V) => number;
96
+ readonly hidden?: boolean;
97
+ /** Per column, because a note column and an id column need different rules. */
98
+ readonly textOverflow?: TextOverflow;
99
+ /** Only meaningful with `textOverflow: 'clamp'`. Defaults to 2. */
100
+ readonly lines?: number;
101
+ /** Row-span sugar: merge consecutive rows whose key is equal. */
102
+ readonly merge?: 'equal' | ((row: T, anchor: T) => boolean);
103
+ /** Column-span sugar: how many columns this cell covers. */
104
+ readonly colSpan?: number | ((context: SpanContext<T>) => number);
105
+ /** Escape hatch, both axes. Cannot be combined with `merge` / `colSpan`. */
106
+ readonly span?: (context: SpanContext<T>) => CellSpan | null | undefined;
107
+ }
108
+ /** A column definition without the two fields `tableColumn()` supplies. */
109
+ type ColumnDef<T, V = unknown> = Omit<TableColumn<T, V>, 'id' | 'accessor'>;
110
+ /** Server mode: the app fetches, the table only describes what it needs. */
111
+ interface TableRequest {
112
+ readonly page: number;
113
+ readonly pageSize: number;
114
+ readonly sort: readonly SortState[];
115
+ readonly filters: readonly FilterState[];
116
+ readonly search: string;
117
+ }
118
+ /** The serialisable slice of the table: what a URL or a saved view holds. */
119
+ interface TableState {
120
+ readonly sort: readonly SortState[];
121
+ readonly filters: readonly FilterState[];
122
+ readonly search: string;
123
+ readonly page: number;
124
+ readonly pageSize: number;
125
+ /** Column ids in display order. */
126
+ readonly order: readonly string[];
127
+ /** Column ids the user hid. */
128
+ readonly hidden: readonly string[];
129
+ /** Pinned column ids on the start edge. */
130
+ readonly pinnedStart: readonly string[];
131
+ /** Pinned column ids on the end edge. */
132
+ readonly pinnedEnd: readonly string[];
133
+ /** Row ids, and the ids explicitly excluded from the set. */
134
+ readonly selected: readonly TableRowId[];
135
+ readonly excluded: readonly TableRowId[];
136
+ }
137
+ /**
138
+ * Where span diagnostics go. A `warn` sink is injected rather than hard-coded
139
+ * so a consumer can route the messages through its own logger, and a test can
140
+ * assert them without touching the console.
141
+ */
142
+ interface SpanWarnOptions {
143
+ readonly warn?: (message: string) => void;
144
+ }
145
+ /**
146
+ * `buildSpanModel` options.
147
+ *
148
+ * `strict` exists because the same builder serves two callers with opposite
149
+ * needs: `createTable()` validates its column list ONCE and may throw (a
150
+ * conflicting declaration is a bug in code, not in data), while a render-time
151
+ * rebuild must never throw — an app that swaps its columns at runtime would
152
+ * otherwise blank the grid. Strict mode is the documented default; the
153
+ * controller passes `strict: false` and lets `span` win, as the design says.
154
+ */
155
+ interface SpanBuildOptions extends SpanWarnOptions {
156
+ /**
157
+ * Throw when a column declares `span` together with `merge`/`colSpan`.
158
+ * @default true
159
+ */
160
+ readonly strict?: boolean;
161
+ }
162
+ /**
163
+ * Validates a column list without touching any data. `createTable()` calls
164
+ * this at construction so a conflicting declaration fails immediately — with
165
+ * an actionable message — instead of at the first paint, and in production
166
+ * too, because it is a defect in the column list rather than in the data.
167
+ */
168
+ export declare function assertSpanColumns<T>(columns: readonly TableColumn<T>[], options?: SpanWarnOptions): void;
169
+ /**
170
+ * Resolves `span` / `merge` / `colSpan` into the concrete span grid of the
171
+ * given rows and columns.
172
+ *
173
+ * Semantics (ported from the validated prototype, 35/35 contract checks):
174
+ * - only anchors exist; a covered position has no cell and, in the UI, no DOM;
175
+ * - first claim wins: an overlapping later span is shrunk (width, then height)
176
+ * and reported, so a configuration hazard is never silent;
177
+ * - row spans stop at the page (a page is a DOM boundary) and column spans at
178
+ * the visible range and at the pinned-region boundary;
179
+ * - `Infinity` means "the rest of my region", resolved before integer checks;
180
+ * - `0`, negatives, `NaN` and fractions are clamped to 1 with a warning, so a
181
+ * bad return value can never delete a cell;
182
+ * - rows must already be the rendered order: a merge follows adjacency, so
183
+ * sorting legitimately starts a new run.
184
+ *
185
+ * The model is a dense matrix of owner indices, so `at()` / `owner()` are O(1)
186
+ * and `move()` walks only the cells it skips.
187
+ */
188
+ export declare function buildSpanModel<T>(rows: readonly TableRow<T>[], columns: readonly TableColumn<T>[], options?: SpanBuildOptions): SpanModel;
189
+ /**
190
+ * The value of one cell, resolved through the column's accessor.
191
+ *
192
+ * The accessor is either a key or a function (the shared contract allows
193
+ * both), so every consumer — the pipeline, the span engine, a custom renderer
194
+ * — reads a cell the same way. A display/action column has no accessor and
195
+ * therefore no value; `undefined` is the honest answer, not an error.
196
+ */
197
+ export declare function readCellValue<T, V = unknown>(column: TableColumn<T, V>, row: TableRow<T>): V | undefined;
198
+ /**
199
+ * A range filter value. Bounds are inclusive and either side may be open, so
200
+ * one shape covers "from", "to" and "between" for both numbers and dates.
201
+ *
202
+ * Numbers and dates share it on purpose: a date bound is compared as a
203
+ * timestamp and a numeric bound as a number, which is exactly what a date
204
+ * input and a number input produce in the UI.
205
+ */
206
+ interface TableFilterRange {
207
+ readonly min?: number | Date | null;
208
+ readonly max?: number | Date | null;
209
+ }
210
+ /** The result of `paginate`, with the display range the footer needs. */
211
+ interface TablePage<T> {
212
+ readonly pageRows: readonly TableRow<T>[];
213
+ /** Rows before pagination. */
214
+ readonly total: number;
215
+ /** `ceil(total / pageSize)`; `0` when there is no row. */
216
+ readonly pageCount: number;
217
+ /** 1-based index of the first row on this page; `0` when the page is empty. */
218
+ readonly start: number;
219
+ /** 1-based index of the last row on this page; `0` when the page is empty. */
220
+ readonly end: number;
221
+ }
222
+ /**
223
+ * Keeps the rows that satisfy EVERY active filter.
224
+ *
225
+ * A filter on a column that is hidden, unknown, or has no accessor is ignored
226
+ * rather than treated as "match nothing": a saved view that references a
227
+ * removed column should lose that constraint, not empty the table.
228
+ */
229
+ export declare function applyFilters<T>(rows: readonly TableRow<T>[], columns: readonly TableColumn<T>[], filters: readonly FilterState[]): readonly TableRow<T>[];
230
+ /**
231
+ * Global search across every column that has a value.
232
+ *
233
+ * Display columns are skipped (they hold no data), and the needle is
234
+ * normalized with the same Arabic rules as the filters, so search finds a name
235
+ * written with or without diacritics. An empty search returns the input array
236
+ * unchanged — identity matters, because the controller's `computed` chain
237
+ * uses it to avoid downstream work.
238
+ */
239
+ export declare function applySearch<T>(rows: readonly TableRow<T>[], columns: readonly TableColumn<T>[], search: string): readonly TableRow<T>[];
240
+ /**
241
+ * Multi-level, stable sort with nulls LAST in both directions.
242
+ *
243
+ * Two decisions worth stating: a missing value always sorts to the end (a
244
+ * blank salary at the top of an ascending list is noise, and flipping it with
245
+ * the direction would put it at the top of a descending one too), and the
246
+ * pre-sort index is the final tie-break, so equal keys keep their order even
247
+ * on an engine whose sort is not stable.
248
+ *
249
+ * Sort levels that reference a hidden, unknown or accessor-less column are
250
+ * skipped rather than applied as a no-op, so `sorting` never lies about what
251
+ * is active.
252
+ */
253
+ export declare function applySort<T>(rows: readonly TableRow<T>[], columns: readonly TableColumn<T>[], sorting: readonly SortState[], locale?: string): readonly TableRow<T>[];
254
+ /**
255
+ * Slices one page and reports the footer's numbers.
256
+ *
257
+ * `start` / `end` are 1-based and inclusive ("1-25 of 12,345"), and both are 0
258
+ * when the page holds nothing — a page past the end is legal and returns an
259
+ * empty page, it is not silently clamped to the last one (clamping would hide
260
+ * a controller bug and lie about the requested state).
261
+ *
262
+ * A non-positive / non-finite `pageSize` means "no pagination": every row on
263
+ * one page. That is a deliberate degradation rather than a crash.
264
+ */
265
+ export declare function paginate<T>(rows: readonly TableRow<T>[], page: number, pageSize: number): TablePage<T>;
266
+ /**
267
+ * The two ways a column is declared. `accessor` is a data column (it has a
268
+ * value to sort, filter and span by); `display` is a rendering-only column
269
+ * such as an action or a status chip.
270
+ */
271
+ interface TableColumnFactory<T> {
272
+ /**
273
+ * A data column bound to a key of the row.
274
+ *
275
+ * The id defaults to the key and the header to the id, so a column is a
276
+ * one-liner until it needs to say something else — and the id stays the key
277
+ * unless the definition overrides it... it cannot: `ColumnDef` omits `id`,
278
+ * which keeps `state`, saved views and template slots referring to the same
279
+ * string the data is bound to.
280
+ */
281
+ accessor<K extends keyof T & string>(key: K, definition?: ColumnDef<T, T[K]>): TableColumn<T, T[K]>;
282
+ /**
283
+ * A column with no accessor: a template, an action, a computed badge.
284
+ *
285
+ * It still has an id (that is what `rvTableCell="status"` and the state
286
+ * round-trip refer to), but no value, so sorting and filtering skip it.
287
+ */
288
+ display(id: string, definition?: ColumnDef<T, unknown>): TableColumn<T>;
289
+ }
290
+ /**
291
+ * Typed column factory: `tableColumn<Employee>()`.
292
+ *
293
+ * The factory exists instead of an object literal so `accessor('salary')` is
294
+ * checked against the row type (`keyof T`) and the value type (`T[K]`) at the
295
+ * call site — a misspelled key is a compile error, not an empty column. The
296
+ * returned columns are frozen: a column list is configuration, and a mutation
297
+ * after `createTable()` would silently disagree with the cached signals.
298
+ */
299
+ export declare function tableColumn<T>(): TableColumnFactory<T>;
300
+ /** How many rows a table can select. */
301
+ type TableSelectionKind = 'none' | 'single' | 'multi';
302
+ /**
303
+ * The shape of the selection set (not how many rows may be selected).
304
+ *
305
+ * `include` lists the selected ids; `exclude` means "everything the current
306
+ * rows contain, minus these". The second form is what makes "select all 12,345
307
+ * matching rows" representable without materializing 12,345 ids, and it is why
308
+ * a server can be sent `{ all: true, except: [...] }` instead.
309
+ */
310
+ type TableSelectionMode = 'include' | 'exclude';
311
+ /** Which rows "select all" covers. */
312
+ type TableSelectionScope = 'page' | 'filtered' | 'all';
313
+ /** Selection configuration. Absent means selection is off. */
314
+ interface TableSelectionOptions {
315
+ /**
316
+ * `'none'` renders no selection affordance and makes every method a no-op.
317
+ * @default 'none'
318
+ */
319
+ readonly mode?: TableSelectionKind;
320
+ /**
321
+ * Scope used by `selectAll()` when the caller does not pass one.
322
+ * @default 'filtered'
323
+ */
324
+ readonly selectAllScope?: TableSelectionScope;
325
+ }
326
+ /**
327
+ * Server-mode loader shape. Declared so the public contract is stable, but
328
+ * NOT wired in 0.1.0: the client-only build never fetches.
329
+ */
330
+ interface TableServerOptions<T> {
331
+ readonly load: (request: TableRequest, context: {
332
+ readonly abortSignal: AbortSignal;
333
+ }) => Promise<{
334
+ readonly items: readonly T[];
335
+ readonly total: number;
336
+ }>;
337
+ }
338
+ /**
339
+ * Everything `createTable()` needs.
340
+ *
341
+ * It is a FUNCTION, not an object, because every read must happen inside the
342
+ * controller's `computed`s: passing `data: () => this.api.rows()` (a signal
343
+ * read) is what makes the row model react to new data without a subscription
344
+ * or a `reload()` call.
345
+ */
346
+ interface TableOptions<T> {
347
+ /** The declared columns, in display order. */
348
+ readonly columns: () => readonly TableColumn<T>[];
349
+ /** Client mode: the whole data set. */
350
+ readonly data?: () => readonly T[];
351
+ /** Server mode: declared only, not wired in 0.1.0 (the table warns). */
352
+ readonly server?: TableServerOptions<T>;
353
+ /**
354
+ * Row identity. Defaults to `row.id`, then a per-object generated id kept in
355
+ * a `WeakMap`. Never the array index: an index changes under sorting and
356
+ * filtering, and would silently break selection and focus.
357
+ */
358
+ readonly getRowId?: (row: T) => TableRowId;
359
+ /** Locale for collation and formatting; read lazily so it can be a signal. */
360
+ readonly locale?: () => string;
361
+ /** Selection configuration; absent = selection off. */
362
+ readonly selection?: TableSelectionOptions;
363
+ /**
364
+ * Rows per page. `initialState.pageSize` — a restored view — wins over it, so
365
+ * a saved state is never silently replaced by the default.
366
+ * @default 25
367
+ */
368
+ readonly pageSize?: number;
369
+ /** The saved view the table starts from. */
370
+ readonly initialState?: Partial<TableState>;
371
+ /** Skip the client filter/search stage (the caller filtered the data). */
372
+ readonly manualFiltering?: boolean;
373
+ /** Skip the client sort stage (the caller sorted the data). */
374
+ readonly manualSorting?: boolean;
375
+ /** Skip the client page stage (the caller paged the data). */
376
+ readonly manualPagination?: boolean;
377
+ /** Total row count when the data comes from outside. */
378
+ readonly rowCount?: () => number;
379
+ /** Diagnostic sink for span/clamp messages. Defaults to dev-mode console. */
380
+ readonly warn?: (message: string) => void;
381
+ }
382
+ /**
383
+ * Row selection as the UI sees it.
384
+ *
385
+ * Reads are signals so a header checkbox and every row cell update together;
386
+ * writes are methods because they must normalize the include/exclude mode and
387
+ * respect `mode: 'none' | 'single'`.
388
+ */
389
+ interface RiviniaTableSelection {
390
+ /** True when selection is enabled at all (the UI renders no box otherwise). */
391
+ readonly enabled: boolean;
392
+ /** True for multi-select; false for single-select (checkbox vs radio). */
393
+ readonly multiple: boolean;
394
+ /** Include or exclude set. */
395
+ readonly mode: Signal<TableSelectionMode>;
396
+ /** Selected ids, in row order, limited to the rows the table currently holds. */
397
+ readonly ids: Signal<readonly TableRowId[]>;
398
+ /** `ids().length`. */
399
+ readonly count: Signal<number>;
400
+ select(id: TableRowId): void;
401
+ deselect(id: TableRowId): void;
402
+ toggle(id: TableRowId): void;
403
+ /** Clears the selection (including "select all", in every mode). */
404
+ clear(): void;
405
+ isSelected(id: TableRowId): boolean;
406
+ /** Adds every row of the current page; in exclude mode it re-includes them. */
407
+ selectAllOnPage(): void;
408
+ /**
409
+ * `'page'` selects the page; `'filtered'`/`'all'` switch to exclude mode
410
+ * with nothing excluded, i.e. every matching row (and any future one).
411
+ */
412
+ selectAll(scope?: TableSelectionScope): void;
413
+ }
414
+ /**
415
+ * The table, as a chain of `computed`s — one per pipeline stage.
416
+ *
417
+ * `data -> rows (filter, search, sort) -> pageRows -> spans` means Angular's
418
+ * own caching gives incremental recomputation for free: a page change does not
419
+ * re-sort, a sort does not re-filter, and selection/scroll never rebuild the
420
+ * span matrix because nothing in that chain reads them.
421
+ *
422
+ * The controller owns no fetching and no DOM. It is constructed outside the
423
+ * injector graph (`createTable` needs no provider), which is what lets a
424
+ * consumer build one inside a component field initializer or inside a test.
425
+ */
426
+ export declare class RiviniaTable<T> {
427
+ /** The declared columns, hidden ones included, in display order. */
428
+ readonly columns: Signal<readonly TableColumn<T>[]>;
429
+ /** The columns that render: `columns` without `hidden: true`. */
430
+ readonly visibleColumns: Signal<readonly TableColumn<T>[]>;
431
+ /** Filtered and sorted rows, numbered from 0. */
432
+ readonly rows: Signal<readonly TableRow<T>[]>;
433
+ /** The current page of `rows`. */
434
+ readonly pageRows: Signal<readonly TableRow<T>[]>;
435
+ /** The resolved span grid over `pageRows` x `visibleColumns`. */
436
+ readonly spans: Signal<SpanModel>;
437
+ /** Client row count, or `rowCount()` when the data comes from outside. */
438
+ readonly total: Signal<number>;
439
+ /** `ceil(total / pageSize)`; `0` for an empty table. */
440
+ readonly pageCount: Signal<number>;
441
+ /** Ordered multi-sort; index 0 is the primary level. */
442
+ readonly sorting: WritableSignal<readonly SortState[]>;
443
+ /** Active column filters, ANDed together. */
444
+ readonly filters: WritableSignal<readonly FilterState[]>;
445
+ /** Global search across every data column. */
446
+ readonly globalSearch: WritableSignal<string>;
447
+ /**
448
+ * 0-based page. A linked signal: it returns to 0 when the filter, sort or
449
+ * search changes (the result set changed under the user), while an explicit
450
+ * `set()` still wins until the next such change.
451
+ */
452
+ readonly page: WritableSignal<number>;
453
+ readonly pageSize: WritableSignal<number>;
454
+ readonly selection: RiviniaTableSelection;
455
+ /** What a server-mode fetch would need; `data`-driven tables ignore it. */
456
+ readonly request: Signal<TableRequest>;
457
+ /** The serializable slice: a saved view, a URL, a test assertion. */
458
+ readonly state: Signal<TableState>;
459
+ private readonly options;
460
+ private readonly initialState;
461
+ /** What `resetState()` returns to: the restored size, else the option. */
462
+ private readonly defaultPageSize;
463
+ private readonly warn;
464
+ private readonly warned;
465
+ private readonly generatedIds;
466
+ private readonly selectionImpl;
467
+ private generatedIdSeq;
468
+ constructor(options: () => TableOptions<T>);
469
+ /**
470
+ * Applies a saved view. Unknown fields are ignored on purpose: column order,
471
+ * visibility and pinning are owned by the column list in 0.1.0, so a state
472
+ * that carries them round-trips unchanged without pretending the controller
473
+ * can reorder the consumer's columns.
474
+ */
475
+ setState(patch: Partial<TableState>): void;
476
+ /** Returns to `initialState`, including the initial page and selection. */
477
+ resetState(): void;
478
+ /**
479
+ * `getRowId` -> `row.id` -> a per-object generated id.
480
+ *
481
+ * A primitive row has no identity of its own, so equal primitives collapse
482
+ * into one id; that is documented, and `getRowId` is the answer.
483
+ */
484
+ private readonly defaultRowId;
485
+ }
486
+ /**
487
+ * Creates a table from a reactive options function.
488
+ *
489
+ * No DI, no injection context, no HTTP: the caller passes `data` as a signal
490
+ * read and gets derived signals back, so the table is usable in a component
491
+ * field initializer, in a test, or in a headless script.
492
+ */
493
+ export declare function createTable<T>(options: () => TableOptions<T>): RiviniaTable<T>;
494
+ export type { CellSpan, ColumnDef, FilterState, RiviniaTableSelection, SortDirection, SortState, SpanBuildOptions, SpanCell, SpanContext, SpanModel, SpanWarnOptions, TableColumn, TableColumnAccessor, TableColumnFactory, TableOptions, TablePage, TableRequest, TableRow, TableRowId, TableSelectionKind, TableSelectionMode, TableSelectionOptions, TableSelectionScope, TableServerOptions, TableState, TextOverflow };