@mk-kit/ui 0.34.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +115 -0
- package/block-editor/README.md +254 -0
- package/fesm2022/mk-kit-ui-block-editor.mjs +2158 -0
- package/fesm2022/mk-kit-ui-block-editor.mjs.map +1 -0
- package/fesm2022/mk-kit-ui-button.mjs +81 -0
- package/fesm2022/mk-kit-ui-button.mjs.map +1 -0
- package/fesm2022/mk-kit-ui-checkbox.mjs +136 -0
- package/fesm2022/mk-kit-ui-checkbox.mjs.map +1 -0
- package/fesm2022/mk-kit-ui-chip.mjs +122 -0
- package/fesm2022/mk-kit-ui-chip.mjs.map +1 -0
- package/fesm2022/mk-kit-ui-context-menu.mjs +144 -0
- package/fesm2022/mk-kit-ui-context-menu.mjs.map +1 -0
- package/fesm2022/mk-kit-ui-core.mjs +1576 -0
- package/fesm2022/mk-kit-ui-core.mjs.map +1 -0
- package/fesm2022/mk-kit-ui-data.mjs +6055 -0
- package/fesm2022/mk-kit-ui-data.mjs.map +1 -0
- package/fesm2022/mk-kit-ui-datetime.mjs +3409 -0
- package/fesm2022/mk-kit-ui-datetime.mjs.map +1 -0
- package/fesm2022/mk-kit-ui-directives.mjs +1779 -0
- package/fesm2022/mk-kit-ui-directives.mjs.map +1 -0
- package/fesm2022/mk-kit-ui-dnd.mjs +1073 -0
- package/fesm2022/mk-kit-ui-dnd.mjs.map +1 -0
- package/fesm2022/mk-kit-ui-feedback.mjs +2426 -0
- package/fesm2022/mk-kit-ui-feedback.mjs.map +1 -0
- package/fesm2022/mk-kit-ui-forms.mjs +9208 -0
- package/fesm2022/mk-kit-ui-forms.mjs.map +1 -0
- package/fesm2022/mk-kit-ui-icon.mjs +470 -0
- package/fesm2022/mk-kit-ui-icon.mjs.map +1 -0
- package/fesm2022/mk-kit-ui-media.mjs +896 -0
- package/fesm2022/mk-kit-ui-media.mjs.map +1 -0
- package/fesm2022/mk-kit-ui-navigation.mjs +2542 -0
- package/fesm2022/mk-kit-ui-navigation.mjs.map +1 -0
- package/fesm2022/mk-kit-ui-rich-text.mjs +565 -0
- package/fesm2022/mk-kit-ui-rich-text.mjs.map +1 -0
- package/fesm2022/mk-kit-ui-table.mjs +1378 -0
- package/fesm2022/mk-kit-ui-table.mjs.map +1 -0
- package/fesm2022/mk-kit-ui.mjs +32 -0
- package/fesm2022/mk-kit-ui.mjs.map +1 -0
- package/package.json +130 -0
- package/schematics/collection.json +10 -0
- package/schematics/ng-add/index.js +113 -0
- package/schematics/ng-add/schema.json +22 -0
- package/schematics/package.json +3 -0
- package/styles/mk-kit.css +750 -0
- package/types/mk-kit-ui-block-editor.d.ts +292 -0
- package/types/mk-kit-ui-button.d.ts +40 -0
- package/types/mk-kit-ui-checkbox.d.ts +62 -0
- package/types/mk-kit-ui-chip.d.ts +59 -0
- package/types/mk-kit-ui-context-menu.d.ts +57 -0
- package/types/mk-kit-ui-core.d.ts +1105 -0
- package/types/mk-kit-ui-data.d.ts +2580 -0
- package/types/mk-kit-ui-datetime.d.ts +1171 -0
- package/types/mk-kit-ui-directives.d.ts +807 -0
- package/types/mk-kit-ui-dnd.d.ts +423 -0
- package/types/mk-kit-ui-feedback.d.ts +1270 -0
- package/types/mk-kit-ui-forms.d.ts +3586 -0
- package/types/mk-kit-ui-icon.d.ts +108 -0
- package/types/mk-kit-ui-media.d.ts +549 -0
- package/types/mk-kit-ui-navigation.d.ts +1169 -0
- package/types/mk-kit-ui-rich-text.d.ts +187 -0
- package/types/mk-kit-ui-table.d.ts +739 -0
- package/types/mk-kit-ui.d.ts +17 -0
|
@@ -0,0 +1,739 @@
|
|
|
1
|
+
import * as _mk_kit_ui_table from '@mk-kit/ui/table';
|
|
2
|
+
import * as _angular_core from '@angular/core';
|
|
3
|
+
import { TemplateRef, Signal } from '@angular/core';
|
|
4
|
+
import * as _mk_kit_ui_core from '@mk-kit/ui/core';
|
|
5
|
+
import { Observable } from 'rxjs';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Marks an `<ng-template>` as the expandable detail content for {@link MkTable}
|
|
9
|
+
* rows. The template's implicit context is the row object, so consumers can
|
|
10
|
+
* destructure it with `let-row`:
|
|
11
|
+
*
|
|
12
|
+
* ```html
|
|
13
|
+
* <mk-table [columns]="cols" [data]="rows()" expandable>
|
|
14
|
+
* <ng-template mkTableRowDetail let-row>
|
|
15
|
+
* <dl>… {{ row.notes }} …</dl>
|
|
16
|
+
* </ng-template>
|
|
17
|
+
* </mk-table>
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
declare class MkTableRowDetail<T = unknown> {
|
|
21
|
+
/** The projected detail template, rendered once per expanded row. */
|
|
22
|
+
readonly template: TemplateRef<{
|
|
23
|
+
$implicit: T;
|
|
24
|
+
}>;
|
|
25
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<MkTableRowDetail<any>, never>;
|
|
26
|
+
static ɵdir: _angular_core.ɵɵDirectiveDeclaration<MkTableRowDetail<any>, "[mkTableRowDetail]", never, {}, {}, never, never, true, never>;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** Horizontal text alignment for a table column. */
|
|
30
|
+
type MkTableAlign = 'start' | 'center' | 'end';
|
|
31
|
+
/** Sort direction; `none` means unsorted. */
|
|
32
|
+
type MkSortDirection = 'asc' | 'desc' | 'none';
|
|
33
|
+
/** Row vertical density. */
|
|
34
|
+
type MkTableDensity = 'comfortable' | 'compact';
|
|
35
|
+
/** Column definition for {@link MkTable}. */
|
|
36
|
+
interface MkTableColumn<T = Record<string, unknown>> {
|
|
37
|
+
/** Property key on each row object supplying the cell value. */
|
|
38
|
+
key: string;
|
|
39
|
+
/** Visible column header text. */
|
|
40
|
+
header: string;
|
|
41
|
+
/** Allow sorting by this column. */
|
|
42
|
+
sortable?: boolean;
|
|
43
|
+
/** Cell/header alignment (default `start`). */
|
|
44
|
+
align?: MkTableAlign;
|
|
45
|
+
/** Fixed column width (any CSS length). */
|
|
46
|
+
width?: string;
|
|
47
|
+
/** Optional formatter turning the raw value into display text. */
|
|
48
|
+
format?: (value: unknown, row: T) => string;
|
|
49
|
+
/** Allow the user to drag-resize this column (needs `resizableColumns`). */
|
|
50
|
+
resizable?: boolean;
|
|
51
|
+
/** Make this column's cells inline-editable (double-click / Enter). */
|
|
52
|
+
editable?: boolean;
|
|
53
|
+
/** Pin (freeze) this column to a side while the body scrolls horizontally. */
|
|
54
|
+
pinned?: 'left' | 'right';
|
|
55
|
+
/** Minimum width in px when resizing (default 60). */
|
|
56
|
+
minWidth?: number;
|
|
57
|
+
/**
|
|
58
|
+
* What becomes of this column when the table stacks into cards
|
|
59
|
+
* (see {@link MkTable.stackAt}). Omitted, the column renders as a labelled
|
|
60
|
+
* field: its `header` on one side, its value on the other.
|
|
61
|
+
*
|
|
62
|
+
* - `'title'` — the card's heading. No label; the value identifies the
|
|
63
|
+
* record at a glance (an order number, a product name). Mark one, or two
|
|
64
|
+
* if something short belongs beside it such as a status or a total.
|
|
65
|
+
* - `'footer'` — pinned to the bottom of the card, full width and unlabelled.
|
|
66
|
+
* Where an actions cell belongs: buttons read as buttons, rather than as
|
|
67
|
+
* the answer to a label.
|
|
68
|
+
* - `'hide'` — not rendered at all. Not merely invisible: the cell is never
|
|
69
|
+
* created, so a screen reader does not read it either. For columns that
|
|
70
|
+
* only earn their place while scanning a grid, and especially for anything
|
|
71
|
+
* an expandable row detail already repeats.
|
|
72
|
+
*/
|
|
73
|
+
stack?: 'title' | 'footer' | 'hide';
|
|
74
|
+
}
|
|
75
|
+
/** Payload emitted by {@link MkTable.sortChange}. */
|
|
76
|
+
interface MkSortChange {
|
|
77
|
+
/** Column key sorted by. */
|
|
78
|
+
key: string;
|
|
79
|
+
/** Resulting direction (`none` when sorting was cleared). */
|
|
80
|
+
direction: MkSortDirection;
|
|
81
|
+
}
|
|
82
|
+
/** Payload emitted by {@link MkTable.columnResize} after a column resize. */
|
|
83
|
+
interface MkColumnResize {
|
|
84
|
+
/** The resized column's key. */
|
|
85
|
+
key: string;
|
|
86
|
+
/** The new width in pixels. */
|
|
87
|
+
width: number;
|
|
88
|
+
}
|
|
89
|
+
/** Payload emitted by {@link MkTable.cellEdit} when an editable cell is saved. */
|
|
90
|
+
interface MkCellEdit<T = Record<string, unknown>> {
|
|
91
|
+
/** The edited row. */
|
|
92
|
+
row: T;
|
|
93
|
+
/** The column key that was edited. */
|
|
94
|
+
key: string;
|
|
95
|
+
/** The new (string) value the user entered. */
|
|
96
|
+
value: string;
|
|
97
|
+
}
|
|
98
|
+
/** A group of rows produced by {@link MkTable.groupBy}. */
|
|
99
|
+
interface MkTableGroup<T = Record<string, unknown>> {
|
|
100
|
+
/** The shared group value. */
|
|
101
|
+
key: unknown;
|
|
102
|
+
/** Display label for the group header. */
|
|
103
|
+
label: string;
|
|
104
|
+
/** The rows in this group, in display (sorted) order. */
|
|
105
|
+
rows: T[];
|
|
106
|
+
}
|
|
107
|
+
/** Payload emitted by {@link MkTable.groupToggle}. */
|
|
108
|
+
interface MkGroupToggle {
|
|
109
|
+
/** The toggled group's value. */
|
|
110
|
+
key: unknown;
|
|
111
|
+
/** Whether the group is now collapsed. */
|
|
112
|
+
collapsed: boolean;
|
|
113
|
+
}
|
|
114
|
+
/** One rendered tbody entry: either a group header or a data row. */
|
|
115
|
+
type MkTableItem<T> = {
|
|
116
|
+
kind: 'group';
|
|
117
|
+
group: MkTableGroup<T>;
|
|
118
|
+
} | {
|
|
119
|
+
kind: 'row';
|
|
120
|
+
row: T;
|
|
121
|
+
};
|
|
122
|
+
/**
|
|
123
|
+
* Table — a themed data table built on a native `<table>` for accessibility.
|
|
124
|
+
* Supply `columns` and `data`; opt into sortable columns, sticky header,
|
|
125
|
+
* zebra striping, hover and density. Sorting is fully keyboard operable
|
|
126
|
+
* (Enter/Space on a header) and announces changes via {@link MkLiveAnnouncer}.
|
|
127
|
+
*
|
|
128
|
+
* ```html
|
|
129
|
+
* <mk-table
|
|
130
|
+
* [columns]="columns"
|
|
131
|
+
* [data]="rows()"
|
|
132
|
+
* stickyHeader
|
|
133
|
+
* zebra
|
|
134
|
+
* (sortChange)="onSort($event)"
|
|
135
|
+
* (rowClick)="open($event)" />
|
|
136
|
+
* ```
|
|
137
|
+
*/
|
|
138
|
+
declare class MkTable<T = Record<string, unknown>> {
|
|
139
|
+
private readonly announcer;
|
|
140
|
+
protected readonly i18n: _mk_kit_ui_core.MkI18nStrings;
|
|
141
|
+
private readonly document;
|
|
142
|
+
private readonly host;
|
|
143
|
+
private readonly injector;
|
|
144
|
+
private readonly isBrowser;
|
|
145
|
+
private readonly destroyRef;
|
|
146
|
+
/**
|
|
147
|
+
* True while the table is narrower than {@link stackAt} and rendering cards.
|
|
148
|
+
*
|
|
149
|
+
* Driven by the element's own width rather than the viewport's: the question
|
|
150
|
+
* "do these columns fit" is about the space the table HAS, not the size of
|
|
151
|
+
* the screen. A table in a sidebar or a dialog should stack while the window
|
|
152
|
+
* around it is enormous.
|
|
153
|
+
*/
|
|
154
|
+
protected readonly stacked: _angular_core.WritableSignal<boolean>;
|
|
155
|
+
constructor();
|
|
156
|
+
/** Measures the thead and exposes it as the group rows' sticky offset. */
|
|
157
|
+
private applyGroupTop;
|
|
158
|
+
/** Column definitions (order = display order). */
|
|
159
|
+
readonly columns: _angular_core.InputSignal<MkTableColumn<T>[]>;
|
|
160
|
+
/** Row objects to render. */
|
|
161
|
+
readonly data: _angular_core.InputSignal<T[]>;
|
|
162
|
+
/** Pin the header to the top of the scroll container. */
|
|
163
|
+
readonly stickyHeader: _angular_core.InputSignalWithTransform<boolean, unknown>;
|
|
164
|
+
/** Alternate row background for readability. */
|
|
165
|
+
readonly zebra: _angular_core.InputSignalWithTransform<boolean, unknown>;
|
|
166
|
+
/** Highlight rows on hover. */
|
|
167
|
+
readonly hover: _angular_core.InputSignalWithTransform<boolean, unknown>;
|
|
168
|
+
/** Row density. */
|
|
169
|
+
readonly density: _angular_core.InputSignal<MkTableDensity>;
|
|
170
|
+
/**
|
|
171
|
+
* Width in px below which each row renders as a CARD instead of a table row.
|
|
172
|
+
* `0` (default) never stacks.
|
|
173
|
+
*
|
|
174
|
+
* Measured on the table's own container, not the viewport — a table in a
|
|
175
|
+
* narrow sidebar should stack on a desktop, and a table on a tablet in
|
|
176
|
+
* landscape should not. Per-column behaviour is set with
|
|
177
|
+
* {@link MkTableColumn.stack}.
|
|
178
|
+
*
|
|
179
|
+
* A grid cannot survive a phone: eight columns become eight unreadable
|
|
180
|
+
* slivers, and horizontal scrolling loses the row you were reading. Cards
|
|
181
|
+
* keep one record together and put its header beside each value.
|
|
182
|
+
*/
|
|
183
|
+
readonly stackAt: _angular_core.InputSignalWithTransform<number, unknown>;
|
|
184
|
+
/** Style rows as clickable and emit `rowClick`. */
|
|
185
|
+
readonly clickableRows: _angular_core.InputSignalWithTransform<boolean, unknown>;
|
|
186
|
+
/** Message shown when there are no rows. */
|
|
187
|
+
readonly emptyMessage: _angular_core.InputSignal<string>;
|
|
188
|
+
/** Render a leading checkbox column for row selection. */
|
|
189
|
+
readonly selectable: _angular_core.InputSignalWithTransform<boolean, unknown>;
|
|
190
|
+
/**
|
|
191
|
+
* Two-way bound array of selected rows. Use `[(selected)]` to bind, or read
|
|
192
|
+
* `selectionChange`. Rows are compared by {@link trackKey} when set, else by
|
|
193
|
+
* referential identity.
|
|
194
|
+
*/
|
|
195
|
+
readonly selected: _angular_core.ModelSignal<T[]>;
|
|
196
|
+
/**
|
|
197
|
+
* Property name identifying a row for selection equality. When omitted rows
|
|
198
|
+
* are matched by reference.
|
|
199
|
+
*/
|
|
200
|
+
readonly trackKey: _angular_core.InputSignal<string | undefined>;
|
|
201
|
+
/**
|
|
202
|
+
* Optional per-row CSS class: called with each row, the returned string is
|
|
203
|
+
* appended to the row's class list (falsy → none). For state the consumer
|
|
204
|
+
* owns — an "active in the side panel" highlight, an unread accent — that
|
|
205
|
+
* `selectable`'s own selected style doesn't cover.
|
|
206
|
+
*/
|
|
207
|
+
readonly rowClass: _angular_core.InputSignal<((row: T) => string | null | undefined) | null>;
|
|
208
|
+
/** Resolved class for a row (empty string when no `rowClass` is set). */
|
|
209
|
+
protected rowClassFor(row: T): string;
|
|
210
|
+
/**
|
|
211
|
+
* Render a leading expander column. Each row can reveal a detail panel
|
|
212
|
+
* supplied via an `<ng-template mkTableRowDetail let-row>`.
|
|
213
|
+
*/
|
|
214
|
+
readonly expandable: _angular_core.InputSignalWithTransform<boolean, unknown>;
|
|
215
|
+
/** Allow only one row expanded at a time (accordion). */
|
|
216
|
+
readonly singleExpand: _angular_core.InputSignalWithTransform<boolean, unknown>;
|
|
217
|
+
/** Enable drag-to-resize on columns marked `resizable` (data-grid pro). */
|
|
218
|
+
readonly resizableColumns: _angular_core.InputSignalWithTransform<boolean, unknown>;
|
|
219
|
+
/** Enable drag-to-reorder of column headers (data-grid pro). */
|
|
220
|
+
readonly reorderableColumns: _angular_core.InputSignalWithTransform<boolean, unknown>;
|
|
221
|
+
/**
|
|
222
|
+
* Group rows by a column key or an accessor. Renders a collapsible group
|
|
223
|
+
* header row (sticky, with a row count) above each group. Sorting still
|
|
224
|
+
* applies within groups; groups follow their first row's sorted position.
|
|
225
|
+
*/
|
|
226
|
+
readonly groupBy: _angular_core.InputSignal<string | ((row: T) => unknown) | null>;
|
|
227
|
+
/** Formats a group header label; defaults to `String(value)`. */
|
|
228
|
+
readonly groupLabel: _angular_core.InputSignal<((value: unknown, rows: T[]) => string) | null>;
|
|
229
|
+
/** Emitted when the sort column/direction changes. */
|
|
230
|
+
readonly sortChange: _angular_core.OutputEmitterRef<MkSortChange>;
|
|
231
|
+
/** Emitted when a row is clicked (enable via `clickableRows`). */
|
|
232
|
+
readonly rowClick: _angular_core.OutputEmitterRef<T>;
|
|
233
|
+
/** Emitted with the new selection whenever it changes (enable via `selectable`). */
|
|
234
|
+
readonly selectionChange: _angular_core.OutputEmitterRef<T[]>;
|
|
235
|
+
/** Emitted with the currently expanded rows whenever they change. */
|
|
236
|
+
readonly expandedChange: _angular_core.OutputEmitterRef<T[]>;
|
|
237
|
+
/** Emitted when a column is resized (px). */
|
|
238
|
+
readonly columnResize: _angular_core.OutputEmitterRef<MkColumnResize>;
|
|
239
|
+
/** Emitted with the new column key order after a reorder. */
|
|
240
|
+
readonly columnReorder: _angular_core.OutputEmitterRef<string[]>;
|
|
241
|
+
/** Emitted when an inline-editable cell is saved. */
|
|
242
|
+
readonly cellEdit: _angular_core.OutputEmitterRef<MkCellEdit<T>>;
|
|
243
|
+
/** Emitted when a group header is expanded or collapsed. */
|
|
244
|
+
readonly groupToggle: _angular_core.OutputEmitterRef<MkGroupToggle>;
|
|
245
|
+
/** User-set column widths (px), keyed by column key. */
|
|
246
|
+
private readonly colWidths;
|
|
247
|
+
/** User-set column order (keys); `null` = the input order. */
|
|
248
|
+
private readonly colOrder;
|
|
249
|
+
/** The cell currently being inline-edited. */
|
|
250
|
+
protected readonly editing: _angular_core.WritableSignal<{
|
|
251
|
+
index: number;
|
|
252
|
+
key: string;
|
|
253
|
+
} | null>;
|
|
254
|
+
/** Columns in display order, honouring any user reordering. */
|
|
255
|
+
protected readonly orderedColumns: _angular_core.Signal<MkTableColumn<T>[]>;
|
|
256
|
+
/** Columns forming the card's heading line. */
|
|
257
|
+
protected readonly stackTitleColumns: _angular_core.Signal<MkTableColumn<T>[]>;
|
|
258
|
+
/** Columns rendered as `label / value` rows in the card body. */
|
|
259
|
+
protected readonly stackFieldColumns: _angular_core.Signal<MkTableColumn<T>[]>;
|
|
260
|
+
/** Columns pinned to the bottom of the card, unlabelled. */
|
|
261
|
+
protected readonly stackFooterColumns: _angular_core.Signal<MkTableColumn<T>[]>;
|
|
262
|
+
/**
|
|
263
|
+
* Whether a stacked cell should show its column header as a label.
|
|
264
|
+
*
|
|
265
|
+
* An empty header means the column never had a name to show — an actions or
|
|
266
|
+
* chevron column — and an empty label box would just be a gap the reader has
|
|
267
|
+
* to account for.
|
|
268
|
+
*/
|
|
269
|
+
protected hasStackLabel(col: MkTableColumn<T>): boolean;
|
|
270
|
+
/** The rendered width for a column, if the user resized it. */
|
|
271
|
+
protected colStyleWidth(col: MkTableColumn<T>): string | null;
|
|
272
|
+
/** Sticky offsets for every pinned column, computed once per layout change. */
|
|
273
|
+
private readonly pinnedOffsets;
|
|
274
|
+
/** Sticky offset (px) for a pinned column. */
|
|
275
|
+
protected pinnedOffset(col: MkTableColumn<T>): number;
|
|
276
|
+
/** Pinning freezes a column against horizontal scroll. Cards do not scroll
|
|
277
|
+
* sideways, so both the class and its inline offset are suppressed. */
|
|
278
|
+
protected isPinned(col: MkTableColumn<T>, side: 'left' | 'right'): boolean;
|
|
279
|
+
private numericWidth;
|
|
280
|
+
private resizeKey;
|
|
281
|
+
private resizeStartX;
|
|
282
|
+
private resizeStartW;
|
|
283
|
+
private resizeMin;
|
|
284
|
+
/** +1 in LTR, -1 in RTL — dragging toward the inline-end always widens. */
|
|
285
|
+
private resizeSign;
|
|
286
|
+
/** Whether the table currently renders right-to-left (SSR-safe). */
|
|
287
|
+
private isRtl;
|
|
288
|
+
private resizeRaf;
|
|
289
|
+
private pendingResizeX;
|
|
290
|
+
/** Begin a drag-resize from a header handle. */
|
|
291
|
+
protected startResize(event: PointerEvent, col: MkTableColumn<T>): void;
|
|
292
|
+
/** rAF-coalesced: at most one width write (and CD pass) per frame. */
|
|
293
|
+
private readonly onResizeMove;
|
|
294
|
+
private applyPendingResize;
|
|
295
|
+
private readonly onResizeEnd;
|
|
296
|
+
/** Keyboard column resize on the focused separator (APG window splitter). */
|
|
297
|
+
protected onResizeKeydown(event: KeyboardEvent, col: MkTableColumn<T>): void;
|
|
298
|
+
/** The current width of a column, for the separator's aria-valuenow. */
|
|
299
|
+
protected resizeValueNow(col: MkTableColumn<T>): number;
|
|
300
|
+
/** The resize floor for a column, for the separator's aria-valuemin. */
|
|
301
|
+
protected resizeValueMin(col: MkTableColumn<T>): number;
|
|
302
|
+
/** Advertised resize ceiling (aria-valuemax) — a sane constant bound. */
|
|
303
|
+
protected readonly resizeValueMax = 2000;
|
|
304
|
+
ngOnDestroy(): void;
|
|
305
|
+
protected readonly dragKey: _angular_core.WritableSignal<string | null>;
|
|
306
|
+
protected onColDragStart(event: DragEvent, col: MkTableColumn<T>): void;
|
|
307
|
+
protected onColDragOver(event: DragEvent): void;
|
|
308
|
+
protected onColDrop(event: DragEvent, target: MkTableColumn<T>): void;
|
|
309
|
+
protected onColDragEnd(): void;
|
|
310
|
+
/** Move a column to `toIdx` in the display order and announce it. */
|
|
311
|
+
private moveColumn;
|
|
312
|
+
/** Keyboard column reorder: Alt+Arrow moves the focused header. */
|
|
313
|
+
protected onReorderKeydown(event: KeyboardEvent, col: MkTableColumn<T>): void;
|
|
314
|
+
protected isEditing(index: number, col: MkTableColumn<T>): boolean;
|
|
315
|
+
/** The cell element being edited, so focus can be restored after. */
|
|
316
|
+
private editingCell;
|
|
317
|
+
/** The inline-edit input, focused once it renders. */
|
|
318
|
+
private readonly editInput;
|
|
319
|
+
protected startEdit(index: number, col: MkTableColumn<T>, event?: Event): void;
|
|
320
|
+
/** Keyboard path into edit mode (Enter / F2 on a focused editable cell). */
|
|
321
|
+
protected onCellKeydown(event: KeyboardEvent, index: number, col: MkTableColumn<T>): void;
|
|
322
|
+
protected commitEdit(row: T, col: MkTableColumn<T>, value: string, restoreFocus?: boolean): void;
|
|
323
|
+
protected cancelEdit(restoreFocus?: boolean): void;
|
|
324
|
+
protected onEditKeydown(event: KeyboardEvent, row: T, col: MkTableColumn<T>): void;
|
|
325
|
+
/** The projected row-detail template (enable via `expandable`). */
|
|
326
|
+
protected readonly rowDetail: _angular_core.Signal<MkTableRowDetail<any> | undefined>;
|
|
327
|
+
/** Per-column cell templates, projected as `<ng-template mkTableCell="key">`. */
|
|
328
|
+
private readonly cellTemplates;
|
|
329
|
+
private readonly cellTemplateByKey;
|
|
330
|
+
/** The template registered for a column, or null to fall back to text. */
|
|
331
|
+
protected cellTemplateFor(key: string): _angular_core.TemplateRef<_mk_kit_ui_table.MkTableCellContext<unknown>> | null;
|
|
332
|
+
private readonly sortKey;
|
|
333
|
+
private readonly sortDir;
|
|
334
|
+
/** Stable id prefix so each detail row can be referenced by aria-controls. */
|
|
335
|
+
private readonly detailIdBase;
|
|
336
|
+
/** Total rendered columns, including the select and expander columns. */
|
|
337
|
+
protected readonly totalColumns: _angular_core.Signal<number>;
|
|
338
|
+
/**
|
|
339
|
+
* Shared locale-sensitive collator for string sorting. `localeCompare`
|
|
340
|
+
* re-resolves locale data on every call; one cached `Intl.Collator` makes
|
|
341
|
+
* large-table sorts several-fold faster with the same default-locale order.
|
|
342
|
+
*/
|
|
343
|
+
private static readonly sortCollator;
|
|
344
|
+
/** Data sorted by the active column, or the input order when unsorted. */
|
|
345
|
+
protected readonly sortedData: _angular_core.Signal<T[]>;
|
|
346
|
+
/** `aria-sort` value for a header cell. */
|
|
347
|
+
protected ariaSort(col: MkTableColumn<T>): string | null;
|
|
348
|
+
/** Glyph indicating a column's sort state. */
|
|
349
|
+
protected sortGlyph(col: MkTableColumn<T>): string;
|
|
350
|
+
/** Raw cell value, handed to an `[mkTableCell]` template unformatted. */
|
|
351
|
+
protected cellValue(row: T, col: MkTableColumn<T>): unknown;
|
|
352
|
+
/** Rendered text for a cell, applying the column formatter if present. */
|
|
353
|
+
protected cellText(row: T, col: MkTableColumn<T>): string;
|
|
354
|
+
protected onSort(col: MkTableColumn<T>): void;
|
|
355
|
+
protected onRowClick(row: T): void;
|
|
356
|
+
/** Keyboard activation for clickable rows (Enter / Space). */
|
|
357
|
+
protected onRowKeydown(event: KeyboardEvent, row: T): void;
|
|
358
|
+
/** Stable row identity for `@for` tracking (trackKey when set, else the row). */
|
|
359
|
+
protected trackRow: (row: T) => unknown;
|
|
360
|
+
/** Leading cell text used to label per-row controls for screen readers. */
|
|
361
|
+
protected rowLabel(row: T): string;
|
|
362
|
+
private rowKey;
|
|
363
|
+
/** Selected row keys as a Set — O(1) membership per cell per CD pass. */
|
|
364
|
+
private readonly selectedKeys;
|
|
365
|
+
/** Whether `row` is currently selected. */
|
|
366
|
+
protected isSelected(row: T): boolean;
|
|
367
|
+
/** True when every visible row is selected. */
|
|
368
|
+
protected readonly allSelected: _angular_core.Signal<boolean>;
|
|
369
|
+
/** True when some — but not all — visible rows are selected. */
|
|
370
|
+
protected readonly someSelected: _angular_core.Signal<boolean>;
|
|
371
|
+
private commitSelection;
|
|
372
|
+
/** Toggle a single row's selection without triggering `rowClick`. */
|
|
373
|
+
protected toggleRow(row: T): void;
|
|
374
|
+
/** Select or deselect all visible rows. */
|
|
375
|
+
protected toggleAll(): void;
|
|
376
|
+
/** Group values currently collapsed. */
|
|
377
|
+
private readonly collapsedGroups;
|
|
378
|
+
/** Rows grouped by {@link groupBy}, or `null` when grouping is off. */
|
|
379
|
+
protected readonly groups: _angular_core.Signal<MkTableGroup<T>[] | null>;
|
|
380
|
+
/**
|
|
381
|
+
* The tbody render list: group headers interleaved with their (expanded)
|
|
382
|
+
* rows when grouping is on, else just the sorted rows.
|
|
383
|
+
*/
|
|
384
|
+
protected readonly displayItems: _angular_core.Signal<MkTableItem<T>[]>;
|
|
385
|
+
/** `@for` identity: group headers by value, rows by {@link trackRow}. */
|
|
386
|
+
protected trackItem: (item: MkTableItem<T>) => unknown;
|
|
387
|
+
/** Whether a group is currently collapsed. */
|
|
388
|
+
protected isGroupCollapsed(key: unknown): boolean;
|
|
389
|
+
/** Collapse or expand a group header. */
|
|
390
|
+
protected onGroupToggle(group: MkTableGroup<T>): void;
|
|
391
|
+
/** Collapse every group. */
|
|
392
|
+
collapseAllGroups(): void;
|
|
393
|
+
/** Expand every group. */
|
|
394
|
+
expandAllGroups(): void;
|
|
395
|
+
private readonly expandedKeys;
|
|
396
|
+
/** Whether `row`'s detail panel is currently expanded. */
|
|
397
|
+
protected isExpanded(row: T): boolean;
|
|
398
|
+
/** The DOM id of a row's detail panel (for `aria-controls`). */
|
|
399
|
+
protected detailId(index: number): string;
|
|
400
|
+
/** Toggle a row's detail panel, honouring `singleExpand`. */
|
|
401
|
+
protected toggleExpand(row: T, event?: Event): void;
|
|
402
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<MkTable<any>, never>;
|
|
403
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<MkTable<any>, "mk-table", never, { "columns": { "alias": "columns"; "required": true; "isSignal": true; }; "data": { "alias": "data"; "required": false; "isSignal": true; }; "stickyHeader": { "alias": "stickyHeader"; "required": false; "isSignal": true; }; "zebra": { "alias": "zebra"; "required": false; "isSignal": true; }; "hover": { "alias": "hover"; "required": false; "isSignal": true; }; "density": { "alias": "density"; "required": false; "isSignal": true; }; "stackAt": { "alias": "stackAt"; "required": false; "isSignal": true; }; "clickableRows": { "alias": "clickableRows"; "required": false; "isSignal": true; }; "emptyMessage": { "alias": "emptyMessage"; "required": false; "isSignal": true; }; "selectable": { "alias": "selectable"; "required": false; "isSignal": true; }; "selected": { "alias": "selected"; "required": false; "isSignal": true; }; "trackKey": { "alias": "trackKey"; "required": false; "isSignal": true; }; "rowClass": { "alias": "rowClass"; "required": false; "isSignal": true; }; "expandable": { "alias": "expandable"; "required": false; "isSignal": true; }; "singleExpand": { "alias": "singleExpand"; "required": false; "isSignal": true; }; "resizableColumns": { "alias": "resizableColumns"; "required": false; "isSignal": true; }; "reorderableColumns": { "alias": "reorderableColumns"; "required": false; "isSignal": true; }; "groupBy": { "alias": "groupBy"; "required": false; "isSignal": true; }; "groupLabel": { "alias": "groupLabel"; "required": false; "isSignal": true; }; }, { "selected": "selectedChange"; "sortChange": "sortChange"; "rowClick": "rowClick"; "selectionChange": "selectionChange"; "expandedChange": "expandedChange"; "columnResize": "columnResize"; "columnReorder": "columnReorder"; "cellEdit": "cellEdit"; "groupToggle": "groupToggle"; }, ["rowDetail", "cellTemplates"], ["[mkTableEmpty]"], true, never>;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
/** Context handed to an `[mkTableCell]` template. */
|
|
407
|
+
interface MkTableCellContext<T = unknown> {
|
|
408
|
+
/** The cell's raw value (the row's property named by the column key). */
|
|
409
|
+
$implicit: unknown;
|
|
410
|
+
/** The whole row, for cells that need more than one field. */
|
|
411
|
+
row: T;
|
|
412
|
+
}
|
|
413
|
+
/**
|
|
414
|
+
* Marks an `<ng-template>` as the renderer for one column's cells, named by the
|
|
415
|
+
* column `key`. Without it a cell can only be text — `MkTableColumn.format`
|
|
416
|
+
* returns a string — so anything richer (a status tag, an avatar, a progress
|
|
417
|
+
* bar, an action button) meant abandoning `mk-table` for a hand-rolled
|
|
418
|
+
* `<table>`.
|
|
419
|
+
*
|
|
420
|
+
* The value is the template's implicit context and the row is available as
|
|
421
|
+
* `let-row`:
|
|
422
|
+
*
|
|
423
|
+
* ```html
|
|
424
|
+
* <mk-table [columns]="cols" [data]="rows()">
|
|
425
|
+
* <ng-template mkTableCell="status" let-value let-row="row">
|
|
426
|
+
* <mk-tag [tone]="toneFor(value)">{{ label(value) }}</mk-tag>
|
|
427
|
+
* </ng-template>
|
|
428
|
+
* </mk-table>
|
|
429
|
+
* ```
|
|
430
|
+
*
|
|
431
|
+
* A column with both a template and a `format` uses the template; `format`
|
|
432
|
+
* still applies to sorting/export paths that need a string.
|
|
433
|
+
*/
|
|
434
|
+
declare class MkTableCell<T = unknown> {
|
|
435
|
+
/** Column key whose cells this template renders. */
|
|
436
|
+
readonly mkTableCell: _angular_core.InputSignal<string>;
|
|
437
|
+
/** The projected template, rendered once per cell in that column. */
|
|
438
|
+
readonly template: TemplateRef<MkTableCellContext<T>>;
|
|
439
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<MkTableCell<any>, never>;
|
|
440
|
+
static ɵdir: _angular_core.ɵɵDirectiveDeclaration<MkTableCell<any>, "[mkTableCell]", never, { "mkTableCell": { "alias": "mkTableCell"; "required": true; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
/** Current sort state emitted by {@link MkSort.mkSortChange}. */
|
|
444
|
+
interface MkSortState {
|
|
445
|
+
/** Id of the column being sorted by (empty when cleared). */
|
|
446
|
+
active: string;
|
|
447
|
+
/** Sort direction (`none` when cleared). */
|
|
448
|
+
direction: MkSortDirection;
|
|
449
|
+
}
|
|
450
|
+
/**
|
|
451
|
+
* The minimal shape {@link MkSort} needs from a header. Implemented by
|
|
452
|
+
* {@link MkSortHeader}; declared separately so the coordinator does not depend
|
|
453
|
+
* on the header's concrete type (avoids a circular import).
|
|
454
|
+
*/
|
|
455
|
+
interface MkSortable {
|
|
456
|
+
id(): string;
|
|
457
|
+
start(): 'asc' | 'desc' | undefined;
|
|
458
|
+
disabled(): boolean;
|
|
459
|
+
sortLabel(): string;
|
|
460
|
+
}
|
|
461
|
+
/**
|
|
462
|
+
* Sort coordinator — apply `mkSort` to a table (or any container) to track
|
|
463
|
+
* which column is sorted and in which direction. It holds no data: register
|
|
464
|
+
* headers with `mkSortHeader`, then re-sort your rows in the
|
|
465
|
+
* `(mkSortChange)` handler. Mirrors the Angular Material `matSort` model but
|
|
466
|
+
* signal-based.
|
|
467
|
+
*
|
|
468
|
+
* Clicking a header cycles asc → desc → unsorted (set `mkSortDisableClear` to
|
|
469
|
+
* cycle asc ↔ desc only). `mkSortStart` flips the initial direction.
|
|
470
|
+
*
|
|
471
|
+
* ```html
|
|
472
|
+
* <table mkSort mkSortActive="name" mkSortDirection="asc"
|
|
473
|
+
* (mkSortChange)="sortData($event)">
|
|
474
|
+
* <thead><tr>
|
|
475
|
+
* <th mkSortHeader="name">Name</th>
|
|
476
|
+
* <th mkSortHeader="size" mkSortHeaderStart="desc">Size</th>
|
|
477
|
+
* </tr></thead>
|
|
478
|
+
* …
|
|
479
|
+
* </table>
|
|
480
|
+
* ```
|
|
481
|
+
*/
|
|
482
|
+
declare class MkSort {
|
|
483
|
+
private readonly announcer;
|
|
484
|
+
private readonly i18n;
|
|
485
|
+
/** Id of the currently sorted column (two-way; empty when unsorted). */
|
|
486
|
+
readonly active: _angular_core.ModelSignal<string>;
|
|
487
|
+
/** Current sort direction (two-way). */
|
|
488
|
+
readonly direction: _angular_core.ModelSignal<MkSortDirection>;
|
|
489
|
+
/** Direction the first click on a header applies. Default `asc`. */
|
|
490
|
+
readonly start: _angular_core.InputSignal<"asc" | "desc">;
|
|
491
|
+
/** Disable sorting for every header. */
|
|
492
|
+
readonly disabled: _angular_core.InputSignalWithTransform<boolean, unknown>;
|
|
493
|
+
/** Remove the "unsorted" step so headers cycle asc ↔ desc only. */
|
|
494
|
+
readonly disableClear: _angular_core.InputSignalWithTransform<boolean, unknown>;
|
|
495
|
+
/** Emits the new sort state whenever a header is activated. */
|
|
496
|
+
readonly sortChange: _angular_core.OutputEmitterRef<MkSortState>;
|
|
497
|
+
/** Advance the sort state for the given header (called on click/keyboard). */
|
|
498
|
+
sort(header: MkSortable): void;
|
|
499
|
+
/** Whether the given column id is the active, non-cleared sort. */
|
|
500
|
+
isActive(id: string): boolean;
|
|
501
|
+
private startFor;
|
|
502
|
+
private nextDirection;
|
|
503
|
+
private announce;
|
|
504
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<MkSort, never>;
|
|
505
|
+
static ɵdir: _angular_core.ɵɵDirectiveDeclaration<MkSort, "[mkSort]", ["mkSort"], { "active": { "alias": "mkSortActive"; "required": false; "isSignal": true; }; "direction": { "alias": "mkSortDirection"; "required": false; "isSignal": true; }; "start": { "alias": "mkSortStart"; "required": false; "isSignal": true; }; "disabled": { "alias": "mkSortDisabled"; "required": false; "isSignal": true; }; "disableClear": { "alias": "mkSortDisableClear"; "required": false; "isSignal": true; }; }, { "active": "mkSortActiveChange"; "direction": "mkSortDirectionChange"; "sortChange": "mkSortChange"; }, never, never, true, never>;
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
/**
|
|
509
|
+
* Sort header — attach `mkSortHeader` to a header cell (`<th>`) inside an
|
|
510
|
+
* element carrying `mkSort`. It wraps the projected header text in a real
|
|
511
|
+
* `<button>` (so assistive tech hears an operable control), adds a directional
|
|
512
|
+
* arrow (faint on hover, solid when active), reflects `aria-sort` on the cell,
|
|
513
|
+
* and toggles the sort on click or Enter/Space.
|
|
514
|
+
*
|
|
515
|
+
* ```html
|
|
516
|
+
* <th mkSortHeader="email" mkSortHeaderLabel="Email address">Email</th>
|
|
517
|
+
* ```
|
|
518
|
+
*/
|
|
519
|
+
declare class MkSortHeader implements MkSortable {
|
|
520
|
+
private readonly sort;
|
|
521
|
+
/** Column id this header sorts by (the `mkSortHeader` value). */
|
|
522
|
+
readonly id: _angular_core.InputSignal<string>;
|
|
523
|
+
/** Per-header override for the initial sort direction. */
|
|
524
|
+
readonly start: _angular_core.InputSignal<"asc" | "desc" | undefined>;
|
|
525
|
+
/** Disable sorting for just this header. */
|
|
526
|
+
readonly disabled: _angular_core.InputSignalWithTransform<boolean, unknown>;
|
|
527
|
+
/** Accessible label used in sort announcements (defaults to the id). */
|
|
528
|
+
readonly sortLabel: _angular_core.InputSignal<string>;
|
|
529
|
+
/** Disabled if this header or the whole `mkSort` is disabled. */
|
|
530
|
+
readonly isDisabled: _angular_core.Signal<boolean>;
|
|
531
|
+
/** Whether this header is the active, non-cleared sort. */
|
|
532
|
+
readonly isActive: _angular_core.Signal<boolean>;
|
|
533
|
+
/** Active direction, or `none` when this header is not the active sort. */
|
|
534
|
+
readonly direction: _angular_core.Signal<MkSortDirection>;
|
|
535
|
+
/** `aria-sort` value for the header cell. */
|
|
536
|
+
protected readonly ariaSort: _angular_core.Signal<"none" | "ascending" | "descending">;
|
|
537
|
+
protected toggle(): void;
|
|
538
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<MkSortHeader, never>;
|
|
539
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<MkSortHeader, "[mkSortHeader]", never, { "id": { "alias": "mkSortHeader"; "required": false; "isSignal": true; }; "start": { "alias": "mkSortHeaderStart"; "required": false; "isSignal": true; }; "disabled": { "alias": "mkSortHeaderDisabled"; "required": false; "isSignal": true; }; "sortLabel": { "alias": "mkSortHeaderLabel"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
/** The request handed to a {@link MkDataFetcher} on every load. */
|
|
543
|
+
interface MkDataRequest {
|
|
544
|
+
/** 1-based page index, matching `mk-pagination`. */
|
|
545
|
+
page: number;
|
|
546
|
+
/** Number of rows per page. */
|
|
547
|
+
pageSize: number;
|
|
548
|
+
/** Active sort, or `null` when unsorted (cleared sorts normalise to `null`). */
|
|
549
|
+
sort: MkSortState | null;
|
|
550
|
+
/** Free-text filter query (`''` = none). */
|
|
551
|
+
filter: string;
|
|
552
|
+
}
|
|
553
|
+
/** One page of server data returned by a {@link MkDataFetcher}. */
|
|
554
|
+
interface MkDataPage<T> {
|
|
555
|
+
/** The rows for the requested page. */
|
|
556
|
+
rows: T[];
|
|
557
|
+
/** Total number of rows across ALL pages (drives the pager). */
|
|
558
|
+
total: number;
|
|
559
|
+
}
|
|
560
|
+
/**
|
|
561
|
+
* Loads one page of data for a {@link MkDataRequest}. May return a `Promise`
|
|
562
|
+
* (e.g. `fetch`) or an `Observable` (e.g. `HttpClient`); an Observable is
|
|
563
|
+
* treated as single-shot — the first emission wins and the subscription is
|
|
564
|
+
* released.
|
|
565
|
+
*/
|
|
566
|
+
type MkDataFetcher<T> = (req: MkDataRequest) => Promise<MkDataPage<T>> | Observable<MkDataPage<T>>;
|
|
567
|
+
/** Construction options for {@link MkTableDataSource}. */
|
|
568
|
+
interface MkTableDataSourceOptions {
|
|
569
|
+
/** Initial rows per page (default 10, matching `mk-pagination`). */
|
|
570
|
+
pageSize?: number;
|
|
571
|
+
/** Debounce for {@link MkTableDataSource.setFilter} in ms (default 300). */
|
|
572
|
+
filterDebounce?: number;
|
|
573
|
+
}
|
|
574
|
+
/**
|
|
575
|
+
* Server-side data adapter for `mk-table` — the page/sort/filter plumbing every
|
|
576
|
+
* admin screen otherwise hand-rolls. A plain class (no component, no injection
|
|
577
|
+
* required): give it a fetcher and bind its signals; every setter re-queries
|
|
578
|
+
* the server and **stale responses never overwrite newer state** (latest-wins).
|
|
579
|
+
*
|
|
580
|
+
* - `setFilter` is debounced (default 300 ms); page, sort and page size load
|
|
581
|
+
* immediately. Sort, filter and page-size changes reset to page 1.
|
|
582
|
+
* - `rows` keeps its previous value while loading and on error, so the table
|
|
583
|
+
* never blanks mid-transition; `error` is cleared by the next successful
|
|
584
|
+
* load.
|
|
585
|
+
* - Created in an injection context (a component field initialiser) it hooks
|
|
586
|
+
* `DestroyRef` and cleans up automatically; anywhere else, call
|
|
587
|
+
* {@link destroy} yourself.
|
|
588
|
+
*
|
|
589
|
+
* ```ts
|
|
590
|
+
* interface User { id: number; name: string; email: string; }
|
|
591
|
+
*
|
|
592
|
+
* @Component({
|
|
593
|
+
* imports: [MkTable, MkPagination, MkInput],
|
|
594
|
+
* template: `
|
|
595
|
+
* <input
|
|
596
|
+
* mkInput
|
|
597
|
+
* type="search"
|
|
598
|
+
* placeholder="Search users…"
|
|
599
|
+
* (input)="ds.setFilter($any($event.target).value)"
|
|
600
|
+
* />
|
|
601
|
+
*
|
|
602
|
+
* <mk-table
|
|
603
|
+
* [columns]="columns"
|
|
604
|
+
* [data]="ds.rows()"
|
|
605
|
+
* (sortChange)="ds.setSort($event)"
|
|
606
|
+
* />
|
|
607
|
+
* @if (ds.error()) { <p role="alert">Failed to load.</p> }
|
|
608
|
+
* @if (ds.empty()) { <p>No users match.</p> }
|
|
609
|
+
*
|
|
610
|
+
* <mk-pagination
|
|
611
|
+
* [total]="ds.total()"
|
|
612
|
+
* [pageSize]="ds.pageSize()"
|
|
613
|
+
* [page]="ds.page()"
|
|
614
|
+
* (pageChange)="ds.setPage($event)"
|
|
615
|
+
* />
|
|
616
|
+
* `,
|
|
617
|
+
* })
|
|
618
|
+
* export class UsersPage {
|
|
619
|
+
* private readonly http = inject(HttpClient);
|
|
620
|
+
*
|
|
621
|
+
* readonly columns: MkTableColumn<User>[] = [
|
|
622
|
+
* { key: 'name', header: 'Name', sortable: true },
|
|
623
|
+
* { key: 'email', header: 'Email', sortable: true },
|
|
624
|
+
* ];
|
|
625
|
+
*
|
|
626
|
+
* // Field initialiser = injection context, so cleanup is automatic.
|
|
627
|
+
* readonly ds = new MkTableDataSource<User>(
|
|
628
|
+
* (req) =>
|
|
629
|
+
* this.http.get<MkDataPage<User>>('/api/users', {
|
|
630
|
+
* params: {
|
|
631
|
+
* page: req.page,
|
|
632
|
+
* size: req.pageSize,
|
|
633
|
+
* q: req.filter,
|
|
634
|
+
* ...(req.sort && {
|
|
635
|
+
* sort: `${req.sort.active},${req.sort.direction}`,
|
|
636
|
+
* }),
|
|
637
|
+
* },
|
|
638
|
+
* }),
|
|
639
|
+
* { pageSize: 20 },
|
|
640
|
+
* );
|
|
641
|
+
* }
|
|
642
|
+
* ```
|
|
643
|
+
*
|
|
644
|
+
* With a custom `mkSort` table, forward the directive instead of binding:
|
|
645
|
+
*
|
|
646
|
+
* ```ts
|
|
647
|
+
* private readonly sort = viewChild.required(MkSort);
|
|
648
|
+
* constructor() {
|
|
649
|
+
* afterNextRender(() => this.ds.connectSort(this.sort()));
|
|
650
|
+
* }
|
|
651
|
+
* ```
|
|
652
|
+
*/
|
|
653
|
+
declare class MkTableDataSource<T> {
|
|
654
|
+
private readonly fetcher;
|
|
655
|
+
private readonly debounceMs;
|
|
656
|
+
private readonly _rows;
|
|
657
|
+
private readonly _total;
|
|
658
|
+
private readonly _loading;
|
|
659
|
+
private readonly _error;
|
|
660
|
+
private readonly _page;
|
|
661
|
+
private readonly _pageSize;
|
|
662
|
+
private readonly _sort;
|
|
663
|
+
private readonly _filter;
|
|
664
|
+
/** Rows of the current page (`[]` until the first load lands). */
|
|
665
|
+
readonly rows: Signal<T[]>;
|
|
666
|
+
/** Total row count across all pages (feed to `mk-pagination`'s `total`). */
|
|
667
|
+
readonly total: Signal<number>;
|
|
668
|
+
/** True while the LATEST request is in flight. */
|
|
669
|
+
readonly loading: Signal<boolean>;
|
|
670
|
+
/** The last load's error, or `null`; cleared by the next successful load. */
|
|
671
|
+
readonly error: Signal<unknown>;
|
|
672
|
+
/** Current 1-based page. */
|
|
673
|
+
readonly page: Signal<number>;
|
|
674
|
+
/** Current page size. */
|
|
675
|
+
readonly pageSize: Signal<number>;
|
|
676
|
+
/** Current sort, or `null` when unsorted. */
|
|
677
|
+
readonly sort: Signal<MkSortState | null>;
|
|
678
|
+
/** Current filter query (updates immediately, even while debouncing). */
|
|
679
|
+
readonly filter: Signal<string>;
|
|
680
|
+
/** True when a settled load reported no rows at all. */
|
|
681
|
+
readonly empty: Signal<boolean>;
|
|
682
|
+
/** Monotonic request id — settles from older epochs are discarded. */
|
|
683
|
+
private epoch;
|
|
684
|
+
/** Subscription to an in-flight Observable fetch, if any. */
|
|
685
|
+
private activeSub;
|
|
686
|
+
/** Pending filter-debounce timer. */
|
|
687
|
+
private filterTimer;
|
|
688
|
+
/** Subscriptions created by {@link connectSort}, keyed for idempotence. */
|
|
689
|
+
private readonly sortSubs;
|
|
690
|
+
private destroyed;
|
|
691
|
+
constructor(fetcher: MkDataFetcher<T>, opts?: MkTableDataSourceOptions);
|
|
692
|
+
/** Jump to a 1-based page and load it immediately. */
|
|
693
|
+
setPage(page: number): void;
|
|
694
|
+
/** Change the page size; resets to page 1 and loads immediately. */
|
|
695
|
+
setPageSize(size: number): void;
|
|
696
|
+
/**
|
|
697
|
+
* Change the sort; resets to page 1 and loads immediately. Accepts either
|
|
698
|
+
* the `mkSort` directive's {@link MkSortState} or `mk-table`'s
|
|
699
|
+
* {@link MkSortChange} payload; a cleared sort (`direction: 'none'` or an
|
|
700
|
+
* empty column id) normalises to `null`. A no-op when the sort is unchanged.
|
|
701
|
+
*/
|
|
702
|
+
setSort(sort: MkSortState | MkSortChange | null): void;
|
|
703
|
+
/**
|
|
704
|
+
* Change the free-text filter. The `filter` signal updates (and the page
|
|
705
|
+
* resets to 1) immediately, but the request is debounced — `refresh()`
|
|
706
|
+
* flushes it early. A no-op when the query is unchanged.
|
|
707
|
+
*/
|
|
708
|
+
setFilter(query: string): void;
|
|
709
|
+
/**
|
|
710
|
+
* Re-run the current request immediately (e.g. after a mutation). Flushes a
|
|
711
|
+
* pending debounced filter, since the request reads the live filter value.
|
|
712
|
+
*/
|
|
713
|
+
refresh(): void;
|
|
714
|
+
/**
|
|
715
|
+
* Pipe an `mkSort` directive's changes into {@link setSort}. Idempotent per
|
|
716
|
+
* directive instance; all subscriptions are released by {@link destroy}.
|
|
717
|
+
*/
|
|
718
|
+
connectSort(sort: MkSort): void;
|
|
719
|
+
/**
|
|
720
|
+
* Cancel pending work: the debounce timer, any in-flight Observable fetch,
|
|
721
|
+
* and `connectSort` subscriptions. In-flight Promise settles are discarded.
|
|
722
|
+
* Called automatically on host destroy when created in an injection context.
|
|
723
|
+
*/
|
|
724
|
+
destroy(): void;
|
|
725
|
+
/** Start a load for the current request state; supersedes any in flight. */
|
|
726
|
+
private load;
|
|
727
|
+
/** Subscribe single-shot: first emission (or error) settles, then release. */
|
|
728
|
+
private runObservable;
|
|
729
|
+
/** Apply a successful settle, unless a newer request superseded it. */
|
|
730
|
+
private settleSuccess;
|
|
731
|
+
/** Record a failed settle (rows/total untouched), unless superseded. */
|
|
732
|
+
private settleError;
|
|
733
|
+
private cancelDebounce;
|
|
734
|
+
private unsubscribeActive;
|
|
735
|
+
private clearSub;
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
export { MkSort, MkSortHeader, MkTable, MkTableCell, MkTableDataSource, MkTableRowDetail };
|
|
739
|
+
export type { MkCellEdit, MkColumnResize, MkDataFetcher, MkDataPage, MkDataRequest, MkGroupToggle, MkSortChange, MkSortDirection, MkSortState, MkSortable, MkTableAlign, MkTableCellContext, MkTableColumn, MkTableDataSourceOptions, MkTableDensity, MkTableGroup };
|