@olenbetong/appframe-ds 1.0.3 → 1.2.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/CHANGELOG.md +13 -0
- package/package.json +8 -7
- package/scripts/copyAssets.mjs +27 -55
- package/src/AfLookup/Lookup.css +129 -125
- package/src/AfLookup/Lookup.tsx +184 -162
- package/src/AfLookup/LookupCombobox.css +78 -78
- package/src/AfLookup/LookupCombobox.tsx +237 -237
- package/src/AfLookup/LookupEdit.tsx +130 -130
- package/src/AfLookup/LookupGrid.tsx +124 -124
- package/src/AfLookup/index.ts +5 -5
- package/src/autocomplete/Autocomplete.css +256 -256
- package/src/autocomplete/Autocomplete.tsx +320 -320
- package/src/autocomplete/AutocompleteFrame.tsx +168 -168
- package/src/autocomplete/Combobox.tsx +286 -286
- package/src/autocomplete/index.ts +4 -4
- package/src/autocomplete/types.ts +204 -204
- package/src/autocomplete/utils.ts +183 -183
- package/src/binding/BoundNumericTextField.tsx +102 -102
- package/src/binding/BoundTextField.tsx +49 -49
- package/src/binding/CancelButton.tsx +30 -30
- package/src/binding/CancelIconButton.tsx +30 -30
- package/src/binding/DataEditToolbar.tsx +92 -92
- package/src/binding/DeleteButton.tsx +35 -35
- package/src/binding/DeleteIconButton.tsx +35 -35
- package/src/binding/RefreshButton.tsx +31 -31
- package/src/binding/RefreshIconButton.tsx +33 -33
- package/src/binding/RefreshRowIconButton.tsx +33 -33
- package/src/binding/SaveButton.tsx +37 -37
- package/src/binding/SaveIconButton.tsx +38 -38
- package/src/container/Container.tsx +49 -49
- package/src/container/index.ts +1 -1
- package/src/cutoff/CutOffPicker.css +12 -0
- package/src/cutoff/CutOffPicker.tsx +165 -0
- package/src/cutoff/context.tsx +194 -0
- package/src/cutoff/dsCutOffDates.ts +21 -0
- package/src/cutoff/index.ts +4 -0
- package/src/cutoff/utils.test.ts +59 -0
- package/src/cutoff/utils.ts +276 -0
- package/src/filter/ChipListInput.tsx +79 -79
- package/src/filter/FieldFilterPanel.css +150 -150
- package/src/filter/FieldFilterPanel.tsx +309 -309
- package/src/filter/FilterBuilder.css +152 -152
- package/src/filter/FilterBuilder.test.tsx +153 -153
- package/src/filter/FilterBuilder.tsx +435 -435
- package/src/filter/FilterEditor.css +231 -231
- package/src/filter/FilterEditor.test.tsx +259 -259
- package/src/filter/FilterEditor.tsx +75 -74
- package/src/filter/FilterGroupEditor.tsx +160 -160
- package/src/filter/FilterNameDialog.css +22 -22
- package/src/filter/FilterNameDialog.tsx +81 -81
- package/src/filter/FilterRow.tsx +117 -117
- package/src/filter/FilterShareDialog.css +28 -28
- package/src/filter/FilterShareDialog.tsx +201 -201
- package/src/filter/FilterStringField.tsx +78 -77
- package/src/filter/FilterValueEditor.tsx +220 -220
- package/src/filter/SavedFilterTree.css +107 -107
- package/src/filter/SavedFilterTree.test.tsx +94 -94
- package/src/filter/SavedFilterTree.tsx +218 -222
- package/src/filter/fieldFilter.test.ts +158 -158
- package/src/filter/fieldFilter.ts +139 -139
- package/src/filter/index.ts +27 -27
- package/src/filter/types.ts +38 -38
- package/src/filter/useDistinctValues.test.ts +102 -102
- package/src/filter/useDistinctValues.ts +230 -230
- package/src/global.d.ts +11 -11
- package/src/grid/AfGridColumnsPanel.tsx +118 -118
- package/src/grid/AfGridContext.tsx +51 -51
- package/src/grid/AfGridError.tsx +46 -46
- package/src/grid/AfHeaderFilterCell.tsx +147 -151
- package/src/grid/AfHeaderFilterPanel.tsx +111 -111
- package/src/grid/GridEditLookup.css +12 -0
- package/src/grid/GridEditLookup.tsx +121 -0
- package/src/grid/Toolbar.tsx +233 -233
- package/src/grid/editing.ts +100 -100
- package/src/grid/filter.ts +78 -78
- package/src/grid/formatters.ts +48 -48
- package/src/grid/index.css +245 -245
- package/src/grid/index.tsx +489 -484
- package/src/grid/license.ts +48 -48
- package/src/grid/localization.ts +369 -369
- package/src/grid/slots/index.tsx +307 -307
- package/src/grid/slots/slots.css +64 -64
- package/src/grid/theme.tsx +138 -138
- package/src/grid/useAfColumns.tsx +371 -371
- package/src/grid/useAfCurrentIndex.ts +63 -63
- package/src/grid/useAfData.ts +20 -20
- package/src/grid/useAfFilter.ts +160 -160
- package/src/grid/useAfFilterFields.ts +55 -55
- package/src/grid/useAfGridApi.ts +71 -71
- package/src/grid/useAfKeyBindings.ts +36 -36
- package/src/grid/useAfNewItemRow.ts +258 -258
- package/src/grid/useAfPagination.ts +53 -53
- package/src/grid/useAfPersistedState.ts +230 -230
- package/src/grid/useAfRowEditModel.ts +145 -145
- package/src/grid/useAfRowGrouping.ts +47 -47
- package/src/grid/useAfServerAggregation.ts +145 -145
- package/src/grid/useAfSortModel.ts +84 -84
- package/src/i18n.ts +13 -0
- package/src/index.ts +14 -12
- package/src/input/InputAdornments.css +75 -75
- package/src/input/InputAdornments.tsx +102 -102
- package/src/input/index.ts +1 -1
- package/src/layout/AppMenu.tsx +74 -75
- package/src/layout/AppMenuDrawer.css +70 -70
- package/src/layout/AppMenuDrawer.tsx +122 -122
- package/src/layout/ErrorBoundary.tsx +162 -162
- package/src/layout/ResponsiveDialog.css +86 -0
- package/src/layout/ResponsiveDialog.tsx +55 -0
- package/src/layout/index.tsx +69 -68
- package/src/link/Link.tsx +23 -0
- package/src/link/index.ts +1 -0
- package/src/mdi/Mdi.css +138 -138
- package/src/mdi/Mdi.test.tsx +91 -87
- package/src/mdi/Mdi.tsx +141 -141
- package/src/mdi/MdiConfirmCloseDialog.tsx +47 -47
- package/src/mdi/MdiContext.tsx +59 -59
- package/src/mdi/MdiDocumentContext.tsx +60 -60
- package/src/mdi/MdiPanel.tsx +61 -61
- package/src/mdi/MdiRestore.test.tsx +86 -82
- package/src/mdi/MdiTab.tsx +30 -30
- package/src/mdi/controller.test.ts +382 -382
- package/src/mdi/controller.ts +656 -656
- package/src/mdi/dockviewHost.ts +132 -132
- package/src/mdi/fakeHost.ts +200 -200
- package/src/mdi/host.ts +44 -44
- package/src/mdi/ids.test.ts +20 -20
- package/src/mdi/ids.ts +18 -18
- package/src/mdi/index.ts +31 -31
- package/src/mdi/layoutRestore.test.ts +156 -156
- package/src/mdi/layoutRestore.ts +311 -311
- package/src/mdi/layoutSpec.test.ts +143 -143
- package/src/mdi/layoutSpec.ts +214 -214
- package/src/mdi/layoutStore.test.ts +93 -93
- package/src/mdi/layoutStore.ts +99 -99
- package/src/mdi/memorySettingsStore.ts +21 -21
- package/src/mdi/registry.ts +34 -34
- package/src/mdi/testTypes.ts +37 -37
- package/src/mdi/theme.ts +22 -22
- package/src/mdi/types.ts +214 -214
- package/src/page/Page.css +53 -53
- package/src/page/PageBlock.css +64 -64
- package/src/page/PageBlock.tsx +92 -92
- package/src/page/PageHeader.css +88 -88
- package/src/page/PageHeader.tsx +115 -116
- package/src/page/PageTitle.css +5 -0
- package/src/page/PageTitle.tsx +21 -0
- package/src/page/index.ts +4 -3
- package/src/paper/Paper.tsx +52 -52
- package/src/progress/LinearProgress.css +93 -93
- package/src/progress/LinearProgress.tsx +104 -104
- package/src/progress/index.ts +1 -1
- package/src/report-downloader/ReportDownloader.css +67 -67
- package/src/report-downloader/components/StatusItem.tsx +87 -86
- package/src/report-downloader/components/StatusList.tsx +28 -28
- package/src/report-downloader/index.ts +41 -41
- package/src/report-downloader/status.tsx +92 -92
- package/src/sortable/DropIndicator.css +51 -51
- package/src/sortable/DropIndicator.tsx +27 -27
- package/src/sortable/SortableItemList.css +21 -21
- package/src/sortable/SortableItemList.tsx +264 -264
- package/src/sortable/index.ts +8 -8
- package/src/sortable/reorderSortOrder.ts +126 -126
- package/src/sortable/sortOrder.ts +101 -101
- package/src/sortable/types.ts +9 -9
- package/src/sortable/useSortableContainer.ts +53 -53
- package/src/sortable/useSortableItem.ts +95 -95
- package/src/sortable/useSortableList.ts +117 -117
- package/src/test/happy-dom-document-class.ts +24 -24
- package/src/test/setup.ts +12 -12
- package/src/theme/useDsColorScheme.ts +53 -53
- package/tsconfig.build.json +9 -9
- package/tsconfig.json +8 -8
- package/vitest.config.ts +10 -10
|
@@ -1,230 +1,230 @@
|
|
|
1
|
-
import type { GridApi, GridInitialState, GridPaginationModel } from "@mui/x-data-grid-premium";
|
|
2
|
-
|
|
3
|
-
import localforage from "localforage";
|
|
4
|
-
import { useEffect, useState } from "react";
|
|
5
|
-
|
|
6
|
-
import type { AfGridProps } from "./index.js";
|
|
7
|
-
import { getColumnVisibilityModel } from "./useAfColumns.js";
|
|
8
|
-
|
|
9
|
-
const settingsStore = localforage.createInstance({
|
|
10
|
-
name: `AfGridLayouts.${globalThis.af?.userSession?.login ?? "anonymous"}`,
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
type GridSettings = {
|
|
14
|
-
layoutVersion: string | number;
|
|
15
|
-
state: GridInitialState;
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
const settingsCache: Record<string, GridSettings> = {};
|
|
19
|
-
|
|
20
|
-
async function persistState(
|
|
21
|
-
key: string,
|
|
22
|
-
layoutVersion: string | number,
|
|
23
|
-
state: GridInitialState | null | undefined,
|
|
24
|
-
config?: StatePersistenceConfig,
|
|
25
|
-
) {
|
|
26
|
-
if (state) {
|
|
27
|
-
state = structuredClone(state);
|
|
28
|
-
|
|
29
|
-
let settings: GridSettings = {
|
|
30
|
-
layoutVersion,
|
|
31
|
-
state: removeNonPersistedState(state, config),
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
if (state.pagination?.paginationModel?.page !== undefined) {
|
|
35
|
-
delete state.pagination.paginationModel.page;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
settingsCache[key] = settings;
|
|
39
|
-
await settingsStore.setItem<GridSettings>(key, settings);
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
function removeNonPersistedState(state: GridInitialState, config?: StatePersistenceConfig): GridInitialState {
|
|
44
|
-
const newState: GridInitialState = {};
|
|
45
|
-
let columns: GridInitialState["columns"] = {};
|
|
46
|
-
let filter: GridInitialState["filter"] = {};
|
|
47
|
-
let pagination: GridInitialState["pagination"] = { paginationModel: {} };
|
|
48
|
-
let sorting: GridInitialState["sorting"] = {};
|
|
49
|
-
|
|
50
|
-
// Persist the relevant state unless explicitly disabled in the config
|
|
51
|
-
if (config?.density !== false) {
|
|
52
|
-
newState.density = state.density;
|
|
53
|
-
}
|
|
54
|
-
if (config?.pinnedColumns !== false) {
|
|
55
|
-
newState.pinnedColumns = state.pinnedColumns;
|
|
56
|
-
}
|
|
57
|
-
if (config?.rowGrouping !== false) {
|
|
58
|
-
newState.rowGrouping = state.rowGrouping;
|
|
59
|
-
}
|
|
60
|
-
if (config?.aggregation !== false) {
|
|
61
|
-
newState.aggregation = state.aggregation;
|
|
62
|
-
}
|
|
63
|
-
if (config?.columns?.columnVisibilityModel !== false) {
|
|
64
|
-
columns.columnVisibilityModel = state.columns?.columnVisibilityModel;
|
|
65
|
-
}
|
|
66
|
-
if (config?.columns?.dimensions !== false) {
|
|
67
|
-
columns.dimensions = state.columns?.dimensions;
|
|
68
|
-
}
|
|
69
|
-
if (config?.columns?.orderedFields !== false) {
|
|
70
|
-
columns.orderedFields = state.columns?.orderedFields;
|
|
71
|
-
}
|
|
72
|
-
if (config?.pagination?.paginationModel.pageSize !== false) {
|
|
73
|
-
(pagination.paginationModel as Partial<GridPaginationModel>).pageSize = state.pagination?.paginationModel?.pageSize;
|
|
74
|
-
}
|
|
75
|
-
if (config?.preferencePanel === true) {
|
|
76
|
-
newState.preferencePanel = state.preferencePanel;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
// Sort model, current page and filter model are not persisted by default
|
|
80
|
-
if (config?.sorting?.sortModel === true) {
|
|
81
|
-
sorting.sortModel = state.sorting?.sortModel;
|
|
82
|
-
}
|
|
83
|
-
if (config?.pagination?.paginationModel.page === true) {
|
|
84
|
-
(pagination.paginationModel as Partial<GridPaginationModel>).page = state.pagination?.paginationModel?.page;
|
|
85
|
-
}
|
|
86
|
-
if (config?.filter?.filterModel === true) {
|
|
87
|
-
filter.filterModel = state.filter?.filterModel;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
return {
|
|
91
|
-
...newState,
|
|
92
|
-
columns,
|
|
93
|
-
filter,
|
|
94
|
-
pagination,
|
|
95
|
-
sorting,
|
|
96
|
-
};
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
let isLoaded = false;
|
|
100
|
-
export const isPersistedStateLoaded = () => isLoaded;
|
|
101
|
-
|
|
102
|
-
type GetInitialStateProps = Pick<AfGridProps, "id" | "layoutVersion" | "columns" | "initialState" | "persistenceModel">;
|
|
103
|
-
|
|
104
|
-
function getInitialState({ columns, persistenceModel, id, initialState, layoutVersion }: GetInitialStateProps) {
|
|
105
|
-
let key = `${af.article.id}.${id}`;
|
|
106
|
-
let persistedState = settingsCache[key];
|
|
107
|
-
|
|
108
|
-
let state: GridInitialState =
|
|
109
|
-
persistedState?.layoutVersion === layoutVersion
|
|
110
|
-
? removeNonPersistedState(persistedState.state, persistenceModel)
|
|
111
|
-
: {
|
|
112
|
-
density: "compact",
|
|
113
|
-
...initialState,
|
|
114
|
-
};
|
|
115
|
-
|
|
116
|
-
if (!state.columns) {
|
|
117
|
-
state.columns = {};
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
if (!state.columns.columnVisibilityModel) {
|
|
121
|
-
state.columns.columnVisibilityModel = getColumnVisibilityModel(columns);
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
if (!state.columns.orderedFields) {
|
|
125
|
-
state.columns.orderedFields = columns
|
|
126
|
-
.filter((c) => typeof c === "string" || !c.hide)
|
|
127
|
-
.map((c) => (typeof c === "string" ? c : c.field));
|
|
128
|
-
} else {
|
|
129
|
-
// Append any columns added since the layout was saved that aren't already present
|
|
130
|
-
let known = new Set(state.columns.orderedFields);
|
|
131
|
-
for (let c of columns) {
|
|
132
|
-
let field = typeof c === "string" ? c : c.field;
|
|
133
|
-
let hidden = typeof c !== "string" && c.hide;
|
|
134
|
-
if (!hidden && !known.has(field)) {
|
|
135
|
-
state.columns.orderedFields.push(field);
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
return state;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
// Load all article grid layouts even if the grid is not mounted yet.
|
|
144
|
-
// This ways the persisted state can be restored faster after the initial
|
|
145
|
-
// page load.
|
|
146
|
-
export const settingsLoaded = settingsStore
|
|
147
|
-
.keys()
|
|
148
|
-
.then((keys) =>
|
|
149
|
-
Promise.allSettled(
|
|
150
|
-
keys
|
|
151
|
-
.filter((key) => key.startsWith(af.article.id))
|
|
152
|
-
.map((key) =>
|
|
153
|
-
settingsStore.getItem<GridSettings>(key).then((settings) => {
|
|
154
|
-
if (settings && !settingsCache[key]) {
|
|
155
|
-
settingsCache[key] = settings;
|
|
156
|
-
}
|
|
157
|
-
}),
|
|
158
|
-
),
|
|
159
|
-
),
|
|
160
|
-
)
|
|
161
|
-
.then(() => {
|
|
162
|
-
isLoaded = true;
|
|
163
|
-
});
|
|
164
|
-
|
|
165
|
-
// Clean legacy state from local storage
|
|
166
|
-
setTimeout(() => {
|
|
167
|
-
for (let key of Object.keys(localStorage)) {
|
|
168
|
-
if (key.includes("AfGrid")) {
|
|
169
|
-
localStorage.removeItem(key);
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
}, 2000);
|
|
173
|
-
|
|
174
|
-
export type StatePersistenceConfig = {
|
|
175
|
-
aggregation?: boolean;
|
|
176
|
-
density?: boolean;
|
|
177
|
-
pinnedColumns?: boolean;
|
|
178
|
-
rowGrouping?: boolean;
|
|
179
|
-
columns?: {
|
|
180
|
-
columnVisibilityModel?: boolean;
|
|
181
|
-
dimensions?: boolean;
|
|
182
|
-
orderedFields?: boolean;
|
|
183
|
-
};
|
|
184
|
-
filter?: {
|
|
185
|
-
filterModel?: boolean;
|
|
186
|
-
};
|
|
187
|
-
pagination?: {
|
|
188
|
-
paginationModel: {
|
|
189
|
-
page?: boolean;
|
|
190
|
-
pageSize?: boolean;
|
|
191
|
-
};
|
|
192
|
-
};
|
|
193
|
-
preferencePanel?: boolean;
|
|
194
|
-
sorting?: {
|
|
195
|
-
sortModel: boolean;
|
|
196
|
-
};
|
|
197
|
-
};
|
|
198
|
-
|
|
199
|
-
/**
|
|
200
|
-
* On mount, load the persisted state from Indexed DB, and if the persisted
|
|
201
|
-
* layout version matches, restore the layout. Then listen to changes to the
|
|
202
|
-
* state, and store the latest version in Indexed DB.
|
|
203
|
-
*/
|
|
204
|
-
export function useAfPersistedState(
|
|
205
|
-
props: { apiRef: React.RefObject<GridApi | null> } & Pick<
|
|
206
|
-
AfGridProps,
|
|
207
|
-
"id" | "layoutVersion" | "columns" | "initialState" | "persistenceModel"
|
|
208
|
-
>,
|
|
209
|
-
): GridInitialState {
|
|
210
|
-
let { apiRef, columns, id, initialState, layoutVersion = 0, persistenceModel } = props;
|
|
211
|
-
let [initialState_] = useState(() => getInitialState({ columns, persistenceModel, id, initialState, layoutVersion }));
|
|
212
|
-
|
|
213
|
-
useEffect(() => {
|
|
214
|
-
let key = `${af.article.id}.${id}`;
|
|
215
|
-
let persistTimeout: any;
|
|
216
|
-
|
|
217
|
-
return apiRef.current?.subscribeEvent("stateChange", () => {
|
|
218
|
-
// Debounce the save, as state changes can be very frequent.
|
|
219
|
-
clearTimeout(persistTimeout);
|
|
220
|
-
persistTimeout = setTimeout(() => {
|
|
221
|
-
// Use optional chaining, as the component may unmount before the timeout
|
|
222
|
-
// fires.
|
|
223
|
-
let state = apiRef.current?.exportState();
|
|
224
|
-
persistState(key, layoutVersion, state, persistenceModel);
|
|
225
|
-
}, 300);
|
|
226
|
-
});
|
|
227
|
-
}, [apiRef, id, layoutVersion, persistenceModel]);
|
|
228
|
-
|
|
229
|
-
return initialState_;
|
|
230
|
-
}
|
|
1
|
+
import type { GridApi, GridInitialState, GridPaginationModel } from "@mui/x-data-grid-premium";
|
|
2
|
+
|
|
3
|
+
import localforage from "localforage";
|
|
4
|
+
import { useEffect, useState } from "react";
|
|
5
|
+
|
|
6
|
+
import type { AfGridProps } from "./index.js";
|
|
7
|
+
import { getColumnVisibilityModel } from "./useAfColumns.js";
|
|
8
|
+
|
|
9
|
+
const settingsStore = localforage.createInstance({
|
|
10
|
+
name: `AfGridLayouts.${globalThis.af?.userSession?.login ?? "anonymous"}`,
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
type GridSettings = {
|
|
14
|
+
layoutVersion: string | number;
|
|
15
|
+
state: GridInitialState;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const settingsCache: Record<string, GridSettings> = {};
|
|
19
|
+
|
|
20
|
+
async function persistState(
|
|
21
|
+
key: string,
|
|
22
|
+
layoutVersion: string | number,
|
|
23
|
+
state: GridInitialState | null | undefined,
|
|
24
|
+
config?: StatePersistenceConfig,
|
|
25
|
+
) {
|
|
26
|
+
if (state) {
|
|
27
|
+
state = structuredClone(state);
|
|
28
|
+
|
|
29
|
+
let settings: GridSettings = {
|
|
30
|
+
layoutVersion,
|
|
31
|
+
state: removeNonPersistedState(state, config),
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
if (state.pagination?.paginationModel?.page !== undefined) {
|
|
35
|
+
delete state.pagination.paginationModel.page;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
settingsCache[key] = settings;
|
|
39
|
+
await settingsStore.setItem<GridSettings>(key, settings);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function removeNonPersistedState(state: GridInitialState, config?: StatePersistenceConfig): GridInitialState {
|
|
44
|
+
const newState: GridInitialState = {};
|
|
45
|
+
let columns: GridInitialState["columns"] = {};
|
|
46
|
+
let filter: GridInitialState["filter"] = {};
|
|
47
|
+
let pagination: GridInitialState["pagination"] = { paginationModel: {} };
|
|
48
|
+
let sorting: GridInitialState["sorting"] = {};
|
|
49
|
+
|
|
50
|
+
// Persist the relevant state unless explicitly disabled in the config
|
|
51
|
+
if (config?.density !== false) {
|
|
52
|
+
newState.density = state.density;
|
|
53
|
+
}
|
|
54
|
+
if (config?.pinnedColumns !== false) {
|
|
55
|
+
newState.pinnedColumns = state.pinnedColumns;
|
|
56
|
+
}
|
|
57
|
+
if (config?.rowGrouping !== false) {
|
|
58
|
+
newState.rowGrouping = state.rowGrouping;
|
|
59
|
+
}
|
|
60
|
+
if (config?.aggregation !== false) {
|
|
61
|
+
newState.aggregation = state.aggregation;
|
|
62
|
+
}
|
|
63
|
+
if (config?.columns?.columnVisibilityModel !== false) {
|
|
64
|
+
columns.columnVisibilityModel = state.columns?.columnVisibilityModel;
|
|
65
|
+
}
|
|
66
|
+
if (config?.columns?.dimensions !== false) {
|
|
67
|
+
columns.dimensions = state.columns?.dimensions;
|
|
68
|
+
}
|
|
69
|
+
if (config?.columns?.orderedFields !== false) {
|
|
70
|
+
columns.orderedFields = state.columns?.orderedFields;
|
|
71
|
+
}
|
|
72
|
+
if (config?.pagination?.paginationModel.pageSize !== false) {
|
|
73
|
+
(pagination.paginationModel as Partial<GridPaginationModel>).pageSize = state.pagination?.paginationModel?.pageSize;
|
|
74
|
+
}
|
|
75
|
+
if (config?.preferencePanel === true) {
|
|
76
|
+
newState.preferencePanel = state.preferencePanel;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Sort model, current page and filter model are not persisted by default
|
|
80
|
+
if (config?.sorting?.sortModel === true) {
|
|
81
|
+
sorting.sortModel = state.sorting?.sortModel;
|
|
82
|
+
}
|
|
83
|
+
if (config?.pagination?.paginationModel.page === true) {
|
|
84
|
+
(pagination.paginationModel as Partial<GridPaginationModel>).page = state.pagination?.paginationModel?.page;
|
|
85
|
+
}
|
|
86
|
+
if (config?.filter?.filterModel === true) {
|
|
87
|
+
filter.filterModel = state.filter?.filterModel;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return {
|
|
91
|
+
...newState,
|
|
92
|
+
columns,
|
|
93
|
+
filter,
|
|
94
|
+
pagination,
|
|
95
|
+
sorting,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
let isLoaded = false;
|
|
100
|
+
export const isPersistedStateLoaded = () => isLoaded;
|
|
101
|
+
|
|
102
|
+
type GetInitialStateProps = Pick<AfGridProps, "id" | "layoutVersion" | "columns" | "initialState" | "persistenceModel">;
|
|
103
|
+
|
|
104
|
+
function getInitialState({ columns, persistenceModel, id, initialState, layoutVersion }: GetInitialStateProps) {
|
|
105
|
+
let key = `${af.article.id}.${id}`;
|
|
106
|
+
let persistedState = settingsCache[key];
|
|
107
|
+
|
|
108
|
+
let state: GridInitialState =
|
|
109
|
+
persistedState?.layoutVersion === layoutVersion
|
|
110
|
+
? removeNonPersistedState(persistedState.state, persistenceModel)
|
|
111
|
+
: {
|
|
112
|
+
density: "compact",
|
|
113
|
+
...initialState,
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
if (!state.columns) {
|
|
117
|
+
state.columns = {};
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (!state.columns.columnVisibilityModel) {
|
|
121
|
+
state.columns.columnVisibilityModel = getColumnVisibilityModel(columns);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (!state.columns.orderedFields) {
|
|
125
|
+
state.columns.orderedFields = columns
|
|
126
|
+
.filter((c) => typeof c === "string" || !c.hide)
|
|
127
|
+
.map((c) => (typeof c === "string" ? c : c.field));
|
|
128
|
+
} else {
|
|
129
|
+
// Append any columns added since the layout was saved that aren't already present
|
|
130
|
+
let known = new Set(state.columns.orderedFields);
|
|
131
|
+
for (let c of columns) {
|
|
132
|
+
let field = typeof c === "string" ? c : c.field;
|
|
133
|
+
let hidden = typeof c !== "string" && c.hide;
|
|
134
|
+
if (!hidden && !known.has(field)) {
|
|
135
|
+
state.columns.orderedFields.push(field);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return state;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Load all article grid layouts even if the grid is not mounted yet.
|
|
144
|
+
// This ways the persisted state can be restored faster after the initial
|
|
145
|
+
// page load.
|
|
146
|
+
export const settingsLoaded = settingsStore
|
|
147
|
+
.keys()
|
|
148
|
+
.then((keys) =>
|
|
149
|
+
Promise.allSettled(
|
|
150
|
+
keys
|
|
151
|
+
.filter((key) => key.startsWith(af.article.id))
|
|
152
|
+
.map((key) =>
|
|
153
|
+
settingsStore.getItem<GridSettings>(key).then((settings) => {
|
|
154
|
+
if (settings && !settingsCache[key]) {
|
|
155
|
+
settingsCache[key] = settings;
|
|
156
|
+
}
|
|
157
|
+
}),
|
|
158
|
+
),
|
|
159
|
+
),
|
|
160
|
+
)
|
|
161
|
+
.then(() => {
|
|
162
|
+
isLoaded = true;
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
// Clean legacy state from local storage
|
|
166
|
+
setTimeout(() => {
|
|
167
|
+
for (let key of Object.keys(localStorage)) {
|
|
168
|
+
if (key.includes("AfGrid")) {
|
|
169
|
+
localStorage.removeItem(key);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}, 2000);
|
|
173
|
+
|
|
174
|
+
export type StatePersistenceConfig = {
|
|
175
|
+
aggregation?: boolean;
|
|
176
|
+
density?: boolean;
|
|
177
|
+
pinnedColumns?: boolean;
|
|
178
|
+
rowGrouping?: boolean;
|
|
179
|
+
columns?: {
|
|
180
|
+
columnVisibilityModel?: boolean;
|
|
181
|
+
dimensions?: boolean;
|
|
182
|
+
orderedFields?: boolean;
|
|
183
|
+
};
|
|
184
|
+
filter?: {
|
|
185
|
+
filterModel?: boolean;
|
|
186
|
+
};
|
|
187
|
+
pagination?: {
|
|
188
|
+
paginationModel: {
|
|
189
|
+
page?: boolean;
|
|
190
|
+
pageSize?: boolean;
|
|
191
|
+
};
|
|
192
|
+
};
|
|
193
|
+
preferencePanel?: boolean;
|
|
194
|
+
sorting?: {
|
|
195
|
+
sortModel: boolean;
|
|
196
|
+
};
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* On mount, load the persisted state from Indexed DB, and if the persisted
|
|
201
|
+
* layout version matches, restore the layout. Then listen to changes to the
|
|
202
|
+
* state, and store the latest version in Indexed DB.
|
|
203
|
+
*/
|
|
204
|
+
export function useAfPersistedState(
|
|
205
|
+
props: { apiRef: React.RefObject<GridApi | null> } & Pick<
|
|
206
|
+
AfGridProps,
|
|
207
|
+
"id" | "layoutVersion" | "columns" | "initialState" | "persistenceModel"
|
|
208
|
+
>,
|
|
209
|
+
): GridInitialState {
|
|
210
|
+
let { apiRef, columns, id, initialState, layoutVersion = 0, persistenceModel } = props;
|
|
211
|
+
let [initialState_] = useState(() => getInitialState({ columns, persistenceModel, id, initialState, layoutVersion }));
|
|
212
|
+
|
|
213
|
+
useEffect(() => {
|
|
214
|
+
let key = `${af.article.id}.${id}`;
|
|
215
|
+
let persistTimeout: any;
|
|
216
|
+
|
|
217
|
+
return apiRef.current?.subscribeEvent("stateChange", () => {
|
|
218
|
+
// Debounce the save, as state changes can be very frequent.
|
|
219
|
+
clearTimeout(persistTimeout);
|
|
220
|
+
persistTimeout = setTimeout(() => {
|
|
221
|
+
// Use optional chaining, as the component may unmount before the timeout
|
|
222
|
+
// fires.
|
|
223
|
+
let state = apiRef.current?.exportState();
|
|
224
|
+
persistState(key, layoutVersion, state, persistenceModel);
|
|
225
|
+
}, 300);
|
|
226
|
+
});
|
|
227
|
+
}, [apiRef, id, layoutVersion, persistenceModel]);
|
|
228
|
+
|
|
229
|
+
return initialState_;
|
|
230
|
+
}
|