@guiexpert/table 1.1.91 → 1.1.93

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.
Files changed (43) hide show
  1. package/README.md +5 -1
  2. package/css/main.css +79 -0
  3. package/css/table-classes.css +63 -0
  4. package/css/table-color-vars.css +16 -0
  5. package/index.cjs +58 -34
  6. package/index.d.ts +11 -0
  7. package/index.js +2425 -1243
  8. package/lib/table/aria/live-announcer.d.ts +9 -0
  9. package/lib/table/crud/crud-table-model-factory.d.ts +8 -0
  10. package/lib/table/data/common/event/ge-state-change-event.d.ts +30 -0
  11. package/lib/table/data/options/auto-restore-options.d.ts +14 -2
  12. package/lib/table/data/options/auto-restore-options.if.d.ts +94 -52
  13. package/lib/table/data/options/default-row-id.d.ts +4 -0
  14. package/lib/table/data/options/overlay-options.d.ts +30 -0
  15. package/lib/table/data/options/overlay-options.if.d.ts +79 -0
  16. package/lib/table/data/options/table-options.d.ts +3 -0
  17. package/lib/table/data/options/table-options.if.d.ts +35 -0
  18. package/lib/table/data/tablemodel/areamodel/area-model-array-of-arrays.d.ts +7 -0
  19. package/lib/table/data/tablemodel/areamodel/area-model-cell-groups.d.ts +31 -0
  20. package/lib/table/data/tablemodel/areamodel/area-model-object-array.d.ts +13 -0
  21. package/lib/table/data/tablemodel/areamodel/area-model-tree.d.ts +9 -0
  22. package/lib/table/data/tablemodel/areamodel/area-model.if.d.ts +19 -0
  23. package/lib/table/data/tablemodel/areamodel/cover-grid-body-model.d.ts +7 -0
  24. package/lib/table/data/tablemodel/cellgroup/cell-group.d.ts +2 -0
  25. package/lib/table/data/tablemodel/cellgroup/cell-group.if.d.ts +6 -0
  26. package/lib/table/data/tablemodel/column/column-visibility.d.ts +5 -0
  27. package/lib/table/ele-scope.d.ts +17 -0
  28. package/lib/table/event-adapter.d.ts +3 -0
  29. package/lib/table/event-listener.if.d.ts +8 -0
  30. package/lib/table/mouse-handler.d.ts +2 -0
  31. package/lib/table/overlay/calc-overlay-kind.d.ts +31 -0
  32. package/lib/table/overlay/ge-overlay-kind.type.d.ts +17 -0
  33. package/lib/table/overlay/table-overlay.d.ts +108 -0
  34. package/lib/table/render-scope.d.ts +26 -2
  35. package/lib/table/service/convenience-dom.service.d.ts +1 -1
  36. package/lib/table/state/state-storage.if.d.ts +39 -0
  37. package/lib/table/state/table-state-migration.d.ts +36 -0
  38. package/lib/table/state/table-state-part.d.ts +13 -0
  39. package/lib/table/state/table-state.if.d.ts +181 -0
  40. package/lib/table/state/table-state.service.d.ts +158 -0
  41. package/lib/table/table-api.d.ts +154 -0
  42. package/lib/table/table-scope.d.ts +78 -31
  43. package/package.json +1 -1
@@ -31,6 +31,7 @@ export declare class LiveAnnouncer {
31
31
  static defaultDelayInMs: number;
32
32
  private element?;
33
33
  private timeout?;
34
+ private pendingText?;
34
35
  constructor(document: Document | undefined, id: string, domService: DomServiceIf);
35
36
  /**
36
37
  * Speaks a message (replaces a message that is still waiting).
@@ -39,6 +40,14 @@ export declare class LiveAnnouncer {
39
40
  * @param {number} [delayInMs=LiveAnnouncer.defaultDelayInMs] - wait this long before the text is written
40
41
  */
41
42
  announce(text: string, delayInMs?: number): void;
43
+ /**
44
+ * Drops a message that is still waiting (not spoken yet) – only if it is `text` (or any message without `text`).
45
+ * Used when the reason for the message is gone, e.g. "Loading…" after the data arrived.
46
+ *
47
+ * @param {string} [text] - only this message is dropped
48
+ * @since 1.1.92
49
+ */
50
+ cancel(text?: string): void;
42
51
  /**
43
52
  * The live region element (created on the first call).
44
53
  *
@@ -56,6 +56,7 @@ export declare class CrudTableModelFactory implements ActionEventListenerIf {
56
56
  * list is loaded again (`fetchList`);
57
57
  * - `DELETE`: `deleteItem(id)` → the row is removed.
58
58
  *
59
+ * While a request runs, the table shows its loading overlay ({@link TableApi.showLoadingOverlay}, since 1.1.92).
59
60
  * A failed request is logged with `console.error`; the table stays unchanged.
60
61
  */
61
62
  onActionEvent(actionEvent: ActionEventIf): void;
@@ -67,6 +68,13 @@ export declare class CrudTableModelFactory implements ActionEventListenerIf {
67
68
  private addRow;
68
69
  /** Loads the list again (`fetchList`) – after a create whose response does not contain the new item. */
69
70
  private reloadRows;
71
+ /**
72
+ * Shows the loading overlay of the table while `request` runs (also when it fails). Before the table exists
73
+ * (first `fetchList` in {@link createTableModel}) there is nothing to show it on.
74
+ */
75
+ private whileLoading;
76
+ /** Requests on the way ({@link whileLoading}). */
77
+ private pendingRequests;
70
78
  private getIdKey;
71
79
  /** The action column carries the id as text (data-id) – compare as strings. */
72
80
  private hasId;
@@ -0,0 +1,30 @@
1
+ import { TableStateIf, TableStatePart } from '../../../state/table-state.if.js';
2
+
3
+ /**
4
+ * The state of a table changed ({@link EventListenerIf.onStateChanged}): a column was resized, moved, hidden,
5
+ * the table was sorted, scrolled, a node expanded, a row checked or selected … Reported once after the changes
6
+ * settled ({@link TableOptionsIf.stateChangedDebounceDelay}, default 300 ms) and only if a part really changed;
7
+ * not while the table is built.
8
+ *
9
+ * @example
10
+ * const listener = new EventAdapter();
11
+ * listener.onStateChanged = (evt: GeStateChangeEvent) => {
12
+ * if (!evt.changedParts.includes("scroll")) { // e.g. ignore pure scrolling
13
+ * void fetch("/api/prefs/orders", { method: "PUT", body: JSON.stringify(evt.state) });
14
+ * }
15
+ * };
16
+ *
17
+ * @category State
18
+ * @since 1.1.92
19
+ */
20
+ export declare class GeStateChangeEvent {
21
+ /** The whole state of the table now (all parts). */
22
+ readonly state: TableStateIf;
23
+ /** The parts that changed since the last event. */
24
+ readonly changedParts: TableStatePart[];
25
+ constructor(
26
+ /** The whole state of the table now (all parts). */
27
+ state: TableStateIf,
28
+ /** The parts that changed since the last event. */
29
+ changedParts: TableStatePart[]);
30
+ }
@@ -2,7 +2,7 @@ import { AutoRestoreOptionsIf } from './auto-restore-options.if.js';
2
2
 
3
3
  /**
4
4
  * Defaults for {@link TableOptionsIf.autoRestoreOptions}: everything off. Spread it and switch on what the table
5
- * should remember in `localStorage` (one key per table).
5
+ * should remember in `localStorage` (one key per table) – see {@link AutoRestoreOptionsIf} for all flags.
6
6
  *
7
7
  * @example
8
8
  * const tableOptions: TableOptionsIf = {
@@ -11,7 +11,8 @@ import { AutoRestoreOptionsIf } from './auto-restore-options.if.js';
11
11
  * ...new AutoRestoreOptions<Order>(),
12
12
  * getStorageKeyFn: () => "orders-table", // unique per table
13
13
  * autoRestoreSortingState: true,
14
- * autoRestoreScrollPosition: true
14
+ * autoRestoreScrollPosition: true,
15
+ * autoRestoreColumnLayout: true // widths, order, visibility (since 1.1.92)
15
16
  * }
16
17
  * };
17
18
  *
@@ -37,7 +38,18 @@ export declare class AutoRestoreOptions<T> implements AutoRestoreOptionsIf<T> {
37
38
  autoRestoreScrollPosition: boolean;
38
39
  autoRestoreCheckedState: boolean;
39
40
  autoRestoreSelectedState: boolean;
41
+ /** @since 1.1.92 */
42
+ autoRestoreColumnLayout: boolean;
43
+ /** @since 1.1.92 */
44
+ autoRestoreHeaderGroupState: boolean;
45
+ /** @since 1.1.92 */
46
+ autoRestoreCustomState: boolean;
40
47
  getStorageKeyFn: undefined;
48
+ /** @deprecated since 1.1.92 – never used by the table. */
41
49
  isSame: (r1: T, r2: T, options: AutoRestoreOptionsIf<T>) => boolean;
50
+ /**
51
+ * Default: the property `id`, then `uuid`, then the JSON of the row. The table itself uses only `id` / `uuid` of
52
+ * this default (since 1.1.92): rows without them are not stored – the JSON would write whole rows into the storage.
53
+ */
42
54
  getRowId: (row: T) => string | number;
43
55
  }
@@ -1,81 +1,123 @@
1
+ import { StateStorageIf } from '../../state/state-storage.if.js';
2
+
1
3
  /**
2
- * `IsSameFn` is a type alias for a function used within the `AutoRestoreOptionsIf` interface. The function is designed to compare
3
- * two instances of a generic type `T`, in conjunction with supplied options of type `AutoRestoreOptionsIf<T>`.
4
+ * `IsSameFn` compares two rows with the help of {@link AutoRestoreOptionsIf.getRowId}.
4
5
  *
5
- * This function is used for determining if two instances of `T` are considered the same when restoring the state of an application.
6
- * This helps in preserving specific states of the application, like sorting or selected state, across different stages or conditions
7
- * based on the developer's implementation. By default, this function uses strict equality check (`===`) if not provided.
8
- *
9
- * @typedef
10
- * @typeparam T - The type of the objects that the function is comparing.
11
- *
12
- * @param {T} r1 - The first object instance of type `T`.
13
- * @param {T} r2 - The second object instance of type `T`.
14
- * @param {AutoRestoreOptionsIf<T>} optionsIf - The options that dictate the behavior of auto-restoring.
15
- *
16
- * @returns {boolean} - Returns `true` if the two instances are considered the same, `false` otherwise.
6
+ * @deprecated since 1.1.92 – never used by the table; rows are compared by {@link AutoRestoreOptionsIf.getRowId}.
17
7
  *
8
+ * @typeparam T - The type of the rows.
18
9
  * @category Options
19
10
  */
20
11
  export type IsSameFn<T> = (r1: T, r2: T, optionsIf: AutoRestoreOptionsIf<T>) => boolean;
21
12
  /**
22
- * GetRowIdFn is a type alias for a function that accepts an instance of type T and returns a unique identifier
23
- * (either a string or a number) for that instance. It is used for restoring state in conjunction with the
24
- * AutoRestoreOptionsIf interface.
25
- * @typedef
26
- * @typeparam T - The type of the object that the function is going to use.
13
+ * Returns a unique, stable id for a row – used to remember expanded tree nodes, checked and selected rows and the
14
+ * first visible row ({@link AutoRestoreOptionsIf}, {@link TableStateIf}).
15
+ *
16
+ * Tree tables get the object of the node (`treeRow.data`). Models without row objects (e.g. an own `AreaModel`
17
+ * that computes its values) get `undefined` and the displayed row index – declare the second parameter to support
18
+ * them: `(_row, rowIndex) => rowIndex`.
27
19
  *
20
+ * @example
21
+ * const getRowId: GetRowIdFn<Order> = (order: Order) => order.id;
22
+ *
23
+ * @typeparam T - The type of the rows.
28
24
  * @category Options
29
25
  */
30
- export type GetRowIdFn<T> = ((row: T) => string | number);
26
+ export type GetRowIdFn<T> = ((row: T, rowIndex?: number) => string | number);
31
27
  /**
32
- * GetStorageKeyFn is a type alias for a function that returns a unique key (as a string) for storage.
33
- * It is used for restoring state in conjunction with the AutoRestoreOptionsIf interface.
34
- * @typedef
28
+ * Returns the storage key of a table – one key per table, e.g. `() => "orders"`. The state is stored under
29
+ * `<key>tableState`.
35
30
  *
36
31
  * @category Options
37
32
  */
38
33
  export type GetStorageKeyFn = (() => string);
39
34
  /**
40
- * The AutoRestoreOptionsIf interface is utilized to encapsulate options
41
- * for managing auto-restoring settings across different states of the application.
42
- * This interface is generic and can be used with any type.
43
- *
44
- * @interface
45
- * @typeparam T - Any type that this interface is going to use.
46
- * @property {boolean} autoRestoreSortingState - If true, the sorting state of
47
- * T elements will be preserved after changes in the application state.
48
- *
49
- * @property {boolean=} autoRestoreCollapsedExpandedState - Optional. If true,
50
- * the expanded/collapsed state of T elements will be preserved after changes in the application state.
51
- *
52
- * @property {boolean=} autoRestoreScrollPosition - Optional. If true, the scroll position
53
- * in the list of T elements will be preserved after changes in the application state.
54
- *
55
- * @property {boolean=} autoRestoreCheckedState - Optional. If true, the checked state of
56
- * T elements will be preserved after changes in the application state.
57
- *
58
- * @property {boolean=} autoRestoreSelectedState - Optional. If true, the selected state of
59
- * T elements will be preserved after changes in the application state.
60
- *
61
- * @property {(row: T) => string | number} getRowId - Function for generating a unique identifier
62
- * for an instance of T. If not provided, each instance of T will create a new entry every time state is saved.
63
- *
64
- * @property {() => string} getStorageKeyFn - Function for generating a unique key used for storage.
65
- * If not provided, a constant key 'autorestoreoptionsif' will be used.
66
- *
67
- * @property {(r1: T, r2: T, optionsIf: AutoRestoreOptionsIf<T>) => boolean} isSame - Function for checking
68
- * if two instances of T are considered the same when restoring state. If not provided, strict equality check (===) will be used.
35
+ * What a table remembers between page loads, and where. Everything is off by default ({@link AutoRestoreOptions});
36
+ * nothing is stored without {@link getStorageKeyFn} (the table warns once if a flag is set without it).
37
+ *
38
+ * The state is stored as one JSON value under `<key>tableState` in {@link storage} (default `localStorage`) –
39
+ * the same format as {@link TableApi.getState}. Columns are stored by property and rows by {@link getRowId}, so a
40
+ * moved column or new data never hits the wrong column or row. The stored state holds no cell values, but row ids
41
+ * can be personal data (e.g. an e-mail address as id) – prefer technical ids. The tree, checked rows, the selection
42
+ * and the first visible row need row ids; tree, checked and selected rows are computed only when their flag is set.
43
+ *
44
+ * | Flag | Remembers | since |
45
+ * |---|---|---|
46
+ * | {@link autoRestoreSortingState} | sorted columns | |
47
+ * | {@link autoRestoreScrollPosition} | scroll position | |
48
+ * | {@link autoRestoreCollapsedExpandedState} | expanded tree nodes | |
49
+ * | {@link autoRestoreColumnLayout} | width, order, visibility of the columns | 1.1.92 |
50
+ * | {@link autoRestoreHeaderGroupState} | collapsed header groups | 1.1.92 |
51
+ * | {@link autoRestoreCheckedState} | checked rows | 1.1.92 (declared before, without effect) |
52
+ * | {@link autoRestoreSelectedState} | selection | 1.1.92 (declared before, without effect) |
53
+ * | {@link autoRestoreCustomState} | your data ({@link TableApi.setCustomState}) | 1.1.92 |
54
+ *
55
+ * Before 1.1.92 sorting, scroll position and tree were stored under three keys (`<key>sortingState`,
56
+ * `<key>scrollState`, `<key>collapsedExpandedState`); they are read once and removed with the next save.
57
+ *
58
+ * @example
59
+ * const tableOptions: TableOptionsIf = {
60
+ * ...new TableOptions(),
61
+ * autoRestoreOptions: {
62
+ * ...new AutoRestoreOptions<Order>(),
63
+ * getStorageKeyFn: () => "orders",
64
+ * getRowId: (order: Order) => order.id,
65
+ * autoRestoreColumnLayout: true,
66
+ * autoRestoreSortingState: true,
67
+ * autoRestoreCheckedState: true
68
+ * }
69
+ * };
70
+ *
71
+ * @typeparam T - The type of the rows.
72
+ * @see Training: [Remember the state](https://gui.expert/training/persist-state/)
69
73
  *
70
74
  * @category Options
71
75
  */
72
76
  export interface AutoRestoreOptionsIf<T> {
77
+ /** Remember the sorted columns (by property). */
73
78
  autoRestoreSortingState: boolean;
79
+ /** Remember expanded and collapsed nodes of a tree table (by {@link getRowId}). */
74
80
  autoRestoreCollapsedExpandedState?: boolean;
81
+ /** Remember the scroll position (and the id of the first visible row). */
75
82
  autoRestoreScrollPosition?: boolean;
83
+ /** Remember the checked rows (by {@link getRowId}). Since 1.1.92 (before: declared without effect). */
76
84
  autoRestoreCheckedState?: boolean;
85
+ /**
86
+ * Remember the selection: rows by {@link getRowId}, columns by property. Restored in the current row order.
87
+ * Since 1.1.92 (before: declared without effect).
88
+ */
77
89
  autoRestoreSelectedState?: boolean;
90
+ /**
91
+ * Remember width, order and visibility of the columns (by property). Widths are stored only for columns that were
92
+ * resized, so `%` and weight columns stay responsive.
93
+ * @since 1.1.92
94
+ */
95
+ autoRestoreColumnLayout?: boolean;
96
+ /**
97
+ * Remember collapsed and expanded header groups ({@link CellGroupIf.toggle}).
98
+ * @since 1.1.92
99
+ */
100
+ autoRestoreHeaderGroupState?: boolean;
101
+ /**
102
+ * Remember the data set with {@link TableApi.setCustomState} (e.g. a filter text).
103
+ * @since 1.1.92
104
+ */
105
+ autoRestoreCustomState?: boolean;
106
+ /**
107
+ * A unique, stable id per row (tree: per node). With the default of {@link AutoRestoreOptions} the table uses the
108
+ * property `id`, then `uuid`; rows without them are not stored (since 1.1.92 – the JSON of the row would write
109
+ * personal data into the storage; the table warns once).
110
+ */
78
111
  getRowId?: undefined | GetRowIdFn<T>;
112
+ /** The storage key of this table, e.g. `() => "orders"`. Without it nothing is stored. */
79
113
  getStorageKeyFn?: undefined | GetStorageKeyFn;
114
+ /**
115
+ * @deprecated since 1.1.92 – never used; rows are compared by {@link getRowId}.
116
+ */
80
117
  isSame?: undefined | IsSameFn<T>;
118
+ /**
119
+ * Where the state is stored. Default: `localStorage`; `sessionStorage` or an own {@link StateStorageIf} also work.
120
+ * @since 1.1.92
121
+ */
122
+ storage?: StateStorageIf;
81
123
  }
@@ -0,0 +1,4 @@
1
+ /** Marks a function as the default `getRowId` of {@link AutoRestoreOptions}. */
2
+ export declare function markDefaultGetRowId<F extends object>(fn: F): F;
3
+ /** true for the default `getRowId` of {@link AutoRestoreOptions} (also after spreading the options). */
4
+ export declare function isDefaultGetRowId(fn: unknown): boolean;
@@ -0,0 +1,30 @@
1
+ import { OverlayOptionsIf } from './overlay-options.if.js';
2
+ import { GeOverlayKind } from '../../overlay/ge-overlay-kind.type.js';
3
+
4
+ /**
5
+ * The defaults of {@link OverlayOptionsIf}: English texts, an automatic "No rows to show", a loading overlay that
6
+ * blocks the body and appears right away.
7
+ *
8
+ * @example
9
+ * const overlayOptions = OverlayOptions.create({ noRowsText: "Keine Daten" });
10
+ * console.log(overlayOptions.loadingText); // "Loading…"
11
+ *
12
+ * @since 1.1.92
13
+ * @category Overlays
14
+ */
15
+ export declare class OverlayOptions implements OverlayOptionsIf {
16
+ loadingText: string;
17
+ noRowsText: string;
18
+ noMatchesText: string;
19
+ autoNoRows: boolean;
20
+ blockInteractionWhileLoading: boolean;
21
+ loadingDelayInMs: number;
22
+ getOverlayElement?: (kind: GeOverlayKind, text: string) => HTMLElement | undefined;
23
+ /**
24
+ * The defaults with the given fields on top (`undefined` fields keep their default).
25
+ *
26
+ * @param {Partial<OverlayOptionsIf>} [partial] - e.g. `tableOptions.overlayOptions`
27
+ * @returns {OverlayOptions} a new object
28
+ */
29
+ static create(partial?: Partial<OverlayOptionsIf>): OverlayOptions;
30
+ }
@@ -0,0 +1,79 @@
1
+ import { GeOverlayKind } from '../../overlay/ge-overlay-kind.type.js';
2
+
3
+ /**
4
+ * Texts and behaviour of the overlays over the table body: "Loading…" ({@link TableApi.showLoadingOverlay}),
5
+ * "No rows to show" (empty body) and "No matching rows" (the external filter hides all rows).
6
+ *
7
+ * Set it with {@link TableOptionsIf.overlayOptions} – every field is optional there, missing ones keep their
8
+ * default ({@link OverlayOptions}). The overlays are plain HTML and CSS (spinner: `@keyframes`), colours come from
9
+ * the CSS variables `--ge-table-overlay-bg`, `--ge-table-overlay-color`, `--ge-table-spinner-color`,
10
+ * `--ge-table-spinner-track-color` and `--ge-table-spinner-size`.
11
+ *
12
+ * @example
13
+ * const tableOptions: TableOptionsIf = {
14
+ * ...new TableOptions(),
15
+ * overlayOptions: {
16
+ * loadingText: "Lädt…",
17
+ * noRowsText: "Keine Daten",
18
+ * noMatchesText: "Keine Treffer",
19
+ * loadingDelayInMs: 200, // fast answers show no spinner at all
20
+ * },
21
+ * };
22
+ *
23
+ * @since 1.1.92
24
+ * @see Training: [Live data](https://gui.expert/training/live-data/)
25
+ * @category Overlays
26
+ */
27
+ export interface OverlayOptionsIf {
28
+ /** Text of the loading overlay. Default: `"Loading…"`. */
29
+ loadingText: string;
30
+ /** Text of the overlay of an empty body. Default: `"No rows to show"`. */
31
+ noRowsText: string;
32
+ /** Text when the external filter hides all rows. Default: `"No matching rows"`. */
33
+ noMatchesText: string;
34
+ /**
35
+ * `true` (default): an empty body shows "No rows to show" (or "No matching rows") by itself, after every
36
+ * repaint. `false`: only {@link TableApi.showNoRowsOverlay} shows it.
37
+ */
38
+ autoNoRows: boolean;
39
+ /**
40
+ * `true` (default): while "Loading…" is shown, the table ignores mouse events on the body (click, double click,
41
+ * hover, context menu – the page still sees them) and the keyboard cannot start editing or toggle rows in the body;
42
+ * navigation, copying and the header still work.
43
+ * `false`: the loading overlay is only a picture, the table stays usable.
44
+ * "No rows" / "No matching rows" never block.
45
+ */
46
+ blockInteractionWhileLoading: boolean;
47
+ /**
48
+ * Waits this long (ms) before the loading overlay appears. Answers that come faster show no spinner at all
49
+ * (no flashing); meanwhile an automatic "No rows to show" stays as it is. Default: `0` (right away).
50
+ */
51
+ loadingDelayInMs: number;
52
+ /**
53
+ * Own content of an overlay instead of the built-in one (spinner + text). Called once each time the overlay
54
+ * of a kind appears; return `undefined` to keep the built-in content for that kind. The table removes the
55
+ * element again when the overlay disappears or changes and in {@link TableApi.destroy}.
56
+ *
57
+ * The overlay is hidden from screen readers (`aria-hidden`, the table announces its text instead):
58
+ * put no buttons or links into it.
59
+ *
60
+ * @param {GeOverlayKind} kind - `"loading"`, `"noRows"` or `"noMatches"`
61
+ * @param {string} text - the text the built-in content would show
62
+ * @returns {HTMLElement | undefined} the element to show, or `undefined` for the default
63
+ *
64
+ * @example
65
+ * const tableOptions: TableOptionsIf = {
66
+ * ...new TableOptions(),
67
+ * overlayOptions: {
68
+ * getOverlayElement: (kind: GeOverlayKind, text: string) => {
69
+ * if (kind !== "noRows") return undefined; // built-in spinner and texts for the others
70
+ * const div = document.createElement("div");
71
+ * div.className = "my-empty-state";
72
+ * div.textContent = `📭 ${text}`;
73
+ * return div;
74
+ * },
75
+ * },
76
+ * };
77
+ */
78
+ getOverlayElement?: (kind: GeOverlayKind, text: string) => HTMLElement | undefined;
79
+ }
@@ -11,6 +11,7 @@ import { FocusModelIf } from '../../focus/focus-model.if.js';
11
11
  import { ShortcutActionIdMapping } from '../../action/shortcut-actionid-mapping.type.js';
12
12
  import { HeaderGroupOptions } from './header-group-options.js';
13
13
  import { AriaTextsIf } from '../../aria/aria-texts.if.js';
14
+ import { OverlayOptionsIf } from './overlay-options.if.js';
14
15
 
15
16
  /**
16
17
  * All default options of a table – see {@link TableOptionsIf} for the meaning of each field.
@@ -77,4 +78,6 @@ export declare class TableOptions implements TableOptionsIf {
77
78
  ariaDescribedBy?: string;
78
79
  /** Own screen reader texts; missing functions use the English defaults ({@link AriaTexts}). @since 1.1.91 */
79
80
  ariaTexts?: Partial<AriaTextsIf>;
81
+ /** Texts and behaviour of the "Loading…" / "No rows" overlays; missing fields use {@link OverlayOptions}. @since 1.1.92 */
82
+ overlayOptions?: Partial<OverlayOptionsIf>;
80
83
  }
@@ -11,6 +11,8 @@ import { FocusModelIf } from '../../focus/focus-model.if.js';
11
11
  import { ShortcutActionIdMapping } from '../../action/shortcut-actionid-mapping.type.js';
12
12
  import { HeaderGroupOptionsIf } from './header-group-options.if.js';
13
13
  import { AriaTextsIf } from '../../aria/aria-texts.if.js';
14
+ import { TableStateIf } from '../../state/table-state.if.js';
15
+ import { OverlayOptionsIf } from './overlay-options.if.js';
14
16
 
15
17
  /**
16
18
  * Represents the options for configuring a table.
@@ -301,4 +303,37 @@ export interface TableOptionsIf {
301
303
  * @since 1.1.91
302
304
  */
303
305
  ariaTexts?: Partial<AriaTextsIf>;
306
+ /**
307
+ * Texts and behaviour of the overlays over the body: "Loading…" ({@link TableApi.showLoadingOverlay}),
308
+ * "No rows to show" (empty body, automatic) and "No matching rows" (the filter hides all rows).
309
+ * Fields you leave out keep their default ({@link OverlayOptions}).
310
+ *
311
+ * @example
312
+ * const tableOptions: TableOptionsIf = {
313
+ * ...new TableOptions(),
314
+ * overlayOptions: { noRowsText: "No orders yet", blockInteractionWhileLoading: false },
315
+ * };
316
+ *
317
+ * @since 1.1.92
318
+ */
319
+ overlayOptions?: Partial<OverlayOptionsIf>;
320
+ /**
321
+ * The state the table starts with: column layout, sorting, tree, checked and selected rows … – e.g. loaded from
322
+ * your server ({@link TableApi.getState}, {@link EventListenerIf.onStateChanged}). A state stored by
323
+ * {@link autoRestoreOptions} wins over it, part by part.
324
+ *
325
+ * @example
326
+ * const saved = (await (await fetch("/api/prefs/orders")).json()) as TableStateIf | null;
327
+ * const tableOptions: TableOptionsIf = { ...new TableOptions(), initialState: saved ?? undefined };
328
+ *
329
+ * @since 1.1.92
330
+ */
331
+ initialState?: Partial<TableStateIf>;
332
+ /**
333
+ * Delay in ms between the last change of the state (resize, sort, scroll …) and
334
+ * {@link EventListenerIf.onStateChanged} / the save of {@link autoRestoreOptions}. Default 300.
335
+ *
336
+ * @since 1.1.92
337
+ */
338
+ stateChangedDebounceDelay?: number;
304
339
  }
@@ -18,6 +18,13 @@ export declare class AreaModelArrayOfArrays<T> extends AbstractAreaModel<T[][]>
18
18
  constructor(areaIdent: AreaIdent, arr: T[][], defaultRowHeight: number, columnDefs?: ColumnDefIf[]);
19
19
  externalFilterChanged(predictFn: FilterFunction<any>): void;
20
20
  getRowCount(): number;
21
+ /**
22
+ * The number of all rows, without the external filter.
23
+ *
24
+ * @returns {number} the length of the array
25
+ * @since 1.1.92
26
+ */
27
+ getUnfilteredRowCount(): number;
21
28
  getValueAt(rowIndex: number, columnIndex: number): any;
22
29
  setValue(rowIndex: number, columnIndex: number, value: any): boolean;
23
30
  getRowByIndex(rowIndex: number): any;
@@ -10,6 +10,7 @@ import { CheckedType } from '../../common/checked-type.js';
10
10
  import { CellGroupExt } from '../cellgroup/cell-group-ext.js';
11
11
  import { MouseTargetData } from '../../event/mouse-target-data.js';
12
12
  import { HeaderGroupOptionsIf } from '../../options/header-group-options.if.js';
13
+ import { TableHeaderGroupStateIf } from '../../../state/table-state.if.js';
13
14
 
14
15
  /** @category Header groups */
15
16
  export declare class AreaModelCellGroups implements AreaModelIf {
@@ -56,4 +57,34 @@ export declare class AreaModelCellGroups implements AreaModelIf {
56
57
  setRowChecked(_rowIndex: number, _checked: boolean): void;
57
58
  setValue(_rowIndex: number, _columnIndex: number, _value: any): boolean;
58
59
  toggleHeaderGroup(mouseTargetData: MouseTargetData): ColumnDefIf[];
60
+ /**
61
+ * The header groups the user can collapse (`toggle: true`, not `visibility: "always"`) with their id
62
+ * ({@link CellGroupIf.id}, otherwise the path of the labels, e.g. `"Customer"` or `"2026/Q1"`; a second group with
63
+ * the same path gets `#2`) and their state.
64
+ *
65
+ * @returns {TableHeaderGroupStateIf[]} id and `closed` per group, in header order
66
+ * @example
67
+ * const header = tableApi.getTableModel().getAreaModel("header") as AreaModelCellGroups;
68
+ * console.log(header.getToggleGroupStates()); // [{ id: "Customer", closed: false }]
69
+ * @since 1.1.92
70
+ */
71
+ getToggleGroupStates(): TableHeaderGroupStateIf[];
72
+ /**
73
+ * Collapses or expands a header group by its id (see {@link getToggleGroupStates}) and returns the column
74
+ * definitions to use from now on – like a click on its toggle. {@link TableApi.setHeaderGroupCollapsed} does this
75
+ * and repaints.
76
+ *
77
+ * @param {string} id - the id of the group
78
+ * @param {boolean} closed - true = collapse
79
+ * @returns {ColumnDefIf[] | undefined} the merged column definitions, `undefined` if there is no such group
80
+ * @since 1.1.92
81
+ */
82
+ setGroupClosed(id: string, closed: boolean): ColumnDefIf[] | undefined;
83
+ /** The toggleable groups by id (header order). */
84
+ private getToggleGroups;
85
+ /**
86
+ * Keep the existing ColumnDef objects (width, classes, renderers, sorting …) – only their visibility
87
+ * comes from the rebuilt groups. Fresh defs would reset every width to the default (100 px).
88
+ */
89
+ private mergeColumnDefs;
59
90
  }
@@ -267,6 +267,19 @@ export declare class AreaModelObjectArray<T> extends AbstractAreaModel<T> implem
267
267
  * console.log(`The table has ${rowCount} rows`);
268
268
  */
269
269
  getRowCount(): number;
270
+ /**
271
+ * The number of all rows, without the external filter (the filter only shrinks {@link getRowCount}).
272
+ *
273
+ * @returns {number} `getAllRows().length`
274
+ *
275
+ * @example
276
+ * if (bodyAreaModel.getRowCount() === 0 && bodyAreaModel.getUnfilteredRowCount() > 0) {
277
+ * console.log("The filter hides all rows");
278
+ * }
279
+ *
280
+ * @since 1.1.92
281
+ */
282
+ getUnfilteredRowCount(): number;
270
283
  /**
271
284
  * Gets the value at the specified row and column indices.
272
285
  * This method handles tree rows and nested properties.
@@ -113,6 +113,15 @@ export declare class AreaModelTree<S> extends AbstractAreaModel<TreeRow<S>> {
113
113
  */
114
114
  getFilteredFlattenRows(): TreeRowIf<S>[];
115
115
  getRowCount(): number;
116
+ /**
117
+ * The number of rows the tree shows without the external filter: all nodes whose parents are expanded
118
+ * (collapsed children do not count). `0` only for a tree without rows.
119
+ *
120
+ * @returns {number} displayed rows before filtering
121
+ *
122
+ * @since 1.1.92
123
+ */
124
+ getUnfilteredRowCount(): number;
116
125
  getValueAt(rowIndex: number, columnIndex: number): any;
117
126
  getRowHeight(_rowIndex: number): number;
118
127
  /**
@@ -261,6 +261,25 @@ export interface AreaModelIf {
261
261
  * }
262
262
  */
263
263
  getRowCount(): number;
264
+ /**
265
+ * The number of rows the area would show **without** the external filter. Optional: the table uses it to tell
266
+ * "No rows to show" (no rows at all) from "No matching rows" (the filter hides all rows) – see
267
+ * {@link OverlayOptionsIf}. Models without it always get "No rows to show".
268
+ *
269
+ * @returns {number} e.g. all rows of an object array; for a tree the displayed rows before filtering
270
+ *
271
+ * @example
272
+ * class OrdersBodyModel extends AreaModel {
273
+ * private all: string[][] = [];
274
+ * private shown: string[][] = [];
275
+ * override getRowCount(): number { return this.shown.length; }
276
+ * getUnfilteredRowCount(): number { return this.all.length; }
277
+ * override getValueAt(rowIndex: number, columnIndex: number): any { return this.shown[rowIndex]?.[columnIndex]; }
278
+ * }
279
+ *
280
+ * @since 1.1.92
281
+ */
282
+ getUnfilteredRowCount?(): number;
264
283
  /**
265
284
  * Returns the value of the cell at the specified row and column indices.
266
285
  * This is one of the most important methods in the area model, as it provides
@@ -23,6 +23,13 @@ export declare class CoverGridBodyModel<T> implements AreaModelIf {
23
23
  constructor(items: T[], cellRenderer: CellRendererIf, coverWidth?: number, coverHeight?: number, width?: number, firstRowPaddingTop?: number);
24
24
  setParentWidth(widthInPixel: number): void;
25
25
  getRowCount(): number;
26
+ /**
27
+ * The number of grid rows without the external filter (`0` without items).
28
+ *
29
+ * @returns {number} grid rows for all items
30
+ * @since 1.1.92
31
+ */
32
+ getUnfilteredRowCount(): number;
26
33
  getColumnCount(): number;
27
34
  getValueAt(rowIndex: number, columnIndex: number): T | undefined;
28
35
  getTextValueAt(_rowIndex: number, _columnIndex: number): string;
@@ -9,5 +9,7 @@ export declare class CellGroup implements CellGroupIf {
9
9
  visibility: "always" | "inverted" | "normal" | undefined;
10
10
  children: CellGroupIf[] | undefined;
11
11
  impl: string;
12
+ /** See {@link CellGroupIf.id}. @since 1.1.92 */
13
+ id?: string;
12
14
  constructor(data: any, property?: string | undefined, toggle?: boolean | undefined, closed?: boolean, visibility?: "always" | "inverted" | "normal" | undefined, children?: CellGroupIf[] | undefined);
13
15
  }
@@ -17,6 +17,12 @@
17
17
  * @category Header groups
18
18
  */
19
19
  export interface CellGroupIf {
20
+ /**
21
+ * A stable id of the group, used to remember whether it is collapsed ({@link TableHeaderGroupStateIf},
22
+ * {@link TableApi.setHeaderGroupCollapsed}). Default: the path of the labels, e.g. `"Customer"` or `"2026/Q1"`.
23
+ * @since 1.1.92
24
+ */
25
+ id?: string;
20
26
  /** The label of the group or column. */
21
27
  data: any;
22
28
  /** Set on leaves only: the property shown in this column. */
@@ -4,3 +4,8 @@ import { ColumnDefIf } from './column-def.if.js';
4
4
  export declare function setApiColumnVisibility(columnDef: ColumnDefIf, visible: boolean): void;
5
5
  /** Sets the header group visibility of a column and rebuilds its `isVisible`. */
6
6
  export declare function setGroupColumnVisibility(columnDef: ColumnDefIf, isVisible: (() => boolean) | undefined): void;
7
+ /**
8
+ * Whether the column is shown apart from a collapsed header group: hidden with `setColumnVisible(…, false)` or by its
9
+ * own `isVisible` (e.g. `FalseFn` in the code) → false.
10
+ */
11
+ export declare function isApiColumnVisible(columnDef: ColumnDefIf): boolean;