@guiexpert/table 1.1.91 → 1.1.92
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 +5 -1
- package/css/main.css +79 -0
- package/css/table-classes.css +63 -0
- package/css/table-color-vars.css +16 -0
- package/index.cjs +58 -34
- package/index.d.ts +11 -0
- package/index.js +2425 -1243
- package/lib/table/aria/live-announcer.d.ts +9 -0
- package/lib/table/crud/crud-table-model-factory.d.ts +8 -0
- package/lib/table/data/common/event/ge-state-change-event.d.ts +30 -0
- package/lib/table/data/options/auto-restore-options.d.ts +14 -2
- package/lib/table/data/options/auto-restore-options.if.d.ts +94 -52
- package/lib/table/data/options/default-row-id.d.ts +4 -0
- package/lib/table/data/options/overlay-options.d.ts +30 -0
- package/lib/table/data/options/overlay-options.if.d.ts +79 -0
- package/lib/table/data/options/table-options.d.ts +3 -0
- package/lib/table/data/options/table-options.if.d.ts +35 -0
- package/lib/table/data/tablemodel/areamodel/area-model-array-of-arrays.d.ts +7 -0
- package/lib/table/data/tablemodel/areamodel/area-model-cell-groups.d.ts +31 -0
- package/lib/table/data/tablemodel/areamodel/area-model-object-array.d.ts +13 -0
- package/lib/table/data/tablemodel/areamodel/area-model-tree.d.ts +9 -0
- package/lib/table/data/tablemodel/areamodel/area-model.if.d.ts +19 -0
- package/lib/table/data/tablemodel/areamodel/cover-grid-body-model.d.ts +7 -0
- package/lib/table/data/tablemodel/cellgroup/cell-group.d.ts +2 -0
- package/lib/table/data/tablemodel/cellgroup/cell-group.if.d.ts +6 -0
- package/lib/table/data/tablemodel/column/column-visibility.d.ts +5 -0
- package/lib/table/ele-scope.d.ts +17 -0
- package/lib/table/event-adapter.d.ts +3 -0
- package/lib/table/event-listener.if.d.ts +8 -0
- package/lib/table/mouse-handler.d.ts +2 -0
- package/lib/table/overlay/calc-overlay-kind.d.ts +31 -0
- package/lib/table/overlay/ge-overlay-kind.type.d.ts +17 -0
- package/lib/table/overlay/table-overlay.d.ts +108 -0
- package/lib/table/render-scope.d.ts +26 -2
- package/lib/table/service/convenience-dom.service.d.ts +1 -1
- package/lib/table/state/state-storage.if.d.ts +39 -0
- package/lib/table/state/table-state-migration.d.ts +36 -0
- package/lib/table/state/table-state-part.d.ts +13 -0
- package/lib/table/state/table-state.if.d.ts +181 -0
- package/lib/table/state/table-state.service.d.ts +158 -0
- package/lib/table/table-api.d.ts +154 -0
- package/lib/table/table-scope.d.ts +78 -31
- package/package.json +1 -1
package/lib/table/ele-scope.d.ts
CHANGED
|
@@ -6,6 +6,7 @@ import { GeoData } from './data/geo-data.js';
|
|
|
6
6
|
import { AriaIds } from './aria/aria-ids.js';
|
|
7
7
|
import { AreaIdent } from './data/tablemodel/area-ident.type.js';
|
|
8
8
|
import { SideIdent } from './data/side-ident.type.js';
|
|
9
|
+
import { TableOverlay } from './overlay/table-overlay.js';
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
12
|
* Represents a store for HTML elements.
|
|
@@ -50,7 +51,16 @@ export declare class EleScope {
|
|
|
50
51
|
protected areaBodyEastGeo: GeoData;
|
|
51
52
|
/** The IDs this table uses for ARIA references (prefix from the host id). @since 1.1.91 */
|
|
52
53
|
readonly ariaIds: AriaIds;
|
|
54
|
+
/** The overlay layer over the body ("Loading…", "No rows to show"). @since 1.1.92 */
|
|
55
|
+
readonly tableOverlay: TableOverlay;
|
|
53
56
|
constructor(hostElement: HTMLDivElement, tableModel: TableModelIf, dom: ConvenienceDomService, tableOptions: TableOptionsIf);
|
|
57
|
+
/**
|
|
58
|
+
* Shows the overlay that fits the current state (requested by the API, or "no rows" for an empty body).
|
|
59
|
+
* Overwritten in {@link RenderScope}.
|
|
60
|
+
*
|
|
61
|
+
* @since 1.1.92
|
|
62
|
+
*/
|
|
63
|
+
protected updateOverlay(): void;
|
|
54
64
|
/** The values of the host attributes before the table changed them (restored by {@link removeHostAria}). */
|
|
55
65
|
private readonly hostAttributesBefore;
|
|
56
66
|
/**
|
|
@@ -60,6 +70,13 @@ export declare class EleScope {
|
|
|
60
70
|
* @since 1.1.91
|
|
61
71
|
*/
|
|
62
72
|
protected setHostAriaAttribute(key: string, value: string | undefined): void;
|
|
73
|
+
/**
|
|
74
|
+
* Gives an ARIA attribute of the host back the value it had before the table changed it (e.g. `aria-busy` after
|
|
75
|
+
* loading), or removes it if the host had none.
|
|
76
|
+
*
|
|
77
|
+
* @since 1.1.92
|
|
78
|
+
*/
|
|
79
|
+
protected restoreHostAriaAttribute(key: string): void;
|
|
63
80
|
/**
|
|
64
81
|
* Removes the ARIA attributes the table has set on the host element (see {@link initHostAria}).
|
|
65
82
|
*
|
|
@@ -4,6 +4,7 @@ import { GeScrollEvent } from './data/common/event/ge-scroll-event.js';
|
|
|
4
4
|
import { EventListenerIf } from './event-listener.if.js';
|
|
5
5
|
import { SelectionModelIf } from './selection/selection-model.if.js';
|
|
6
6
|
import { FocusModelIf } from './focus/focus-model.if.js';
|
|
7
|
+
import { GeStateChangeEvent } from './data/common/event/ge-state-change-event.js';
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
10
|
* Empty implementation of {@link EventListenerIf} – extend it and override only the events you need.
|
|
@@ -30,4 +31,6 @@ export declare class EventAdapter implements EventListenerIf {
|
|
|
30
31
|
onScroll(_evt: GeScrollEvent): void;
|
|
31
32
|
onSelectionChanged(_model: SelectionModelIf): void;
|
|
32
33
|
onFocusChanged(_model: FocusModelIf): void;
|
|
34
|
+
/** @since 1.1.92 */
|
|
35
|
+
onStateChanged(_evt: GeStateChangeEvent): void;
|
|
33
36
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { GeMouseEvent } from './data/common/event/ge-mouse-event.js';
|
|
2
2
|
import { GeModelChangeEvent } from './data/common/event/ge-model-change-event.js';
|
|
3
3
|
import { GeScrollEvent } from './data/common/event/ge-scroll-event.js';
|
|
4
|
+
import { GeStateChangeEvent } from './data/common/event/ge-state-change-event.js';
|
|
4
5
|
import { SelectionModelIf } from './selection/selection-model.if.js';
|
|
5
6
|
import { FocusModelIf } from './focus/focus-model.if.js';
|
|
6
7
|
import { EventFocusChangedListenerIf } from './focus/event-focus-changed-listener.if.js';
|
|
@@ -55,4 +56,11 @@ export interface EventListenerIf extends EventFocusChangedListenerIf, EventSelec
|
|
|
55
56
|
onSelectionChanged(model: SelectionModelIf): void;
|
|
56
57
|
/** The focused cell changed – read it from {@link FocusModelIf.getFocus}. */
|
|
57
58
|
onFocusChanged(model: FocusModelIf): void;
|
|
59
|
+
/**
|
|
60
|
+
* The state of the table changed (column layout, sorting, scroll position, tree, checked or selected rows,
|
|
61
|
+
* header groups, custom data) – reported once after the changes settled, see {@link GeStateChangeEvent}.
|
|
62
|
+
* Save `evt.state` e.g. on your server and pass it back as {@link TableOptionsIf.initialState}.
|
|
63
|
+
* @since 1.1.92
|
|
64
|
+
*/
|
|
65
|
+
onStateChanged?(evt: GeStateChangeEvent): void;
|
|
58
66
|
}
|
|
@@ -78,4 +78,6 @@ export declare class MouseHandler {
|
|
|
78
78
|
private mouseDraggingOnFrame;
|
|
79
79
|
private mouseDraggingEndOnFrame;
|
|
80
80
|
private mouseMoveOnFrame;
|
|
81
|
+
/** A mouse event on a blocking loading overlay ({@link TableOverlay.isEventBlocked}): the table ignores it. */
|
|
82
|
+
private isBlocked;
|
|
81
83
|
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { GeOverlayKind } from './ge-overlay-kind.type.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Input of {@link calcOverlayKind}.
|
|
5
|
+
*
|
|
6
|
+
* @since 1.1.92
|
|
7
|
+
* @category Internals
|
|
8
|
+
*/
|
|
9
|
+
export interface OverlayKindInput {
|
|
10
|
+
/** The overlay requested by the API ({@link TableApi.showLoadingOverlay}, {@link TableApi.showNoRowsOverlay}). */
|
|
11
|
+
manual?: "loading" | "noRows";
|
|
12
|
+
/** `getRowCount()` of the body (after the filter); < 0 = unknown. */
|
|
13
|
+
rowCount: number;
|
|
14
|
+
/** `getUnfilteredRowCount()` of the body, `undefined` if the model cannot tell. */
|
|
15
|
+
unfilteredRowCount?: number;
|
|
16
|
+
/** {@link OverlayOptionsIf.autoNoRows} */
|
|
17
|
+
autoNoRows: boolean;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Decides which overlay the table shows. Order: a manual overlay > "no matching rows" > "no rows to show".
|
|
21
|
+
* A model that cannot tell its unfiltered row count gets "no rows" (never a wrong "no matching rows"),
|
|
22
|
+
* an unknown row count (< 0) no overlay.
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* calcOverlayKind({ rowCount: 0, unfilteredRowCount: 100, autoNoRows: true }); // "noMatches"
|
|
26
|
+
* calcOverlayKind({ manual: "loading", rowCount: 0, autoNoRows: true }); // "loading"
|
|
27
|
+
*
|
|
28
|
+
* @since 1.1.92
|
|
29
|
+
* @category Internals
|
|
30
|
+
*/
|
|
31
|
+
export declare function calcOverlayKind(input: OverlayKindInput): GeOverlayKind;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The overlay the table shows over its body:
|
|
3
|
+
* - `"none"` – no overlay;
|
|
4
|
+
* - `"loading"` – spinner + "Loading…" ({@link TableApi.showLoadingOverlay});
|
|
5
|
+
* - `"noRows"` – "No rows to show": the body is empty ({@link OverlayOptionsIf.autoNoRows}) or
|
|
6
|
+
* {@link TableApi.showNoRowsOverlay} was called;
|
|
7
|
+
* - `"noMatches"` – "No matching rows": the body has rows, but the external filter hides all of them.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* if (tableApi.getOverlayState() === "noMatches") {
|
|
11
|
+
* console.log("Reset the filter to see rows again");
|
|
12
|
+
* }
|
|
13
|
+
*
|
|
14
|
+
* @since 1.1.92
|
|
15
|
+
* @category Overlays
|
|
16
|
+
*/
|
|
17
|
+
export type GeOverlayKind = "none" | "loading" | "noRows" | "noMatches";
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { ConvenienceDomService } from '../service/convenience-dom.service.js';
|
|
2
|
+
import { OverlayOptionsIf } from '../data/options/overlay-options.if.js';
|
|
3
|
+
import { OverlayOptions } from '../data/options/overlay-options.js';
|
|
4
|
+
import { GeOverlayKind } from './ge-overlay-kind.type.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* A change of the shown overlay, reported by {@link TableOverlay.update}.
|
|
8
|
+
*
|
|
9
|
+
* @since 1.1.92
|
|
10
|
+
* @category Internals
|
|
11
|
+
*/
|
|
12
|
+
export interface TableOverlayChange {
|
|
13
|
+
previous: GeOverlayKind;
|
|
14
|
+
kind: GeOverlayKind;
|
|
15
|
+
/** the text of the new overlay (`""` for `"none"`) */
|
|
16
|
+
text: string;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* The overlay layer over the table body: "Loading…" with a CSS spinner, "No rows to show", "No matching rows".
|
|
20
|
+
* One element per table (`.ge-table-overlay`, a child of the host, `aria-hidden` – the table announces the texts
|
|
21
|
+
* through its live region instead). It is created once and only changed when the shown overlay changes, so
|
|
22
|
+
* scrolling costs one comparison per frame.
|
|
23
|
+
*
|
|
24
|
+
* Apps use it through {@link TableApi.showLoadingOverlay}, {@link TableApi.showNoRowsOverlay},
|
|
25
|
+
* {@link TableApi.hideOverlay} and {@link TableApi.getOverlayState}; texts and behaviour come from
|
|
26
|
+
* {@link TableOptionsIf.overlayOptions}.
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* const overlay = new TableOverlay(hostDiv, new ConvenienceDomService(new SimpleDomService()), {}, () => {
|
|
30
|
+
* overlay.update(0, 0);
|
|
31
|
+
* });
|
|
32
|
+
* overlay.showLoading("Saving…"); // calls the update callback
|
|
33
|
+
* console.log(overlay.getState()); // "loading"
|
|
34
|
+
* overlay.destroy();
|
|
35
|
+
*
|
|
36
|
+
* @since 1.1.92
|
|
37
|
+
* @category Internals
|
|
38
|
+
*/
|
|
39
|
+
export declare class TableOverlay {
|
|
40
|
+
private readonly dom;
|
|
41
|
+
private readonly requestUpdate;
|
|
42
|
+
/** The overlay element (`.ge-table-overlay`). */
|
|
43
|
+
readonly element: HTMLDivElement;
|
|
44
|
+
/** The content box, centered in the overlay (`.ge-table-overlay-content`). */
|
|
45
|
+
readonly contentElement: HTMLDivElement;
|
|
46
|
+
readonly options: OverlayOptions;
|
|
47
|
+
private manual?;
|
|
48
|
+
private delayTimeout?;
|
|
49
|
+
private shownKind;
|
|
50
|
+
private shownText;
|
|
51
|
+
private shownBlocking;
|
|
52
|
+
private bounds;
|
|
53
|
+
private destroyed;
|
|
54
|
+
/**
|
|
55
|
+
* @param {HTMLDivElement} hostElement - the table host; the overlay is appended to it
|
|
56
|
+
* @param {ConvenienceDomService} dom - DOM access of the table
|
|
57
|
+
* @param {Partial<OverlayOptionsIf>} [overlayOptions] - {@link TableOptionsIf.overlayOptions}
|
|
58
|
+
* @param {() => void} [requestUpdate] - called when the requested overlay changed (API call, end of the
|
|
59
|
+
* delay); the table answers with {@link update}
|
|
60
|
+
*/
|
|
61
|
+
constructor(hostElement: HTMLDivElement, dom: ConvenienceDomService, overlayOptions?: Partial<OverlayOptionsIf>, requestUpdate?: () => void);
|
|
62
|
+
/**
|
|
63
|
+
* Requests the loading overlay (after {@link OverlayOptionsIf.loadingDelayInMs}).
|
|
64
|
+
*
|
|
65
|
+
* @param {string} [text] - text for this overlay, default {@link OverlayOptionsIf.loadingText}
|
|
66
|
+
*/
|
|
67
|
+
showLoading(text?: string): void;
|
|
68
|
+
/**
|
|
69
|
+
* Requests the "no rows" overlay, also over rows and with {@link OverlayOptionsIf.autoNoRows} `false`.
|
|
70
|
+
*
|
|
71
|
+
* @param {string} [text] - text for this overlay, default {@link OverlayOptionsIf.noRowsText}
|
|
72
|
+
*/
|
|
73
|
+
showNoRows(text?: string): void;
|
|
74
|
+
/** Ends a requested overlay; the automatic "no rows" / "no matching rows" stays as long as the body is empty. */
|
|
75
|
+
hide(): void;
|
|
76
|
+
/** The overlay that is shown now. */
|
|
77
|
+
getState(): GeOverlayKind;
|
|
78
|
+
/** `true` while a loading overlay blocks mouse and keyboard ({@link OverlayOptionsIf.blockInteractionWhileLoading}). */
|
|
79
|
+
isBlocking(): boolean;
|
|
80
|
+
/**
|
|
81
|
+
* `true` for a mouse event on a blocking loading overlay: the table ignores it (click, double click, mouse down,
|
|
82
|
+
* context menu, hover). The event is not stopped – page handlers (e.g. "click outside closes the menu") see it.
|
|
83
|
+
*
|
|
84
|
+
* @param {Event} evt - a mouse event that reached the host
|
|
85
|
+
* @returns {boolean} the table must not react to it
|
|
86
|
+
*/
|
|
87
|
+
isEventBlocked(evt: Event): boolean;
|
|
88
|
+
/**
|
|
89
|
+
* Shows the overlay that fits the body: a requested one, else "no matching rows" / "no rows to show"
|
|
90
|
+
* for an empty body. Changes the DOM only if the result differs from what is shown.
|
|
91
|
+
*
|
|
92
|
+
* @param {number} rowCount - `getRowCount()` of the body
|
|
93
|
+
* @param {number} [unfilteredRowCount] - `getUnfilteredRowCount()` of the body (only needed for 0 rows)
|
|
94
|
+
* @returns {TableOverlayChange | undefined} the change, `undefined` if nothing changed
|
|
95
|
+
*/
|
|
96
|
+
update(rowCount: number, unfilteredRowCount?: number): TableOverlayChange | undefined;
|
|
97
|
+
/**
|
|
98
|
+
* Places the overlay over the body (px, relative to the host). Unchanged values do not touch the DOM.
|
|
99
|
+
*/
|
|
100
|
+
setBounds(left: number, top: number, width: number, height: number): void;
|
|
101
|
+
/** Removes the overlay element and a waiting delay. Calling it twice is harmless. */
|
|
102
|
+
destroy(): void;
|
|
103
|
+
private getText;
|
|
104
|
+
private render;
|
|
105
|
+
/** The element of {@link OverlayOptionsIf.getOverlayElement}; an error falls back to the built-in content. */
|
|
106
|
+
private getOwnElement;
|
|
107
|
+
private clearDelay;
|
|
108
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { TableOverlayChange } from './overlay/table-overlay.js';
|
|
1
2
|
import { EleScope } from './ele-scope.js';
|
|
2
3
|
import { TableModelIf } from './data/tablemodel/table-model.if.js';
|
|
3
4
|
import { ConvenienceDomService } from './service/convenience-dom.service.js';
|
|
@@ -34,11 +35,11 @@ export declare class RenderScope extends EleScope {
|
|
|
34
35
|
protected dragging: boolean;
|
|
35
36
|
protected editing: boolean;
|
|
36
37
|
protected storedColumnWidths: number[];
|
|
38
|
+
/** @deprecated since 1.1.92 – no longer used, see {@link TableStateService}. */
|
|
37
39
|
protected storeScrollPosStateService?: StoreStateScrollPosService;
|
|
38
40
|
protected getSelectionModel?: GetT<SelectionModelIf>;
|
|
39
41
|
protected getFocusModel?: GetT<FocusModelIf>;
|
|
40
42
|
protected scrollLeft: number;
|
|
41
|
-
private scrollPosSaveTimeout?;
|
|
42
43
|
protected scrollViewportLeft: number;
|
|
43
44
|
protected scrollFactorY: number;
|
|
44
45
|
protected scrollFactorX: number;
|
|
@@ -145,7 +146,30 @@ export declare class RenderScope extends EleScope {
|
|
|
145
146
|
*/
|
|
146
147
|
adjustAfterScrolling(): void;
|
|
147
148
|
/**
|
|
148
|
-
*
|
|
149
|
+
* Called at the end of every render pass (scroll, repaint). {@link TableScope} marks the scroll position of the
|
|
150
|
+
* table state as changed here (stored and reported debounced, see {@link TableStateService}).
|
|
151
|
+
*
|
|
152
|
+
* @since 1.1.92
|
|
153
|
+
*/
|
|
154
|
+
protected afterRenderPass(): void;
|
|
155
|
+
/**
|
|
156
|
+
* Shows the overlay that fits the body now (see {@link TableOverlay.update}) and reports a change to
|
|
157
|
+
* {@link onOverlayChanged}.
|
|
158
|
+
*
|
|
159
|
+
* @since 1.1.92
|
|
160
|
+
*/
|
|
161
|
+
protected updateOverlay(): void;
|
|
162
|
+
/**
|
|
163
|
+
* Called after the shown overlay changed (ARIA in {@link TableScope}).
|
|
164
|
+
*
|
|
165
|
+
* @param {TableOverlayChange} _change - previous and new overlay, its text
|
|
166
|
+
* @since 1.1.92
|
|
167
|
+
*/
|
|
168
|
+
protected onOverlayChanged(_change: TableOverlayChange): void;
|
|
169
|
+
/**
|
|
170
|
+
* Marks the scroll position as changed (it is stored with the table state, see {@link TableStateService}).
|
|
171
|
+
*
|
|
172
|
+
* @deprecated since 1.1.92 – the table does this after every render pass.
|
|
149
173
|
*
|
|
150
174
|
* @return {void}
|
|
151
175
|
*/
|
|
@@ -41,7 +41,7 @@ export declare class ConvenienceDomService {
|
|
|
41
41
|
applyStylePosistionRelative(ele: HTMLDivElement): HTMLDivElement;
|
|
42
42
|
applyStylePosistionAbsolute(ele: HTMLDivElement): HTMLDivElement;
|
|
43
43
|
applyStyleFullSize(ele: HTMLDivElement): HTMLDivElement;
|
|
44
|
-
applyStyleOverflowAuto(overflowX: "
|
|
44
|
+
applyStyleOverflowAuto(overflowX: "scroll" | "auto" | undefined, overflowY: "scroll" | "auto" | undefined, ele: HTMLDivElement): HTMLDivElement;
|
|
45
45
|
applyStyleNoPadding(ele: HTMLDivElement): HTMLDivElement;
|
|
46
46
|
appendRelativeChildDiv(parent: HTMLDivElement): DivScope;
|
|
47
47
|
appendText(parent: HTMLDivElement, text: string): HTMLElement;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where {@link AutoRestoreOptionsIf} stores the state of a table ({@link AutoRestoreOptionsIf.storage}). A synchronous
|
|
3
|
+
* key-value store with the method names of the Web Storage API – `localStorage` (default) and `sessionStorage`
|
|
4
|
+
* fit directly.
|
|
5
|
+
*
|
|
6
|
+
* The table reads the state while it is built, so the storage must answer synchronously. To keep the state on a
|
|
7
|
+
* server, load it before you create the table and pass it as {@link TableOptionsIf.initialState}; save it in
|
|
8
|
+
* {@link EventListenerIf.onStateChanged}.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* const tableOptions: TableOptionsIf = {
|
|
12
|
+
* ...new TableOptions(),
|
|
13
|
+
* autoRestoreOptions: {
|
|
14
|
+
* ...new AutoRestoreOptions<Order>(),
|
|
15
|
+
* getStorageKeyFn: () => "orders",
|
|
16
|
+
* storage: sessionStorage, // forget the layout when the tab is closed
|
|
17
|
+
* autoRestoreColumnLayout: true
|
|
18
|
+
* }
|
|
19
|
+
* };
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* // an own storage, e.g. a prefix for all keys
|
|
23
|
+
* const prefixed: StateStorageIf = {
|
|
24
|
+
* getItem: key => localStorage.getItem("my-app/" + key),
|
|
25
|
+
* setItem: (key, value) => localStorage.setItem("my-app/" + key, value),
|
|
26
|
+
* removeItem: key => localStorage.removeItem("my-app/" + key)
|
|
27
|
+
* };
|
|
28
|
+
*
|
|
29
|
+
* @category State
|
|
30
|
+
* @since 1.1.92
|
|
31
|
+
*/
|
|
32
|
+
export interface StateStorageIf {
|
|
33
|
+
/** The stored string or `null`. */
|
|
34
|
+
getItem(key: string): string | null;
|
|
35
|
+
/** Stores a string. May throw (quota) – the table catches it. */
|
|
36
|
+
setItem(key: string, value: string): void;
|
|
37
|
+
/** Removes a key. */
|
|
38
|
+
removeItem(key: string): void;
|
|
39
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { StateStorageIf } from './state-storage.if.js';
|
|
2
|
+
import { TableStateIf } from './table-state.if.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Reads the state that tables before 1.1.92 stored under `<key>sortingState`, `<key>scrollState` and
|
|
6
|
+
* `<key>collapsedExpandedState` and converts it to a {@link TableStateIf}. The stored sort items hold a column
|
|
7
|
+
* INDEX – it is converted to the property of the column at that index in the default column order (the order a
|
|
8
|
+
* table of the old version always started with).
|
|
9
|
+
*
|
|
10
|
+
* @param {StateStorageIf} storage - the storage
|
|
11
|
+
* @param {string} key - the storage key of the table (`getStorageKeyFn()`)
|
|
12
|
+
* @param {string[]} properties - the properties of the columns in the default order
|
|
13
|
+
* @returns {TableStateIf | undefined} the state, `undefined` if nothing was stored
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* localStorage.setItem("orderssortingState", '[{"columnIndex":2,"sortState":"desc"}]');
|
|
17
|
+
* readLegacyState(localStorage, "orders", ["id", "customer", "total"]);
|
|
18
|
+
* // → { version: 1, sort: [{ property: "total", sortState: "desc" }] }
|
|
19
|
+
*
|
|
20
|
+
* @category Internals
|
|
21
|
+
* @since 1.1.92
|
|
22
|
+
*/
|
|
23
|
+
export declare function readLegacyState(storage: StateStorageIf, key: string, properties: string[]): TableStateIf | undefined;
|
|
24
|
+
/**
|
|
25
|
+
* Removes the keys of tables before 1.1.92 (see {@link readLegacyState}).
|
|
26
|
+
*
|
|
27
|
+
* @param {StateStorageIf} storage - the storage
|
|
28
|
+
* @param {string} key - the storage key of the table
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* removeLegacyState(localStorage, "orders");
|
|
32
|
+
*
|
|
33
|
+
* @category Internals
|
|
34
|
+
* @since 1.1.92
|
|
35
|
+
*/
|
|
36
|
+
export declare function removeLegacyState(storage: StateStorageIf, key: string): void;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { TableStatePart } from './table-state.if.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* All parts of a {@link TableStateIf}, in the order the table applies them.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* const withoutScroll = ALL_TABLE_STATE_PARTS.filter(part => part !== "scroll");
|
|
8
|
+
* const state = tableApi.getState(withoutScroll);
|
|
9
|
+
*
|
|
10
|
+
* @category State
|
|
11
|
+
* @since 1.1.92
|
|
12
|
+
*/
|
|
13
|
+
export declare const ALL_TABLE_STATE_PARTS: readonly TableStatePart[];
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import { SortState } from '../data/common/sort-state.type.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The id of a row in a {@link TableStateIf} – what {@link AutoRestoreOptionsIf.getRowId} returns.
|
|
5
|
+
*
|
|
6
|
+
* @category State
|
|
7
|
+
* @since 1.1.92
|
|
8
|
+
*/
|
|
9
|
+
export type TableStateRowId = string | number;
|
|
10
|
+
/**
|
|
11
|
+
* One part of a {@link TableStateIf}. Use it to read or apply only some parts:
|
|
12
|
+
* `tableApi.getState(["columns", "sort"])`.
|
|
13
|
+
*
|
|
14
|
+
* | Part | Content | {@link AutoRestoreOptionsIf} flag |
|
|
15
|
+
* |---|---|---|
|
|
16
|
+
* | `columns` | width, order and visibility of the columns | `autoRestoreColumnLayout` |
|
|
17
|
+
* | `sort` | sorted columns | `autoRestoreSortingState` |
|
|
18
|
+
* | `scroll` | scroll position | `autoRestoreScrollPosition` |
|
|
19
|
+
* | `tree` | expanded / collapsed tree nodes | `autoRestoreCollapsedExpandedState` |
|
|
20
|
+
* | `checked` | checked rows (row checkboxes) | `autoRestoreCheckedState` |
|
|
21
|
+
* | `selection` | selected rows, columns, cells or ranges | `autoRestoreSelectedState` |
|
|
22
|
+
* | `headerGroups` | collapsed header groups | `autoRestoreHeaderGroupState` |
|
|
23
|
+
* | `custom` | data of your application ({@link TableApi.setCustomState}) | `autoRestoreCustomState` |
|
|
24
|
+
*
|
|
25
|
+
* @category State
|
|
26
|
+
* @since 1.1.92
|
|
27
|
+
*/
|
|
28
|
+
export type TableStatePart = "columns" | "sort" | "scroll" | "tree" | "checked" | "selection" | "headerGroups" | "custom";
|
|
29
|
+
/**
|
|
30
|
+
* The state of a table as plain JSON: column layout, sorting, scroll position, tree, checked and selected rows,
|
|
31
|
+
* header groups and data of your own. Get it with {@link TableApi.getState}, apply it with
|
|
32
|
+
* {@link TableApi.applyState} or pass it at the start as {@link TableOptionsIf.initialState} – e.g. to store it on
|
|
33
|
+
* your server per user. {@link TableOptionsIf.autoRestoreOptions} stores it in `localStorage` for you.
|
|
34
|
+
*
|
|
35
|
+
* Every part is optional: a missing part is not applied (the table keeps its current state of that part).
|
|
36
|
+
* Columns are identified by their property, rows by their id ({@link AutoRestoreOptionsIf.getRowId}) – never by an
|
|
37
|
+
* index, so the state survives moved columns and rows that arrive in another order. Unknown columns and rows are
|
|
38
|
+
* ignored. The state holds no cell values – but row ids can be personal data (e.g. an e-mail address as id).
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* const state: TableStateIf = tableApi.getState();
|
|
42
|
+
* localStorage.setItem("orders-layout", JSON.stringify(state));
|
|
43
|
+
* // later, e.g. in another table:
|
|
44
|
+
* tableApi.applyState(JSON.parse(localStorage.getItem("orders-layout")!) as TableStateIf);
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* // hand-written: "total" first and 240 px wide, "city" hidden, sorted by total descending
|
|
48
|
+
* const state: TableStateIf = {
|
|
49
|
+
* version: 1,
|
|
50
|
+
* columns: [{ property: "total", width: 240 }, { property: "id" }, { property: "city", visible: false }],
|
|
51
|
+
* sort: [{ property: "total", sortState: "desc" }]
|
|
52
|
+
* };
|
|
53
|
+
*
|
|
54
|
+
* @see Training: [Remember the state](https://gui.expert/training/persist-state/)
|
|
55
|
+
*
|
|
56
|
+
* @category State
|
|
57
|
+
* @since 1.1.92
|
|
58
|
+
*/
|
|
59
|
+
export interface TableStateIf {
|
|
60
|
+
/** Format version, always `1`. */
|
|
61
|
+
version: 1;
|
|
62
|
+
/**
|
|
63
|
+
* The columns in their order (fixed columns included). Columns of the table that are missing here keep their
|
|
64
|
+
* place; unknown properties are ignored.
|
|
65
|
+
*/
|
|
66
|
+
columns?: TableColumnStateIf[];
|
|
67
|
+
/** The sorted columns, the first one is the primary sort key. `[]` = unsorted. */
|
|
68
|
+
sort?: TableSortStateIf[];
|
|
69
|
+
/** The scroll position. */
|
|
70
|
+
scroll?: TableScrollStateIf;
|
|
71
|
+
/** Expanded / collapsed nodes of a tree table. */
|
|
72
|
+
tree?: TableTreeStateIf;
|
|
73
|
+
/** The ids of the checked rows. */
|
|
74
|
+
checkedRowIds?: TableStateRowId[];
|
|
75
|
+
/** The selection. */
|
|
76
|
+
selection?: TableSelectionStateIf;
|
|
77
|
+
/** The state of the header groups with a toggle. */
|
|
78
|
+
headerGroups?: TableHeaderGroupStateIf[];
|
|
79
|
+
/** Data of your application, e.g. the text of a filter field (must be JSON). */
|
|
80
|
+
custom?: unknown;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Width, visibility and (by its position in {@link TableStateIf.columns}) order of one column.
|
|
84
|
+
*
|
|
85
|
+
* @category State
|
|
86
|
+
* @since 1.1.92
|
|
87
|
+
*/
|
|
88
|
+
export interface TableColumnStateIf {
|
|
89
|
+
/** The property of the column ({@link ColumnDefIf.property}). */
|
|
90
|
+
property: string;
|
|
91
|
+
/** Width in px – only set for columns the user (or `setColumnWidth`) resized; otherwise the width of the code. */
|
|
92
|
+
width?: number;
|
|
93
|
+
/** `false` = hidden with {@link TableApi.setColumnVisible} (a collapsed header group is stored separately). */
|
|
94
|
+
visible?: boolean;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* One sorted column.
|
|
98
|
+
*
|
|
99
|
+
* @category State
|
|
100
|
+
* @since 1.1.92
|
|
101
|
+
*/
|
|
102
|
+
export interface TableSortStateIf {
|
|
103
|
+
/** The property of the column. */
|
|
104
|
+
property?: string;
|
|
105
|
+
/** Only for tables without column definitions (and the format before 1.1.92): the column index. */
|
|
106
|
+
columnIndex?: number;
|
|
107
|
+
/** `"asc"` or `"desc"`. */
|
|
108
|
+
sortState: SortState;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* The scroll position.
|
|
112
|
+
*
|
|
113
|
+
* @category State
|
|
114
|
+
* @since 1.1.92
|
|
115
|
+
*/
|
|
116
|
+
export interface TableScrollStateIf {
|
|
117
|
+
/** Horizontal scroll position in px. */
|
|
118
|
+
left: number;
|
|
119
|
+
/** Vertical scroll position in px. */
|
|
120
|
+
top: number;
|
|
121
|
+
/**
|
|
122
|
+
* The id of the first visible row. If other rows are at the top after restoring `top` (new data), the table
|
|
123
|
+
* scrolls to this row.
|
|
124
|
+
*/
|
|
125
|
+
firstRowId?: TableStateRowId;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Expanded and collapsed nodes of a tree. Only one of the two lists is set – the shorter one:
|
|
129
|
+
* `expandedRowIds` = these nodes are expanded, all others collapsed; `collapsedRowIds` = the other way round.
|
|
130
|
+
* `{ collapsedRowIds: [] }` = all expanded.
|
|
131
|
+
*
|
|
132
|
+
* @category State
|
|
133
|
+
* @since 1.1.92
|
|
134
|
+
*/
|
|
135
|
+
export interface TableTreeStateIf {
|
|
136
|
+
/** These nodes are expanded, all other nodes are collapsed. */
|
|
137
|
+
expandedRowIds?: TableStateRowId[];
|
|
138
|
+
/** These nodes are collapsed, all other nodes are expanded. */
|
|
139
|
+
collapsedRowIds?: TableStateRowId[];
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* The selection: rectangles between rows (ids) and columns (properties).
|
|
143
|
+
*
|
|
144
|
+
* @category State
|
|
145
|
+
* @since 1.1.92
|
|
146
|
+
*/
|
|
147
|
+
export interface TableSelectionStateIf {
|
|
148
|
+
/** Everything selected (⌘/Ctrl+A). */
|
|
149
|
+
allSelected?: boolean;
|
|
150
|
+
/** The selected rectangles. */
|
|
151
|
+
ranges: TableStateRangeIf[];
|
|
152
|
+
/** Rectangles removed from the selection (Ctrl/⌘+Alt-click). */
|
|
153
|
+
negativeRanges?: TableStateRangeIf[];
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* A rectangle of a selection. Without row ids it spans all rows (a selected column), without properties all columns
|
|
157
|
+
* (a selected row); without `toRowId` / `toProperty` it reaches to the last row / column. Restored in the current
|
|
158
|
+
* row order: a rectangle between the same first and last row. Tables without column definitions store only
|
|
159
|
+
* row selections (columns have no property there).
|
|
160
|
+
*
|
|
161
|
+
* @category State
|
|
162
|
+
* @since 1.1.92
|
|
163
|
+
*/
|
|
164
|
+
export interface TableStateRangeIf {
|
|
165
|
+
fromRowId?: TableStateRowId;
|
|
166
|
+
toRowId?: TableStateRowId;
|
|
167
|
+
fromProperty?: string;
|
|
168
|
+
toProperty?: string;
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Collapsed or expanded header group ({@link CellGroupIf} with `toggle: true`).
|
|
172
|
+
*
|
|
173
|
+
* @category State
|
|
174
|
+
* @since 1.1.92
|
|
175
|
+
*/
|
|
176
|
+
export interface TableHeaderGroupStateIf {
|
|
177
|
+
/** {@link CellGroupIf.id}, otherwise the path of the labels, e.g. `"Customer"` or `"2026/Q1"`. */
|
|
178
|
+
id: string;
|
|
179
|
+
/** `true` = collapsed. */
|
|
180
|
+
closed: boolean;
|
|
181
|
+
}
|