@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.
- package/README.md +272 -2
- package/fesm2022/rivinia-table-ui.mjs +867 -0
- package/fesm2022/rivinia-table-ui.mjs.map +1 -0
- package/fesm2022/rivinia-table.mjs +783 -0
- package/fesm2022/rivinia-table.mjs.map +1 -0
- package/package.json +35 -3
- package/types/rivinia-table-ui.d.ts +514 -0
- package/types/rivinia-table.d.ts +494 -0
|
@@ -0,0 +1,514 @@
|
|
|
1
|
+
import { CellSpan, SortDirection, SortState, SpanModel, TableRow, TableRowId, TextOverflow } from "@rivinia/table";
|
|
2
|
+
import * as i0 from "@angular/core";
|
|
3
|
+
import { TemplateRef } from "@angular/core";
|
|
4
|
+
/**
|
|
5
|
+
* The column shape the grid READS — a renderer-facing contract, not a copy of
|
|
6
|
+
* the core type.
|
|
7
|
+
*
|
|
8
|
+
* Why not `TableColumn<any>`: that looks like the obvious "some column" type,
|
|
9
|
+
* but it is not a supertype of a real one. Its accessor is
|
|
10
|
+
* `(keyof any & string) | ((row: any) => V)` = `string | …`, and a concrete
|
|
11
|
+
* `keyof Person & string` is narrower, so `TableColumn<Person>` would NOT be
|
|
12
|
+
* assignable to `TableColumn<any>` — a consumer could not pass their own
|
|
13
|
+
* typed columns at all. The same inversion repeats through `SpanContext`.
|
|
14
|
+
*
|
|
15
|
+
* So the callbacks here take `any` as their PARAMETER type (always assignable
|
|
16
|
+
* in both directions) instead of `any` as the type argument. The result is a
|
|
17
|
+
* genuine supertype: every `TableColumn<T>` is assignable to it.
|
|
18
|
+
*
|
|
19
|
+
* The renderer reads `id`, `header`, `accessor`, `format`, `sortable`,
|
|
20
|
+
* `width`, `flex`, `align`, `hidden`, `textOverflow` and `lines`. The rest is
|
|
21
|
+
* declared so a column literal typed as `TableUiColumn[]` can carry the full
|
|
22
|
+
* core vocabulary without an excess-property error: spans are rendered from
|
|
23
|
+
* `[spans]` (not from the column), `comparator` belongs to the core's sorting,
|
|
24
|
+
* and pinning is not implemented by this entry point.
|
|
25
|
+
*/
|
|
26
|
+
interface TableUiColumn {
|
|
27
|
+
readonly id: string;
|
|
28
|
+
readonly header?: string;
|
|
29
|
+
readonly accessor?: string | ((row: any) => unknown);
|
|
30
|
+
readonly sortable?: boolean;
|
|
31
|
+
readonly width?: string;
|
|
32
|
+
/** 0 = fixed; > 0 absorbs free space. */
|
|
33
|
+
readonly flex?: number;
|
|
34
|
+
readonly pinned?: 'start' | 'end';
|
|
35
|
+
readonly align?: 'start' | 'center' | 'end';
|
|
36
|
+
readonly format?: (value: any, row: any, locale: string) => string;
|
|
37
|
+
readonly comparator?: (a: any, b: any) => number;
|
|
38
|
+
readonly hidden?: boolean;
|
|
39
|
+
readonly textOverflow?: TextOverflow;
|
|
40
|
+
readonly lines?: number;
|
|
41
|
+
readonly merge?: 'equal' | ((row: any, anchor: any) => boolean);
|
|
42
|
+
readonly colSpan?: number | ((context: any) => number);
|
|
43
|
+
readonly span?: (context: any) => CellSpan | null | undefined;
|
|
44
|
+
}
|
|
45
|
+
/** A row as the grid reads it. `TableRow<T>` has no variance, so this is safe. */
|
|
46
|
+
type TableUiRow = TableRow<any>;
|
|
47
|
+
/** Row height + padding preset. Token values per preset are in the CSS. */
|
|
48
|
+
type TableUiDensity = 'compact' | 'default' | 'comfortable';
|
|
49
|
+
/**
|
|
50
|
+
* The text a grid shows on its own. Everything here is also a `[labels]`
|
|
51
|
+
* override, so an Arabic page gets Arabic text without a second code path.
|
|
52
|
+
*/
|
|
53
|
+
interface TableUiLabels {
|
|
54
|
+
/** The grid's accessible name when `[ariaLabel]` is unset. */
|
|
55
|
+
readonly grid: string;
|
|
56
|
+
/** Announced while `[loading]` is true (the skeletons are decorative). */
|
|
57
|
+
readonly loading: string;
|
|
58
|
+
/** Fallback when `[error]` holds something that is not an `Error`/string. */
|
|
59
|
+
readonly error: string;
|
|
60
|
+
/** Used when `[emptyMessage]` is unset. */
|
|
61
|
+
readonly empty: string;
|
|
62
|
+
/** The grid's `aria-describedby`: what the keyboard does. */
|
|
63
|
+
readonly keyboardHelp: string;
|
|
64
|
+
}
|
|
65
|
+
export declare const TABLE_UI_LABELS_EN: TableUiLabels;
|
|
66
|
+
export declare const TABLE_UI_LABELS_AR: TableUiLabels;
|
|
67
|
+
/**
|
|
68
|
+
* The label set for a locale, defaulting to English.
|
|
69
|
+
*
|
|
70
|
+
* It is a plain function (not a DI token) because it is pure and the UI entry
|
|
71
|
+
* must stay usable without a provider — the same rule the core follows.
|
|
72
|
+
*/
|
|
73
|
+
declare function tableUiLabels(locale: string | undefined): TableUiLabels;
|
|
74
|
+
/**
|
|
75
|
+
* What a projected `[rvTableCell]` template receives.
|
|
76
|
+
*
|
|
77
|
+
* `$implicit` is the {@link TableRow} wrapper — the same shape the `rows`
|
|
78
|
+
* input holds — so `let-row` is what the grid itself sees. `data` is the
|
|
79
|
+
* consumer's own object, `value` is the accessor's resolved value BEFORE
|
|
80
|
+
* `format`, and `column` is the column definition. The value is untrusted
|
|
81
|
+
* (`unknown`) because the grid does not know the column's value type.
|
|
82
|
+
*
|
|
83
|
+
* ```html
|
|
84
|
+
* <ng-template rvTableCell="actions" let-row let-data="data">
|
|
85
|
+
* <button (click)="open(row.id)">Open {{ data.number }}</button>
|
|
86
|
+
* </ng-template>
|
|
87
|
+
* ```
|
|
88
|
+
*/
|
|
89
|
+
interface TableUiCellContext<T = unknown> {
|
|
90
|
+
readonly $implicit: TableRow<T>;
|
|
91
|
+
/** The same row under a named key, for templates that already destructure. */
|
|
92
|
+
readonly row: TableRow<T>;
|
|
93
|
+
/** The consumer's own object (`row.data`). */
|
|
94
|
+
readonly data: T;
|
|
95
|
+
/** The accessor's value before `column.format`. */
|
|
96
|
+
readonly value: unknown;
|
|
97
|
+
/** The renderer-facing column definition the grid rendered. */
|
|
98
|
+
readonly column: TableUiColumn;
|
|
99
|
+
/** Position inside the rendered page, not the index in the full data set. */
|
|
100
|
+
readonly rowIndex: number;
|
|
101
|
+
}
|
|
102
|
+
/** Payload of `(selectionChange)`: the new set AND which row moved it. */
|
|
103
|
+
interface TableUiSelectionChange<T = unknown> {
|
|
104
|
+
readonly selected: ReadonlySet<TableRowId>;
|
|
105
|
+
/** `null` when no single row caused the change (programmatic or clear-all). */
|
|
106
|
+
readonly row: TableRow<T> | null;
|
|
107
|
+
}
|
|
108
|
+
/** One cell as the template sees it — everything resolved, nothing computed there. */
|
|
109
|
+
interface RenderCell {
|
|
110
|
+
readonly column: TableUiColumn;
|
|
111
|
+
readonly columnIndex: number;
|
|
112
|
+
readonly rowSpan: number;
|
|
113
|
+
readonly colSpan: number;
|
|
114
|
+
readonly spanned: boolean;
|
|
115
|
+
readonly overflow: TextOverflow;
|
|
116
|
+
/** `null` unless the column is a clamp column; drives `--rv-table-clamp-lines`. */
|
|
117
|
+
readonly lines: number | null;
|
|
118
|
+
readonly text: string;
|
|
119
|
+
/** Id of the visually-hidden full-text node, when the cell can be cut off. */
|
|
120
|
+
readonly describedBy: string | null;
|
|
121
|
+
readonly template: TemplateRef<TableUiCellContext> | null;
|
|
122
|
+
readonly context: TableUiCellContext;
|
|
123
|
+
/** Explicit placement, so covered positions leave no auto-flow hole. */
|
|
124
|
+
readonly gridRow: string;
|
|
125
|
+
readonly gridColumn: string;
|
|
126
|
+
}
|
|
127
|
+
interface RenderRow {
|
|
128
|
+
/** The effective row id (`[rowKey]` applied): what selection and tracking use. */
|
|
129
|
+
readonly id: TableRowId;
|
|
130
|
+
readonly row: TableUiRow;
|
|
131
|
+
/** Position inside the rendered page. */
|
|
132
|
+
readonly rowIndex: number;
|
|
133
|
+
/** 1-based position in the FULL data set; the header is row 1. */
|
|
134
|
+
readonly ariaRowIndex: number;
|
|
135
|
+
readonly selected: boolean;
|
|
136
|
+
readonly cells: readonly RenderCell[];
|
|
137
|
+
}
|
|
138
|
+
interface TableUiState {
|
|
139
|
+
readonly kind: 'error' | 'empty';
|
|
140
|
+
readonly message: string;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* `<rv-table>` — a `role="grid"` data grid, built from divs on purpose.
|
|
144
|
+
*
|
|
145
|
+
* ## Why not `<table>`
|
|
146
|
+
* An interactive grid is `role="grid"`, not `role="table"`, so the roles have
|
|
147
|
+
* to be written either way. Once they are, a native table adds layout
|
|
148
|
+
* negotiation (`table-layout`), inconsistent sticky headers and no way to span
|
|
149
|
+
* a cell across ROWS without owning the row structure. Divs + CSS grid keep
|
|
150
|
+
* the geometry explicit and the ARIA exact.
|
|
151
|
+
*
|
|
152
|
+
* ## The consumer decides what a cell contains
|
|
153
|
+
* The column list carries geometry and a header; the CELL is either the
|
|
154
|
+
* column's `accessor` (+ optional `format`) or — for an actions column, a
|
|
155
|
+
* badge, an avatar — a projected template keyed by column id:
|
|
156
|
+
*
|
|
157
|
+
* ```html
|
|
158
|
+
* <rv-table [columns]="columns()" [rows]="rows()" [spans]="spans()" selectable>
|
|
159
|
+
* <ng-template rvTableCell="actions" let-row let-data="data">
|
|
160
|
+
* <button type="button" (click)="edit(row.id)">Edit {{ data.number }}</button>
|
|
161
|
+
* </ng-template>
|
|
162
|
+
* </rv-table>
|
|
163
|
+
* ```
|
|
164
|
+
*
|
|
165
|
+
* The table never knows that "actions" means actions: a column with an id and
|
|
166
|
+
* no `accessor` renders only the projected template. A template also WINS over
|
|
167
|
+
* an accessor when both exist — markup is an override, not a second source of
|
|
168
|
+
* truth for the column list (`[columns]` stays the only one).
|
|
169
|
+
*
|
|
170
|
+
* ## Spans
|
|
171
|
+
* `[spans]` is the core's resolved `SpanModel`: only anchors are rendered,
|
|
172
|
+
* covered positions have no element at all (nothing is hidden with a flag), and
|
|
173
|
+
* each anchor is placed on the shared CSS grid with an explicit
|
|
174
|
+
* `grid-row: <start> / span <n>` and `grid-column: <start> / span <n>`. Because
|
|
175
|
+
* the spans are real grid spans, a merged cell survives wrapping rows and
|
|
176
|
+
* needs no height arithmetic. `aria-rowspan` / `aria-colspan` are written only
|
|
177
|
+
* when greater than 1, and every cell carries `aria-colindex` (WAI-ARIA
|
|
178
|
+
* requires it once the grid is not a simple matrix).
|
|
179
|
+
*
|
|
180
|
+
* ## Keyboard
|
|
181
|
+
* One tab stop (roving `tabindex`), moved with the arrows through
|
|
182
|
+
* `spans.move()` — so moving INTO a merged cell lands on its anchor and moving
|
|
183
|
+
* inside one leaves it. Home/End pick the first/last anchor of the row,
|
|
184
|
+
* PageUp/PageDown move a screen, Enter activates the row, Space toggles
|
|
185
|
+
* selection. Horizontal arrows mirror in RTL; vertical ones never do.
|
|
186
|
+
*
|
|
187
|
+
* ## Theming
|
|
188
|
+
* Every colour and shape is read AT THE USE SITE as
|
|
189
|
+
* `var(--rv-table-<part>, var(--rv-<semantic>, <default>))`, so one
|
|
190
|
+
* `:root { --rv-bg: … }` reaches the grid. Nothing is declared on the host, so
|
|
191
|
+
* a token set anywhere on an ancestor wins (the D42/D46 rule).
|
|
192
|
+
* `[density]` only changes the FALLBACK of the size tokens, so an explicit
|
|
193
|
+
* token still wins over the preset.
|
|
194
|
+
*
|
|
195
|
+
* The override surface is documented and stable:
|
|
196
|
+
* classes `.rv-table-scroller`, `.rv-table-grid`, `.rv-table-head`,
|
|
197
|
+
* `.rv-table-head-cell`, `.rv-table-head-button`, `.rv-table-head-label`,
|
|
198
|
+
* `.rv-table-body`, `.rv-table-row`, `.rv-table-cell`, `.rv-table-text`,
|
|
199
|
+
* `.rv-table-sort`, `.rv-table-sort-rank`, `.rv-table-state`,
|
|
200
|
+
* `.rv-table-skeleton`, `.rv-table-sr-only`;
|
|
201
|
+
* hooks `[data-density]`, `[data-loading]`, `[data-sticky]`, `[data-selectable]`
|
|
202
|
+
* on the host, `[data-sortable]`, `[data-sorted]`, `[data-sort-rank]`,
|
|
203
|
+
* `[data-sort-direction]` on a header cell, and `[data-selected]`,
|
|
204
|
+
* `[data-span]`, `[data-align]`, `[data-overflow]`, `[data-row]`, `[data-col]`
|
|
205
|
+
* on a body cell. Component styles are injected at runtime and carry the
|
|
206
|
+
* component's scope attribute, so a plain `.rv-table-cell { … }` in an app
|
|
207
|
+
* stylesheet can lose at equal specificity: scope it as
|
|
208
|
+
* `rv-table .rv-table-cell { … }`, or use the tokens.
|
|
209
|
+
*/
|
|
210
|
+
export declare class RiviniaTableComponent {
|
|
211
|
+
private readonly host;
|
|
212
|
+
private readonly scroller;
|
|
213
|
+
private readonly projectedCells;
|
|
214
|
+
/** The columns, in render order. Hidden ones are filtered out, not counted. */
|
|
215
|
+
readonly columns: import("@angular/core").InputSignal<readonly TableUiColumn[]>;
|
|
216
|
+
/** The page's rows (already filtered/sorted/paged by the consumer or core). */
|
|
217
|
+
readonly rows: import("@angular/core").InputSignal<readonly TableUiRow[]>;
|
|
218
|
+
/** The core's resolved span matrix for `rows` + visible columns. */
|
|
219
|
+
readonly spans: import("@angular/core").InputSignal<SpanModel | null>;
|
|
220
|
+
/** While true the grid is `aria-busy`, renders skeletons and no data rows. */
|
|
221
|
+
readonly loading: import("@angular/core").InputSignal<boolean>;
|
|
222
|
+
/**
|
|
223
|
+
* How many skeleton rows to draw. `'auto'` (default) mirrors the rows that
|
|
224
|
+
* are currently rendered, at least one — so the layout does not jump.
|
|
225
|
+
*/
|
|
226
|
+
readonly skeletonRows: import("@angular/core").InputSignal<number | "auto">;
|
|
227
|
+
/** A failed load. `null`/`undefined` means "no error". */
|
|
228
|
+
readonly error: import("@angular/core").InputSignal<unknown>;
|
|
229
|
+
/** Text of the empty state. Unset falls back to the locale's label. */
|
|
230
|
+
readonly emptyMessage: import("@angular/core").InputSignal<string | undefined>;
|
|
231
|
+
/**
|
|
232
|
+
* Derives the id used by selection, tracking and `(rowActivate)` from the
|
|
233
|
+
* row's data. Unset uses `TableRow.id`, which is already stable.
|
|
234
|
+
*/
|
|
235
|
+
readonly rowKey: import("@angular/core").InputSignal<((row: any) => TableRowId) | undefined>;
|
|
236
|
+
/** Renders row selection: `aria-selected`, Space, and click-to-select. */
|
|
237
|
+
readonly selectable: import("@angular/core").InputSignal<boolean>;
|
|
238
|
+
/**
|
|
239
|
+
* The selected row ids. Two-way: `[(selected)]`. A `Set` (not an array)
|
|
240
|
+
* because every row asks "am I selected?" on every render.
|
|
241
|
+
*/
|
|
242
|
+
readonly selected: import("@angular/core").ModelSignal<ReadonlySet<TableRowId>>;
|
|
243
|
+
/**
|
|
244
|
+
* The active sort, highest priority first. Two-way: `[(sort)]`, and the
|
|
245
|
+
* model's own `(sortChange)` fires whenever this component changes it.
|
|
246
|
+
*/
|
|
247
|
+
readonly sort: import("@angular/core").ModelSignal<readonly SortState[]>;
|
|
248
|
+
readonly density: import("@angular/core").InputSignal<TableUiDensity>;
|
|
249
|
+
/** Sticks the header to the top of the scroller. */
|
|
250
|
+
readonly stickyHeader: import("@angular/core").InputSignal<boolean>;
|
|
251
|
+
/**
|
|
252
|
+
* The number of rows in the FULL result set, for `aria-rowcount` when the
|
|
253
|
+
* consumer pages on the server. Unset derives it from the rows' `index`,
|
|
254
|
+
* which is exact when the grid renders the whole set.
|
|
255
|
+
*/
|
|
256
|
+
readonly totalRows: import("@angular/core").InputSignal<number | undefined>;
|
|
257
|
+
/** Accessible name of the grid (`aria-label`). */
|
|
258
|
+
readonly ariaLabel: import("@angular/core").InputSignal<string | undefined>;
|
|
259
|
+
/** Overrides for the built-in English/Arabic text. */
|
|
260
|
+
readonly labels: import("@angular/core").InputSignal<Partial<TableUiLabels> | undefined>;
|
|
261
|
+
/** Enter on a focused cell, or a double-click on a row. */
|
|
262
|
+
readonly rowActivate: import("@angular/core").OutputEmitterRef<TableUiRow>;
|
|
263
|
+
/**
|
|
264
|
+
* A row was selected/deselected by the user. The model already carries the
|
|
265
|
+
* new set (`selectedChange`); this output also names the row that moved.
|
|
266
|
+
*/
|
|
267
|
+
readonly selectionChange: import("@angular/core").OutputEmitterRef<TableUiSelectionChange<unknown>>;
|
|
268
|
+
protected readonly instructionsId: string;
|
|
269
|
+
private readonly uid;
|
|
270
|
+
protected readonly visibleColumns: import("@angular/core").Signal<TableUiColumn[]>;
|
|
271
|
+
protected readonly columnCount: import("@angular/core").Signal<number>;
|
|
272
|
+
/**
|
|
273
|
+
* The ONE column geometry both the header and the body read.
|
|
274
|
+
*
|
|
275
|
+
* A flex column keeps its declared width as the track's FLOOR
|
|
276
|
+
* (`minmax(<width>, Nfr)`, design §5.2): with `minmax(0, Nfr)` a narrow
|
|
277
|
+
* container would collapse the column to zero and its content would vanish
|
|
278
|
+
* instead of scrolling. Fixed columns are their width, untouched.
|
|
279
|
+
*/
|
|
280
|
+
protected readonly columnTemplate: import("@angular/core").Signal<string>;
|
|
281
|
+
/**
|
|
282
|
+
* The grid's intrinsic width, so the scroller actually scrolls instead of
|
|
283
|
+
* squeezing every column. It is the sum of the declared widths — which is
|
|
284
|
+
* why a column width should be a length (`px`, `rem`, `ch`), not `max-content`.
|
|
285
|
+
*/
|
|
286
|
+
protected readonly minWidth: import("@angular/core").Signal<string>;
|
|
287
|
+
/**
|
|
288
|
+
* `aria-label` written on the HOST — `<rv-table aria-label="Invoices">` — is
|
|
289
|
+
* the attribute a consumer reaches for first, but the `role="grid"` element
|
|
290
|
+
* lives INSIDE the component, so the attribute would otherwise stop at the
|
|
291
|
+
* host and the grid would keep its fallback name ("Data grid"). Read on every
|
|
292
|
+
* change detection: it is a `getAttribute` call, and a dynamic label belongs
|
|
293
|
+
* on the `[ariaLabel]` input, which is reactive.
|
|
294
|
+
*/
|
|
295
|
+
protected get hostLabel(): string | undefined;
|
|
296
|
+
protected readonly effectiveLabels: import("@angular/core").Signal<TableUiLabels>;
|
|
297
|
+
protected readonly locale: import("@angular/core").Signal<string>;
|
|
298
|
+
/** Column id → the consumer's template. Rebuilt only when content changes. */
|
|
299
|
+
private readonly cellTemplates;
|
|
300
|
+
/**
|
|
301
|
+
* Sort lookup by column id: direction plus 1-based priority. One map instead
|
|
302
|
+
* of a linear search per rendered header cell.
|
|
303
|
+
*/
|
|
304
|
+
private readonly sortIndex;
|
|
305
|
+
/** The fully resolved view model. Nothing in the template computes. */
|
|
306
|
+
protected readonly renderedRows: import("@angular/core").Signal<readonly RenderRow[]>;
|
|
307
|
+
protected readonly skeletonList: import("@angular/core").Signal<unknown[]>;
|
|
308
|
+
protected readonly state: import("@angular/core").Signal<TableUiState | null>;
|
|
309
|
+
/**
|
|
310
|
+
* The rows that actually have DOM cells. While loading the skeletons replace
|
|
311
|
+
* them, and an error/empty state replaces them too, so the keyboard must not
|
|
312
|
+
* "move" into a row that has no element to focus.
|
|
313
|
+
*/
|
|
314
|
+
protected readonly bodyRows: import("@angular/core").Signal<readonly RenderRow[]>;
|
|
315
|
+
/**
|
|
316
|
+
* The header row is row 1 (WAI-ARIA counts the header), so the grid's row
|
|
317
|
+
* count is the data count + 1. `-1` is legal ("unknown"), but a wrong count
|
|
318
|
+
* is worse than an honest one, so `[totalRows]` is the precise answer when
|
|
319
|
+
* the consumer pages on the server.
|
|
320
|
+
*/
|
|
321
|
+
protected readonly rowCount: import("@angular/core").Signal<number>;
|
|
322
|
+
/** Row index in the rendered page; `-1` is the header row. */
|
|
323
|
+
private readonly active;
|
|
324
|
+
private pendingFocus;
|
|
325
|
+
/**
|
|
326
|
+
* The active cell, clamped to what is actually rendered. A stale row index
|
|
327
|
+
* (the data shrank) would otherwise leave the grid with no tab stop at all.
|
|
328
|
+
* While loading there are no data cells, so the header is the tab stop.
|
|
329
|
+
*/
|
|
330
|
+
protected readonly effectiveActive: import("@angular/core").Signal<{
|
|
331
|
+
row: number;
|
|
332
|
+
column: number;
|
|
333
|
+
}>;
|
|
334
|
+
constructor();
|
|
335
|
+
protected isActive(row: number, column: number): boolean;
|
|
336
|
+
protected isSortable(column: TableUiColumn): boolean;
|
|
337
|
+
protected sortRank(column: TableUiColumn): number;
|
|
338
|
+
protected sortDirection(column: TableUiColumn): SortDirection | null;
|
|
339
|
+
/**
|
|
340
|
+
* `aria-sort` lives on exactly ONE header (the highest-priority sort), as
|
|
341
|
+
* WAI-ARIA requires; secondary sorted columns are shown with a rank badge
|
|
342
|
+
* and announce `none`, because the attribute has no "secondary" value.
|
|
343
|
+
*/
|
|
344
|
+
protected ariaSort(column: TableUiColumn): 'ascending' | 'descending' | 'none' | null;
|
|
345
|
+
protected onHeaderClick(column: TableUiColumn, columnIndex: number, event: MouseEvent): void;
|
|
346
|
+
protected rowId(row: TableUiRow): TableRowId;
|
|
347
|
+
protected onRowActivate(item: RenderRow): void;
|
|
348
|
+
/**
|
|
349
|
+
* A double-click is a row activation. It is ignored when it lands on an
|
|
350
|
+
* interactive element inside a projected template: the action the user asked
|
|
351
|
+
* for (Edit, Open, a link) must not also fire the row's own activation.
|
|
352
|
+
*/
|
|
353
|
+
protected onRowDoubleClick(item: RenderRow, event: MouseEvent): void;
|
|
354
|
+
protected onCellClick(item: RenderRow, cell: RenderCell, event: MouseEvent): void;
|
|
355
|
+
protected onCellFocus(row: number, column: number): void;
|
|
356
|
+
protected onKeydown(event: KeyboardEvent): void;
|
|
357
|
+
private onHeaderKey;
|
|
358
|
+
private cycleSort;
|
|
359
|
+
/**
|
|
360
|
+
* A page step stays inside the BODY. The header is reachable with ArrowUp,
|
|
361
|
+
* but PageUp from the first data row must not surprise the user by landing on
|
|
362
|
+
* the sort controls; page keys are a data-reading affordance.
|
|
363
|
+
*/
|
|
364
|
+
private page;
|
|
365
|
+
private moveBy;
|
|
366
|
+
/** Clamp to the page, then snap onto the anchor of whatever cell is there. */
|
|
367
|
+
private moveTo;
|
|
368
|
+
private firstColumn;
|
|
369
|
+
private lastColumn;
|
|
370
|
+
/** One screen minus one row; 10 when there is no layout to measure (SSR, jsdom). */
|
|
371
|
+
private pageRows;
|
|
372
|
+
private focusCell;
|
|
373
|
+
/**
|
|
374
|
+
* Direction is read from the nearest `[dir]` FIRST, then from computed
|
|
375
|
+
* style: jsdom does not inherit computed direction, and `dir="auto"` needs
|
|
376
|
+
* the computed answer anyway.
|
|
377
|
+
*/
|
|
378
|
+
private isRtl;
|
|
379
|
+
private buildCells;
|
|
380
|
+
private readValue;
|
|
381
|
+
private toggleRow;
|
|
382
|
+
private errorText;
|
|
383
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<RiviniaTableComponent, never>;
|
|
384
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<RiviniaTableComponent, "rv-table", never, {
|
|
385
|
+
"columns": {
|
|
386
|
+
"alias": "columns";
|
|
387
|
+
"required": true;
|
|
388
|
+
"isSignal": true;
|
|
389
|
+
};
|
|
390
|
+
"rows": {
|
|
391
|
+
"alias": "rows";
|
|
392
|
+
"required": false;
|
|
393
|
+
"isSignal": true;
|
|
394
|
+
};
|
|
395
|
+
"spans": {
|
|
396
|
+
"alias": "spans";
|
|
397
|
+
"required": false;
|
|
398
|
+
"isSignal": true;
|
|
399
|
+
};
|
|
400
|
+
"loading": {
|
|
401
|
+
"alias": "loading";
|
|
402
|
+
"required": false;
|
|
403
|
+
"isSignal": true;
|
|
404
|
+
};
|
|
405
|
+
"skeletonRows": {
|
|
406
|
+
"alias": "skeletonRows";
|
|
407
|
+
"required": false;
|
|
408
|
+
"isSignal": true;
|
|
409
|
+
};
|
|
410
|
+
"error": {
|
|
411
|
+
"alias": "error";
|
|
412
|
+
"required": false;
|
|
413
|
+
"isSignal": true;
|
|
414
|
+
};
|
|
415
|
+
"emptyMessage": {
|
|
416
|
+
"alias": "emptyMessage";
|
|
417
|
+
"required": false;
|
|
418
|
+
"isSignal": true;
|
|
419
|
+
};
|
|
420
|
+
"rowKey": {
|
|
421
|
+
"alias": "rowKey";
|
|
422
|
+
"required": false;
|
|
423
|
+
"isSignal": true;
|
|
424
|
+
};
|
|
425
|
+
"selectable": {
|
|
426
|
+
"alias": "selectable";
|
|
427
|
+
"required": false;
|
|
428
|
+
"isSignal": true;
|
|
429
|
+
};
|
|
430
|
+
"selected": {
|
|
431
|
+
"alias": "selected";
|
|
432
|
+
"required": false;
|
|
433
|
+
"isSignal": true;
|
|
434
|
+
};
|
|
435
|
+
"sort": {
|
|
436
|
+
"alias": "sort";
|
|
437
|
+
"required": false;
|
|
438
|
+
"isSignal": true;
|
|
439
|
+
};
|
|
440
|
+
"density": {
|
|
441
|
+
"alias": "density";
|
|
442
|
+
"required": false;
|
|
443
|
+
"isSignal": true;
|
|
444
|
+
};
|
|
445
|
+
"stickyHeader": {
|
|
446
|
+
"alias": "stickyHeader";
|
|
447
|
+
"required": false;
|
|
448
|
+
"isSignal": true;
|
|
449
|
+
};
|
|
450
|
+
"totalRows": {
|
|
451
|
+
"alias": "totalRows";
|
|
452
|
+
"required": false;
|
|
453
|
+
"isSignal": true;
|
|
454
|
+
};
|
|
455
|
+
"ariaLabel": {
|
|
456
|
+
"alias": "ariaLabel";
|
|
457
|
+
"required": false;
|
|
458
|
+
"isSignal": true;
|
|
459
|
+
};
|
|
460
|
+
"labels": {
|
|
461
|
+
"alias": "labels";
|
|
462
|
+
"required": false;
|
|
463
|
+
"isSignal": true;
|
|
464
|
+
};
|
|
465
|
+
}, {
|
|
466
|
+
"selected": "selectedChange";
|
|
467
|
+
"sort": "sortChange";
|
|
468
|
+
"rowActivate": "rowActivate";
|
|
469
|
+
"selectionChange": "selectionChange";
|
|
470
|
+
}, ["projectedCells"], ["[rvTableCell]"], true, never>;
|
|
471
|
+
}
|
|
472
|
+
/**
|
|
473
|
+
* `[rvTableCell]="columnId"` — the consumer's renderer for one column.
|
|
474
|
+
*
|
|
475
|
+
* Put it on an `<ng-template>`; the grid never renders the template directly,
|
|
476
|
+
* it asks for it BY COLUMN ID and instantiates it once per row. That is what
|
|
477
|
+
* makes an actions column possible without the table knowing anything about
|
|
478
|
+
* actions: the column list only declares geometry and a header, and the
|
|
479
|
+
* markup decides what a cell contains.
|
|
480
|
+
*
|
|
481
|
+
* ```html
|
|
482
|
+
* <rv-table [columns]="columns()" [rows]="rows()">
|
|
483
|
+
* <ng-template rvTableCell="actions" let-row let-data="data">
|
|
484
|
+
* <button type="button" (click)="edit(data)">Edit</button>
|
|
485
|
+
* </ng-template>
|
|
486
|
+
* </rv-table>
|
|
487
|
+
* ```
|
|
488
|
+
*
|
|
489
|
+
* A column whose id has no template and no `accessor` renders an empty cell,
|
|
490
|
+
* so declare the column for its width/header and supply the template here.
|
|
491
|
+
* The template wins over `accessor` when both exist: markup is an override.
|
|
492
|
+
*
|
|
493
|
+
* The context is {@link TableUiCellContext} — `$implicit` (and `row`) is the
|
|
494
|
+
* `TableRow`, `data` is your object, `value` is the accessor's value.
|
|
495
|
+
*/
|
|
496
|
+
export declare class RvTableCell {
|
|
497
|
+
/**
|
|
498
|
+
* The id of the column this template renders. It must match a
|
|
499
|
+
* `TableColumn.id`; an id that matches nothing is simply never used (the
|
|
500
|
+
* grid cannot throw at render time for a template it was given).
|
|
501
|
+
*/
|
|
502
|
+
readonly columnId: import("@angular/core").InputSignal<string>;
|
|
503
|
+
/** The template to instantiate. Injected from the `<ng-template>` host. */
|
|
504
|
+
readonly template: TemplateRef<TableUiCellContext>;
|
|
505
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<RvTableCell, never>;
|
|
506
|
+
static ɵdir: i0.ɵɵDirectiveDeclaration<RvTableCell, "[rvTableCell]", never, {
|
|
507
|
+
"columnId": {
|
|
508
|
+
"alias": "rvTableCell";
|
|
509
|
+
"required": true;
|
|
510
|
+
"isSignal": true;
|
|
511
|
+
};
|
|
512
|
+
}, {}, never, never, true, never>;
|
|
513
|
+
}
|
|
514
|
+
export type { TableUiCellContext, TableUiColumn, TableUiDensity, TableUiLabels, TableUiRow, TableUiSelectionChange };
|