@rivinia/table 0.0.0-stage → 1.0.0

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