ag-grid-url-sync 0.5.1 → 0.5.2
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 +244 -26
- package/dist/core/ag-grid-url-sync.d.ts +6 -0
- package/dist/core/ag-grid-url-sync.js.map +1 -1
- package/dist/core/codecs.d.ts +28 -0
- package/dist/core/codecs.js +39 -0
- package/dist/core/codecs.js.map +1 -0
- package/dist/core/data-issue-codes.d.ts +3 -0
- package/dist/core/data-issue-codes.js +10 -0
- package/dist/core/data-issue-codes.js.map +1 -0
- package/dist/core/grid-integration.d.ts +11 -1
- package/dist/core/grid-integration.js +20 -11
- package/dist/core/grid-integration.js.map +1 -1
- package/dist/core/grid-url-sync.d.ts +62 -0
- package/dist/core/grid-url-sync.js +738 -0
- package/dist/core/grid-url-sync.js.map +1 -0
- package/dist/core/index.d.ts +2 -0
- package/dist/core/index.js +2 -0
- package/dist/core/index.js.map +1 -1
- package/dist/core/issues.d.ts +2 -2
- package/dist/core/issues.js +2 -9
- package/dist/core/issues.js.map +1 -1
- package/dist/core/own-write.d.ts +138 -0
- package/dist/core/own-write.js +133 -0
- package/dist/core/own-write.js.map +1 -0
- package/dist/core/types.d.ts +151 -4
- package/dist/core/types.js +4 -0
- package/dist/core/types.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/react/index.d.ts +3 -2
- package/dist/react/index.js +1 -0
- package/dist/react/index.js.map +1 -1
- package/dist/react/sync-holder.d.ts +22 -0
- package/dist/react/sync-holder.js +68 -0
- package/dist/react/sync-holder.js.map +1 -0
- package/dist/react/sync-options.d.ts +17 -0
- package/dist/react/sync-options.js +22 -0
- package/dist/react/sync-options.js.map +1 -0
- package/dist/react/types.d.ts +71 -3
- package/dist/react/use-ag-grid-url-sync.d.ts +3 -0
- package/dist/react/use-ag-grid-url-sync.js +310 -526
- package/dist/react/use-ag-grid-url-sync.js.map +1 -1
- package/dist/react/use-grid-url-sync.d.ts +14 -0
- package/dist/react/use-grid-url-sync.js +206 -0
- package/dist/react/use-grid-url-sync.js.map +1 -0
- package/package.json +16 -3
|
@@ -1,170 +1,118 @@
|
|
|
1
1
|
import { useState, useEffect, useRef, useCallback, useMemo } from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { useSyncExternalStore } from 'use-sync-external-store/shim';
|
|
3
|
+
import { createSync, handOver, IDLE_SNAPSHOT } from '../core/grid-url-sync.js';
|
|
3
4
|
import { parseUrlFilters as parseFilters } from '../core/url-parser.js';
|
|
4
|
-
import {
|
|
5
|
+
import { generateUrl } from '../core/url-generator.js';
|
|
6
|
+
import { getFilterModel } from '../core/grid-integration.js';
|
|
7
|
+
import { serializeGrouped } from '../core/serialization/grouped.js';
|
|
5
8
|
import { createViewStore } from '../core/view-storage.js';
|
|
6
9
|
import { DEFAULT_CONFIG, resolveOptions } from '../core/options.js';
|
|
7
10
|
import { toError } from '../core/to-error.js';
|
|
8
|
-
import { isDataIssue, shouldThrow
|
|
11
|
+
import { isDataIssue, shouldThrow } from '../core/issues.js';
|
|
9
12
|
import { URLSyncError } from '../core/types.js';
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
}
|
|
13
|
-
function sameFilterModel(a, b) {
|
|
14
|
-
const keys = Object.keys(a);
|
|
15
|
-
return (keys.length === Object.keys(b).length &&
|
|
16
|
-
keys.every(key => JSON.stringify(a[key]) === JSON.stringify(b[key])));
|
|
17
|
-
}
|
|
13
|
+
import { createSyncHolder } from './sync-holder.js';
|
|
14
|
+
import { gridUrlSyncOptions } from './sync-options.js';
|
|
18
15
|
const NO_ISSUES = [];
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
16
|
+
const getServerSnapshot = () => IDLE_SNAPSHOT;
|
|
17
|
+
const WRITE_CODES = new Set([
|
|
18
|
+
'STORAGE_WRITE_FAILED',
|
|
19
|
+
'STORAGE_FULL',
|
|
20
|
+
'STORAGE_UNAVAILABLE',
|
|
21
|
+
'HISTORY_WRITE_FAILED'
|
|
22
|
+
]);
|
|
23
|
+
const LOAD_STOP_CODES = new Set([
|
|
24
|
+
'VIEW_NOT_FOUND',
|
|
25
|
+
'GRID_ERROR'
|
|
26
|
+
]);
|
|
27
|
+
function newSession(grid, storageKey) {
|
|
28
|
+
return {
|
|
29
|
+
grid,
|
|
30
|
+
storageKey,
|
|
31
|
+
autoApplied: false,
|
|
32
|
+
urlApplied: false,
|
|
33
|
+
activeViewId: null,
|
|
34
|
+
applyIssues: NO_ISSUES,
|
|
35
|
+
handover: null
|
|
36
|
+
};
|
|
24
37
|
}
|
|
25
|
-
function
|
|
26
|
-
const
|
|
27
|
-
return
|
|
38
|
+
function dataIssues(issues) {
|
|
39
|
+
const data = issues.filter(isDataIssue);
|
|
40
|
+
return data.length ? data : NO_ISSUES;
|
|
28
41
|
}
|
|
29
|
-
function
|
|
42
|
+
function pageGivesFilters(options) {
|
|
30
43
|
try {
|
|
31
|
-
|
|
44
|
+
const filters = parseFilters(pageUrl(), {
|
|
45
|
+
...options,
|
|
46
|
+
onParseError: () => { }
|
|
47
|
+
});
|
|
48
|
+
return Object.keys(filters).length > 0;
|
|
32
49
|
}
|
|
33
|
-
catch
|
|
34
|
-
|
|
50
|
+
catch {
|
|
51
|
+
return false;
|
|
35
52
|
}
|
|
36
53
|
}
|
|
37
|
-
function
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
54
|
+
function urlWithFilters(api, base, options) {
|
|
55
|
+
return generateUrl(base, getFilterModel(api, options), options);
|
|
56
|
+
}
|
|
57
|
+
function filtersAsFormat(api, format, options) {
|
|
58
|
+
const filters = getFilterModel(api, options);
|
|
59
|
+
return serializeGrouped(filters, {
|
|
60
|
+
...options,
|
|
61
|
+
format,
|
|
62
|
+
serialization: 'grouped'
|
|
63
|
+
}).value;
|
|
64
|
+
}
|
|
65
|
+
function hasWindow() {
|
|
66
|
+
return typeof window !== 'undefined';
|
|
41
67
|
}
|
|
42
|
-
function
|
|
43
|
-
return
|
|
44
|
-
(before !== null &&
|
|
45
|
-
Object.hasOwn(before, colId) &&
|
|
46
|
-
JSON.stringify(live[colId]) === JSON.stringify(before[colId])));
|
|
68
|
+
function pageUrl() {
|
|
69
|
+
return hasWindow() ? window.location.href : '';
|
|
47
70
|
}
|
|
48
71
|
export function useAGGridUrlSync(gridApi, options = {}) {
|
|
49
|
-
const { autoApplyOnMount = false, enabledWhenReady = true, onError, storageKey, throwOnError, ...coreOptions } = options;
|
|
50
|
-
const
|
|
51
|
-
const
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
|
|
72
|
+
const { autoApplyOnMount = false, enabledWhenReady = true, onError, storageKey, history = 'none', popstate, throwOnError, ...coreOptions } = options;
|
|
73
|
+
const resolved = resolveOptions(coreOptions);
|
|
74
|
+
const { paramPrefix, maxValueLength, serialization, groupedParam, format } = resolved;
|
|
75
|
+
const urlOptions = useMemo(() => ({
|
|
76
|
+
paramPrefix,
|
|
77
|
+
maxValueLength,
|
|
78
|
+
serialization,
|
|
79
|
+
groupedParam,
|
|
80
|
+
format
|
|
81
|
+
}), [paramPrefix, maxValueLength, serialization, groupedParam, format]);
|
|
82
|
+
const [holder] = useState(createSyncHolder);
|
|
83
|
+
const snapshot = useSyncExternalStore(holder.subscribe, holder.getSnapshot, getServerSnapshot);
|
|
84
|
+
const isReady = snapshot.status === 'active';
|
|
85
|
+
const currentUrl = isReady ? snapshot.url : '';
|
|
86
|
+
const hasFilters = isReady && snapshot.hasFilters;
|
|
55
87
|
const viewStore = useMemo(() => (storageKey ? createViewStore(storageKey) : null), [storageKey]);
|
|
56
|
-
const [views, setViews] = useState([]);
|
|
57
|
-
const [activeViewId, setActiveViewId] = useState(null);
|
|
58
|
-
const activeViewIdRef = useRef(null);
|
|
59
|
-
const appliedModelRef = useRef(null);
|
|
60
|
-
const pendingWriteRef = useRef(null);
|
|
61
|
-
const writingRef = useRef(false);
|
|
62
|
-
const commitActiveViewId = useCallback((id) => {
|
|
63
|
-
activeViewIdRef.current = id;
|
|
64
|
-
setActiveViewId(id);
|
|
65
|
-
if (id === null) {
|
|
66
|
-
appliedModelRef.current = null;
|
|
67
|
-
pendingWriteRef.current = null;
|
|
68
|
-
}
|
|
69
|
-
}, []);
|
|
70
|
-
const applyModelToGrid = useCallback((api, model) => {
|
|
71
|
-
let before = null;
|
|
72
|
-
try {
|
|
73
|
-
before = api.getFilterModel() ?? {};
|
|
74
|
-
}
|
|
75
|
-
catch {
|
|
76
|
-
}
|
|
77
|
-
pendingWriteRef.current = {
|
|
78
|
-
before,
|
|
79
|
-
expected: applicableModel(api, model)
|
|
80
|
-
};
|
|
81
|
-
writingRef.current = true;
|
|
82
|
-
try {
|
|
83
|
-
callGrid(() => api.setFilterModel(model));
|
|
84
|
-
}
|
|
85
|
-
catch (error) {
|
|
86
|
-
pendingWriteRef.current = null;
|
|
87
|
-
throw error;
|
|
88
|
-
}
|
|
89
|
-
finally {
|
|
90
|
-
writingRef.current = false;
|
|
91
|
-
}
|
|
92
|
-
if (!pendingWriteRef.current)
|
|
93
|
-
return;
|
|
94
|
-
let live = null;
|
|
95
|
-
try {
|
|
96
|
-
live = api.getFilterModel() ?? {};
|
|
97
|
-
}
|
|
98
|
-
catch {
|
|
99
|
-
}
|
|
100
|
-
if (live && (!before || !sameFilterModel(live, before))) {
|
|
101
|
-
pendingWriteRef.current = null;
|
|
102
|
-
appliedModelRef.current = live;
|
|
103
|
-
}
|
|
104
|
-
}, []);
|
|
105
|
-
const writeOverPendingView = useCallback((write) => {
|
|
106
|
-
const pending = pendingWriteRef.current;
|
|
107
|
-
if (!pending || !gridApi) {
|
|
108
|
-
write();
|
|
109
|
-
return;
|
|
110
|
-
}
|
|
111
|
-
const applied = appliedModelRef.current;
|
|
112
|
-
const activeId = activeViewIdRef.current;
|
|
113
|
-
let before = null;
|
|
114
|
-
try {
|
|
115
|
-
before = gridApi.getFilterModel() ?? {};
|
|
116
|
-
}
|
|
117
|
-
catch {
|
|
118
|
-
}
|
|
119
|
-
pendingWriteRef.current = null;
|
|
120
|
-
appliedModelRef.current = pending.expected;
|
|
121
|
-
const reinstate = () => {
|
|
122
|
-
if (pendingWriteRef.current || activeViewIdRef.current !== activeId) {
|
|
123
|
-
return;
|
|
124
|
-
}
|
|
125
|
-
pendingWriteRef.current = pending;
|
|
126
|
-
appliedModelRef.current = applied;
|
|
127
|
-
};
|
|
128
|
-
try {
|
|
129
|
-
write();
|
|
130
|
-
}
|
|
131
|
-
catch (error) {
|
|
132
|
-
reinstate();
|
|
133
|
-
throw error;
|
|
134
|
-
}
|
|
135
|
-
if (!before)
|
|
136
|
-
return;
|
|
137
|
-
try {
|
|
138
|
-
const live = gridApi.getFilterModel() ?? {};
|
|
139
|
-
if (sameFilterModel(live, before))
|
|
140
|
-
reinstate();
|
|
141
|
-
}
|
|
142
|
-
catch {
|
|
143
|
-
}
|
|
144
|
-
}, [gridApi]);
|
|
145
|
-
const syncViewsFromStore = useCallback(() => {
|
|
146
|
-
const nextViews = viewStore ? viewStore.listViews() : [];
|
|
147
|
-
setViews(prev => (sameViews(prev, nextViews) ? prev : nextViews));
|
|
148
|
-
}, [viewStore]);
|
|
149
|
-
const urlSyncRef = useRef(null);
|
|
150
|
-
const autoAppliedRef = useRef(false);
|
|
151
|
-
const urlAppliedRef = useRef(false);
|
|
152
|
-
const lastGridApiRef = useRef(null);
|
|
153
|
-
useEffect(() => {
|
|
154
|
-
syncViewsFromStore();
|
|
155
|
-
commitActiveViewId(null);
|
|
156
|
-
if (viewStore) {
|
|
157
|
-
autoAppliedRef.current = false;
|
|
158
|
-
}
|
|
159
|
-
}, [syncViewsFromStore, viewStore, commitActiveViewId]);
|
|
160
88
|
const callbacksRef = useRef({
|
|
161
89
|
onError,
|
|
162
|
-
onParseError: coreOptions.onParseError
|
|
90
|
+
onParseError: coreOptions.onParseError,
|
|
91
|
+
history
|
|
163
92
|
});
|
|
164
93
|
useEffect(() => {
|
|
165
|
-
callbacksRef.current = {
|
|
94
|
+
callbacksRef.current = {
|
|
95
|
+
onError,
|
|
96
|
+
onParseError: coreOptions.onParseError,
|
|
97
|
+
history
|
|
98
|
+
};
|
|
166
99
|
});
|
|
100
|
+
const historyKind = history instanceof Function ? 'function' : history;
|
|
101
|
+
const followsPopstate = popstate ?? historyKind === 'push';
|
|
102
|
+
const addressBar = useMemo(() => ({
|
|
103
|
+
history: historyKind === 'function'
|
|
104
|
+
? url => {
|
|
105
|
+
const write = callbacksRef.current.history;
|
|
106
|
+
if (write instanceof Function)
|
|
107
|
+
write(url);
|
|
108
|
+
}
|
|
109
|
+
: historyKind,
|
|
110
|
+
popstate: followsPopstate
|
|
111
|
+
}), [historyKind, followsPopstate]);
|
|
167
112
|
const contextRef = useRef('initialization');
|
|
113
|
+
const sessionRef = useRef(newSession(null, undefined));
|
|
114
|
+
const liveSyncRef = useRef(null);
|
|
115
|
+
const loadStoppedRef = useRef(false);
|
|
168
116
|
const reportFailure = useCallback((cause, context) => {
|
|
169
117
|
const error = toError(cause);
|
|
170
118
|
const callbacks = callbacksRef.current;
|
|
@@ -178,465 +126,299 @@ export function useAGGridUrlSync(gridApi, options = {}) {
|
|
|
178
126
|
const reportNotReady = useCallback((operation, context) => {
|
|
179
127
|
reportFailure(new URLSyncError(`${operation} called while the hook is not ready.`, undefined, { code: 'NOT_READY' }), context);
|
|
180
128
|
}, [reportFailure]);
|
|
129
|
+
const dropSync = useCallback((current) => {
|
|
130
|
+
sessionRef.current.activeViewId = current.sync.getSnapshot().activeViewId;
|
|
131
|
+
sessionRef.current.handover = handOver(current.sync);
|
|
132
|
+
current.stopPopstate();
|
|
133
|
+
current.sync.destroy();
|
|
134
|
+
holder.set(null);
|
|
135
|
+
liveSyncRef.current = null;
|
|
136
|
+
}, [holder]);
|
|
137
|
+
const activeSync = useCallback(() => {
|
|
138
|
+
const sync = holder.get();
|
|
139
|
+
return sync?.getSnapshot().status === 'active' ? sync : null;
|
|
140
|
+
}, [holder]);
|
|
141
|
+
const keepApplyIssues = useCallback((issues) => {
|
|
142
|
+
sessionRef.current.applyIssues = dataIssues(issues);
|
|
143
|
+
holder.setCarriedIssues(NO_ISSUES);
|
|
144
|
+
}, [holder]);
|
|
145
|
+
const loadAndKeep = useCallback((sync, id) => {
|
|
146
|
+
loadStoppedRef.current = false;
|
|
147
|
+
sync.loadView(id);
|
|
148
|
+
if (!loadStoppedRef.current)
|
|
149
|
+
keepApplyIssues(NO_ISSUES);
|
|
150
|
+
}, [keepApplyIssues]);
|
|
181
151
|
useEffect(() => {
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
reportFailure(error, 'initialization');
|
|
202
|
-
setIsReady(false);
|
|
203
|
-
}
|
|
204
|
-
}
|
|
152
|
+
const live = gridApi && enabledWhenReady ? gridApi : null;
|
|
153
|
+
const current = liveSyncRef.current;
|
|
154
|
+
const newGrid = live !== sessionRef.current.grid;
|
|
155
|
+
if (newGrid) {
|
|
156
|
+
if (current)
|
|
157
|
+
dropSync(current);
|
|
158
|
+
sessionRef.current = newSession(live, storageKey);
|
|
159
|
+
}
|
|
160
|
+
const session = sessionRef.current;
|
|
161
|
+
const sameKey = session.storageKey === storageKey;
|
|
162
|
+
const autoApply = autoApplyOnMount &&
|
|
163
|
+
(!session.autoApplied || (!sameKey && viewStore !== null));
|
|
164
|
+
if (current &&
|
|
165
|
+
!newGrid &&
|
|
166
|
+
current.urlOptions === urlOptions &&
|
|
167
|
+
current.addressBar === addressBar &&
|
|
168
|
+
current.viewStore === viewStore &&
|
|
169
|
+
!autoApply) {
|
|
170
|
+
return;
|
|
205
171
|
}
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
urlAppliedRef.current = false;
|
|
212
|
-
commitActiveViewId(null);
|
|
213
|
-
setApplyIssues(NO_ISSUES);
|
|
214
|
-
}
|
|
215
|
-
setIsReady(false);
|
|
216
|
-
lastGridApiRef.current = null;
|
|
172
|
+
if (current && !newGrid)
|
|
173
|
+
dropSync(current);
|
|
174
|
+
if (!live) {
|
|
175
|
+
holder.setIdleViews(viewStore?.listViews() ?? []);
|
|
176
|
+
return;
|
|
217
177
|
}
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
if (
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
const
|
|
231
|
-
const
|
|
232
|
-
const
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
if (key.startsWith(prefix) || key === groupedParam) {
|
|
236
|
-
hasFilterParam = true;
|
|
237
|
-
break;
|
|
178
|
+
const kept = sameKey ? session.activeViewId : null;
|
|
179
|
+
session.storageKey = storageKey;
|
|
180
|
+
session.activeViewId = null;
|
|
181
|
+
if (!sameKey && viewStore)
|
|
182
|
+
session.autoApplied = false;
|
|
183
|
+
const landsOnCreate = autoApply && viewStore !== null && !session.urlApplied;
|
|
184
|
+
if (autoApply)
|
|
185
|
+
session.autoApplied = true;
|
|
186
|
+
if (landsOnCreate)
|
|
187
|
+
session.urlApplied = true;
|
|
188
|
+
let urlIssues = [];
|
|
189
|
+
let urlPhase = 'create';
|
|
190
|
+
const keptView = autoApply ? null : kept;
|
|
191
|
+
const createContext = keptView === null ? 'auto-apply-filters' : 'initialization';
|
|
192
|
+
const onIssue = ({ issue, source }) => {
|
|
193
|
+
if (source === 'api' && LOAD_STOP_CODES.has(issue.code)) {
|
|
194
|
+
loadStoppedRef.current = true;
|
|
238
195
|
}
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
196
|
+
if (source === 'url' && urlPhase !== 'live' && isDataIssue(issue)) {
|
|
197
|
+
urlIssues.push(issue);
|
|
198
|
+
}
|
|
199
|
+
const urlContext = urlPhase === 'popstate'
|
|
200
|
+
? 'popstate'
|
|
201
|
+
: issue.code === 'HISTORY_WRITE_FAILED'
|
|
202
|
+
? 'filter-change'
|
|
203
|
+
: createContext;
|
|
204
|
+
reportFailure(issue, source === 'grid'
|
|
205
|
+
? WRITE_CODES.has(issue.code)
|
|
206
|
+
? 'filter-change'
|
|
207
|
+
: 'update-state'
|
|
208
|
+
: source === 'url'
|
|
209
|
+
? urlContext
|
|
210
|
+
: contextRef.current);
|
|
211
|
+
};
|
|
212
|
+
const beforePopstate = () => {
|
|
213
|
+
urlPhase = 'popstate';
|
|
214
|
+
urlIssues = [];
|
|
245
215
|
};
|
|
216
|
+
const afterPopstate = () => {
|
|
217
|
+
urlPhase = 'live';
|
|
218
|
+
keepApplyIssues(urlIssues);
|
|
219
|
+
};
|
|
220
|
+
const listens = addressBar.popstate && hasWindow();
|
|
221
|
+
if (listens)
|
|
222
|
+
window.addEventListener('popstate', beforePopstate);
|
|
223
|
+
const stopPopstate = () => {
|
|
224
|
+
if (!listens)
|
|
225
|
+
return;
|
|
226
|
+
window.removeEventListener('popstate', beforePopstate);
|
|
227
|
+
window.removeEventListener('popstate', afterPopstate);
|
|
228
|
+
};
|
|
229
|
+
let sync;
|
|
230
|
+
contextRef.current = 'initialization';
|
|
231
|
+
holder.setCarriedIssues(session.applyIssues);
|
|
232
|
+
const { handover } = session;
|
|
233
|
+
session.handover = null;
|
|
246
234
|
try {
|
|
247
|
-
|
|
235
|
+
sync = createSync(gridUrlSyncOptions(live, urlOptions, onIssue, viewStore ?? undefined, landsOnCreate, addressBar), { keptViewId: keptView, handover });
|
|
236
|
+
}
|
|
237
|
+
catch (error) {
|
|
238
|
+
stopPopstate();
|
|
239
|
+
reportFailure(error, 'initialization');
|
|
240
|
+
return;
|
|
248
241
|
}
|
|
249
|
-
|
|
250
|
-
|
|
242
|
+
if (sync.getSnapshot().status !== 'active') {
|
|
243
|
+
stopPopstate();
|
|
251
244
|
}
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
try {
|
|
271
|
-
viewStore?.persistActiveViewId(null);
|
|
272
|
-
}
|
|
273
|
-
catch (error) {
|
|
274
|
-
reportFailure(error, 'auto-apply-filters');
|
|
275
|
-
}
|
|
276
|
-
return;
|
|
277
|
-
}
|
|
278
|
-
const storedId = viewStore.getActiveViewId();
|
|
279
|
-
const stored = storedId
|
|
280
|
-
? viewStore.listViews().find(view => view.id === storedId)
|
|
281
|
-
: undefined;
|
|
282
|
-
if (stored) {
|
|
283
|
-
try {
|
|
284
|
-
applyModelToGrid(gridApi, stored.filterModel);
|
|
285
|
-
commitActiveViewId(stored.id);
|
|
286
|
-
setApplyIssues(NO_ISSUES);
|
|
287
|
-
}
|
|
288
|
-
catch (error) {
|
|
289
|
-
reportFailure(error, 'auto-apply-filters');
|
|
290
|
-
}
|
|
291
|
-
}
|
|
245
|
+
else if (listens) {
|
|
246
|
+
window.addEventListener('popstate', afterPopstate);
|
|
247
|
+
}
|
|
248
|
+
holder.set(sync);
|
|
249
|
+
liveSyncRef.current = {
|
|
250
|
+
sync,
|
|
251
|
+
urlOptions,
|
|
252
|
+
addressBar,
|
|
253
|
+
viewStore,
|
|
254
|
+
stopPopstate
|
|
255
|
+
};
|
|
256
|
+
if (sync.getSnapshot().status === 'active') {
|
|
257
|
+
if (autoApply)
|
|
258
|
+
contextRef.current = 'auto-apply-filters';
|
|
259
|
+
if (landsOnCreate &&
|
|
260
|
+
(sync.getSnapshot().activeViewId !== null ||
|
|
261
|
+
pageGivesFilters(urlOptions))) {
|
|
262
|
+
keepApplyIssues(urlIssues);
|
|
292
263
|
}
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
264
|
+
else if (autoApply && !viewStore) {
|
|
265
|
+
keepApplyIssues(sync.applyUrl().issues);
|
|
266
|
+
}
|
|
267
|
+
else if (autoApply && viewStore && !landsOnCreate) {
|
|
268
|
+
const pointer = viewStore.getActiveViewId();
|
|
269
|
+
if (sync.getSnapshot().views.some(view => view.id === pointer)) {
|
|
270
|
+
loadAndKeep(sync, pointer);
|
|
271
|
+
}
|
|
296
272
|
}
|
|
297
273
|
}
|
|
274
|
+
urlPhase = 'live';
|
|
298
275
|
}, [
|
|
299
|
-
|
|
276
|
+
gridApi,
|
|
277
|
+
enabledWhenReady,
|
|
278
|
+
urlOptions,
|
|
279
|
+
addressBar,
|
|
280
|
+
viewStore,
|
|
281
|
+
storageKey,
|
|
300
282
|
autoApplyOnMount,
|
|
301
|
-
|
|
283
|
+
holder,
|
|
302
284
|
reportFailure,
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
commitActiveViewId,
|
|
307
|
-
applyModelToGrid
|
|
285
|
+
dropSync,
|
|
286
|
+
keepApplyIssues,
|
|
287
|
+
loadAndKeep
|
|
308
288
|
]);
|
|
309
|
-
useEffect(() => {
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
let applied = appliedModelRef.current;
|
|
319
|
-
if (!pendingWriteRef.current && (!activeId || !applied || !viewStore)) {
|
|
320
|
-
return;
|
|
321
|
-
}
|
|
322
|
-
let live;
|
|
323
|
-
try {
|
|
324
|
-
live = gridApi.getFilterModel() ?? {};
|
|
325
|
-
}
|
|
326
|
-
catch {
|
|
327
|
-
return;
|
|
328
|
-
}
|
|
329
|
-
const pending = pendingWriteRef.current;
|
|
330
|
-
if (pending) {
|
|
331
|
-
if (!fromEvent)
|
|
332
|
-
return;
|
|
333
|
-
const inWrite = writingRef.current;
|
|
334
|
-
if (!inWrite &&
|
|
335
|
-
pending.before &&
|
|
336
|
-
sameFilterModel(live, pending.before)) {
|
|
337
|
-
return;
|
|
338
|
-
}
|
|
339
|
-
pendingWriteRef.current = null;
|
|
340
|
-
if (inWrite ||
|
|
341
|
-
(source === 'api' &&
|
|
342
|
-
couldBeLanding(live, pending.expected, pending.before)) ||
|
|
343
|
-
sameFilterModel(live, pending.expected)) {
|
|
344
|
-
appliedModelRef.current = live;
|
|
345
|
-
return;
|
|
346
|
-
}
|
|
347
|
-
applied = pending.expected;
|
|
348
|
-
}
|
|
349
|
-
if (!activeId || !applied || !viewStore)
|
|
350
|
-
return;
|
|
351
|
-
if (sameFilterModel(live, applied))
|
|
352
|
-
return;
|
|
353
|
-
commitActiveViewId(null);
|
|
354
|
-
try {
|
|
355
|
-
if (viewStore.getActiveViewId() === activeId) {
|
|
356
|
-
viewStore.persistActiveViewId(null);
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
catch (error) {
|
|
360
|
-
reportFailure(error, 'filter-change');
|
|
361
|
-
}
|
|
362
|
-
};
|
|
363
|
-
const updateState = (fromEvent, source) => {
|
|
364
|
-
syncActiveViewToGrid(fromEvent, source);
|
|
365
|
-
try {
|
|
366
|
-
contextRef.current = 'update-state';
|
|
367
|
-
const { url, hasFilters, issues } = urlSyncRef.current.getUrlState();
|
|
368
|
-
setCurrentUrl(url);
|
|
369
|
-
setHasFilters(hasFilters);
|
|
370
|
-
setWriteIssues(keepIssues(issues));
|
|
371
|
-
}
|
|
372
|
-
catch (error) {
|
|
373
|
-
reportFailure(error, 'update-state');
|
|
374
|
-
}
|
|
375
|
-
};
|
|
376
|
-
const onFilterChanged = (event) => updateState(true, event?.source);
|
|
377
|
-
gridApi.addEventListener('filterChanged', onFilterChanged);
|
|
378
|
-
updateState(false);
|
|
379
|
-
return () => {
|
|
380
|
-
gridApi.removeEventListener('filterChanged', onFilterChanged);
|
|
381
|
-
};
|
|
382
|
-
}, [isReady, gridApi, reportFailure, viewStore, commitActiveViewId]);
|
|
383
|
-
useEffect(() => {
|
|
384
|
-
return () => {
|
|
385
|
-
if (urlSyncRef.current) {
|
|
386
|
-
urlSyncRef.current.destroy();
|
|
387
|
-
urlSyncRef.current = null;
|
|
388
|
-
}
|
|
389
|
-
};
|
|
390
|
-
}, []);
|
|
289
|
+
useEffect(() => () => {
|
|
290
|
+
const current = liveSyncRef.current;
|
|
291
|
+
if (current)
|
|
292
|
+
dropSync(current);
|
|
293
|
+
}, [dropSync]);
|
|
294
|
+
const readOptions = useCallback((context) => resolveOptions({
|
|
295
|
+
...urlOptions,
|
|
296
|
+
onParseError: issue => reportFailure(issue, context)
|
|
297
|
+
}), [urlOptions, reportFailure]);
|
|
391
298
|
const shareUrl = useCallback((baseUrl) => {
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
299
|
+
const fallback = baseUrl ?? pageUrl();
|
|
300
|
+
if (!gridApi || !activeSync())
|
|
301
|
+
return fallback;
|
|
395
302
|
try {
|
|
396
|
-
|
|
397
|
-
return urlSyncRef.current.generateUrl(baseUrl);
|
|
303
|
+
return urlWithFilters(gridApi, fallback, readOptions('generate-share-url'));
|
|
398
304
|
}
|
|
399
305
|
catch (error) {
|
|
400
306
|
reportFailure(error, 'generate-share-url');
|
|
401
|
-
return
|
|
307
|
+
return fallback;
|
|
402
308
|
}
|
|
403
|
-
}, [reportFailure]);
|
|
309
|
+
}, [gridApi, activeSync, readOptions, reportFailure]);
|
|
404
310
|
const getQueryParams = useCallback(() => {
|
|
405
|
-
if (!
|
|
311
|
+
if (!gridApi || !activeSync())
|
|
406
312
|
return '';
|
|
407
|
-
}
|
|
408
313
|
try {
|
|
409
|
-
|
|
410
|
-
return
|
|
314
|
+
const url = urlWithFilters(gridApi, pageUrl(), readOptions('get-query-params'));
|
|
315
|
+
return new URL(url).search;
|
|
411
316
|
}
|
|
412
317
|
catch (error) {
|
|
413
318
|
reportFailure(error, 'get-query-params');
|
|
414
319
|
return '';
|
|
415
320
|
}
|
|
416
|
-
}, [reportFailure]);
|
|
321
|
+
}, [gridApi, activeSync, readOptions, reportFailure]);
|
|
417
322
|
const applyUrlFilters = useCallback((url) => {
|
|
418
|
-
|
|
323
|
+
const sync = activeSync();
|
|
324
|
+
if (!sync) {
|
|
419
325
|
reportNotReady('applyUrlFilters', 'apply-url-filters');
|
|
420
326
|
return;
|
|
421
327
|
}
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
const applied = { issues: NO_ISSUES };
|
|
426
|
-
writeOverPendingView(() => {
|
|
427
|
-
applied.issues = sync.applyFromUrl(url).issues;
|
|
428
|
-
});
|
|
429
|
-
setApplyIssues(keepIssues(applied.issues));
|
|
430
|
-
}
|
|
431
|
-
catch (error) {
|
|
432
|
-
reportFailure(error, 'apply-url-filters');
|
|
433
|
-
setApplyIssues(keepIssues([toError(error)]));
|
|
434
|
-
}
|
|
435
|
-
}, [reportFailure, reportNotReady, writeOverPendingView]);
|
|
328
|
+
contextRef.current = 'apply-url-filters';
|
|
329
|
+
keepApplyIssues(sync.applyUrl(url).issues);
|
|
330
|
+
}, [activeSync, reportNotReady, keepApplyIssues]);
|
|
436
331
|
const clearFilters = useCallback(() => {
|
|
437
|
-
|
|
332
|
+
const sync = activeSync();
|
|
333
|
+
if (!sync) {
|
|
438
334
|
reportNotReady('clearFilters', 'clear-filters');
|
|
439
335
|
return;
|
|
440
336
|
}
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
writeOverPendingView(() => sync.clearFilters());
|
|
445
|
-
setApplyIssues(NO_ISSUES);
|
|
446
|
-
}
|
|
447
|
-
catch (error) {
|
|
448
|
-
reportFailure(error, 'clear-filters');
|
|
449
|
-
}
|
|
450
|
-
}, [reportFailure, reportNotReady, writeOverPendingView]);
|
|
337
|
+
contextRef.current = 'clear-filters';
|
|
338
|
+
keepApplyIssues(sync.applyFilters({}).issues);
|
|
339
|
+
}, [activeSync, reportNotReady, keepApplyIssues]);
|
|
451
340
|
const parseUrlFilters = useCallback((url) => {
|
|
452
341
|
if (!gridApi) {
|
|
453
342
|
return {};
|
|
454
343
|
}
|
|
455
344
|
try {
|
|
456
|
-
return parseFilters(url,
|
|
345
|
+
return parseFilters(url, {
|
|
346
|
+
...urlOptions,
|
|
347
|
+
onParseError: issue => callbacksRef.current.onParseError?.(issue)
|
|
348
|
+
});
|
|
457
349
|
}
|
|
458
350
|
catch (error) {
|
|
459
351
|
reportFailure(error, 'parse-url-filters');
|
|
460
352
|
return {};
|
|
461
353
|
}
|
|
462
|
-
}, [
|
|
354
|
+
}, [urlOptions, gridApi, reportFailure]);
|
|
463
355
|
const applyFilters = useCallback((filters) => {
|
|
464
|
-
|
|
356
|
+
const sync = activeSync();
|
|
357
|
+
if (!sync) {
|
|
465
358
|
reportNotReady('applyFilters', 'apply-filters');
|
|
466
359
|
return;
|
|
467
360
|
}
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
applied.issues = sync.applyFilters(filters).issues;
|
|
474
|
-
});
|
|
475
|
-
setApplyIssues(keepIssues(applied.issues));
|
|
476
|
-
}
|
|
477
|
-
catch (error) {
|
|
478
|
-
reportFailure(error, 'apply-filters');
|
|
479
|
-
}
|
|
480
|
-
}, [reportFailure, reportNotReady, writeOverPendingView]);
|
|
481
|
-
const getFiltersAsFormat = useCallback((format) => {
|
|
482
|
-
if (!urlSyncRef.current) {
|
|
361
|
+
contextRef.current = 'apply-filters';
|
|
362
|
+
keepApplyIssues(sync.applyFilters(filters).issues);
|
|
363
|
+
}, [activeSync, reportNotReady, keepApplyIssues]);
|
|
364
|
+
const getFiltersAsFormat = useCallback((asFormat) => {
|
|
365
|
+
if (!gridApi || !activeSync())
|
|
483
366
|
return '';
|
|
484
|
-
}
|
|
485
367
|
try {
|
|
486
|
-
|
|
487
|
-
return urlSyncRef.current.getFiltersAsFormat(format);
|
|
368
|
+
return filtersAsFormat(gridApi, asFormat, readOptions('get-filters-as-format'));
|
|
488
369
|
}
|
|
489
370
|
catch (error) {
|
|
490
371
|
reportFailure(error, 'get-filters-as-format');
|
|
491
372
|
return '';
|
|
492
373
|
}
|
|
493
|
-
}, [reportFailure]);
|
|
494
|
-
const
|
|
495
|
-
|
|
496
|
-
return DEFAULT_CONFIG.serialization;
|
|
497
|
-
}
|
|
498
|
-
try {
|
|
499
|
-
return urlSyncRef.current.getSerializationMode();
|
|
500
|
-
}
|
|
501
|
-
catch (error) {
|
|
502
|
-
reportFailure(error, 'get-current-format');
|
|
503
|
-
return DEFAULT_CONFIG.serialization;
|
|
504
|
-
}
|
|
505
|
-
}, [reportFailure]);
|
|
374
|
+
}, [gridApi, activeSync, readOptions, reportFailure]);
|
|
375
|
+
const getSerializationMode = useCallback(() => activeSync() ? serialization : DEFAULT_CONFIG.serialization, [activeSync, serialization]);
|
|
376
|
+
const getCurrentFormat = getSerializationMode;
|
|
506
377
|
const saveView = useCallback((name) => {
|
|
507
|
-
if (!viewStore || !enabledWhenReady)
|
|
378
|
+
if (!viewStore || !enabledWhenReady)
|
|
508
379
|
return null;
|
|
509
|
-
|
|
510
|
-
if (!
|
|
380
|
+
const sync = activeSync();
|
|
381
|
+
if (!sync) {
|
|
511
382
|
reportNotReady('saveView', 'save-view');
|
|
512
383
|
return null;
|
|
513
384
|
}
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
commitActiveViewId(view.id);
|
|
518
|
-
appliedModelRef.current = view.filterModel;
|
|
519
|
-
pendingWriteRef.current = null;
|
|
520
|
-
return view;
|
|
521
|
-
}
|
|
522
|
-
catch (error) {
|
|
523
|
-
reportFailure(error, 'save-view');
|
|
524
|
-
return null;
|
|
525
|
-
}
|
|
526
|
-
}, [
|
|
527
|
-
viewStore,
|
|
528
|
-
gridApi,
|
|
529
|
-
reportFailure,
|
|
530
|
-
syncViewsFromStore,
|
|
531
|
-
reportNotReady,
|
|
532
|
-
enabledWhenReady,
|
|
533
|
-
commitActiveViewId
|
|
534
|
-
]);
|
|
385
|
+
contextRef.current = 'save-view';
|
|
386
|
+
return sync.saveView(name);
|
|
387
|
+
}, [viewStore, enabledWhenReady, activeSync, reportNotReady]);
|
|
535
388
|
const loadView = useCallback((id) => {
|
|
536
|
-
if (!viewStore || !enabledWhenReady)
|
|
389
|
+
if (!viewStore || !enabledWhenReady)
|
|
537
390
|
return;
|
|
538
|
-
|
|
539
|
-
if (!
|
|
391
|
+
const sync = activeSync();
|
|
392
|
+
if (!sync) {
|
|
540
393
|
reportNotReady('loadView', 'load-view');
|
|
541
394
|
return;
|
|
542
395
|
}
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
const rollBack = () => {
|
|
547
|
-
commitActiveViewId(previous);
|
|
548
|
-
appliedModelRef.current = previousApplied;
|
|
549
|
-
};
|
|
550
|
-
if (id == null) {
|
|
551
|
-
commitActiveViewId(null);
|
|
552
|
-
try {
|
|
553
|
-
applyModelToGrid(gridApi, {});
|
|
554
|
-
}
|
|
555
|
-
catch (error) {
|
|
556
|
-
rollBack();
|
|
557
|
-
throw error;
|
|
558
|
-
}
|
|
559
|
-
setApplyIssues(NO_ISSUES);
|
|
560
|
-
viewStore.persistActiveViewId(null);
|
|
561
|
-
return;
|
|
562
|
-
}
|
|
563
|
-
const view = viewStore
|
|
564
|
-
.listViews()
|
|
565
|
-
.find(candidate => candidate.id === id);
|
|
566
|
-
if (!view) {
|
|
567
|
-
syncViewsFromStore();
|
|
568
|
-
reportFailure(new URLSyncError(`No saved view with id "${id}"`, undefined, {
|
|
569
|
-
code: 'VIEW_NOT_FOUND'
|
|
570
|
-
}), 'load-view');
|
|
571
|
-
return;
|
|
572
|
-
}
|
|
573
|
-
commitActiveViewId(view.id);
|
|
574
|
-
try {
|
|
575
|
-
applyModelToGrid(gridApi, view.filterModel);
|
|
576
|
-
}
|
|
577
|
-
catch (error) {
|
|
578
|
-
rollBack();
|
|
579
|
-
throw error;
|
|
580
|
-
}
|
|
581
|
-
setApplyIssues(NO_ISSUES);
|
|
582
|
-
if (activeViewIdRef.current === view.id) {
|
|
583
|
-
viewStore.persistActiveViewId(view.id);
|
|
584
|
-
}
|
|
585
|
-
}
|
|
586
|
-
catch (error) {
|
|
587
|
-
reportFailure(error, 'load-view');
|
|
588
|
-
}
|
|
589
|
-
}, [
|
|
590
|
-
gridApi,
|
|
591
|
-
viewStore,
|
|
592
|
-
reportFailure,
|
|
593
|
-
reportNotReady,
|
|
594
|
-
syncViewsFromStore,
|
|
595
|
-
enabledWhenReady,
|
|
596
|
-
commitActiveViewId,
|
|
597
|
-
applyModelToGrid
|
|
598
|
-
]);
|
|
396
|
+
contextRef.current = 'load-view';
|
|
397
|
+
loadAndKeep(sync, id);
|
|
398
|
+
}, [viewStore, enabledWhenReady, activeSync, reportNotReady, loadAndKeep]);
|
|
599
399
|
const deleteView = useCallback((id) => {
|
|
600
|
-
if (!viewStore || !enabledWhenReady)
|
|
400
|
+
if (!viewStore || !enabledWhenReady)
|
|
401
|
+
return;
|
|
402
|
+
const sync = holder.get();
|
|
403
|
+
if (sync) {
|
|
404
|
+
contextRef.current = 'delete-view';
|
|
405
|
+
sync.deleteView(id);
|
|
601
406
|
return;
|
|
602
407
|
}
|
|
603
408
|
try {
|
|
604
|
-
const wasActive = activeViewIdRef.current === id;
|
|
605
|
-
const view = viewStore.listViews().find(entry => entry.id === id);
|
|
606
|
-
const applied = appliedModelRef.current ?? view?.filterModel;
|
|
607
409
|
viewStore.deleteView(id);
|
|
608
|
-
syncViewsFromStore();
|
|
609
|
-
if (wasActive) {
|
|
610
|
-
commitActiveViewId(null);
|
|
611
|
-
}
|
|
612
|
-
if (wasActive && applied !== undefined && gridApi !== null) {
|
|
613
|
-
try {
|
|
614
|
-
const current = callGrid(() => gridApi.getFilterModel());
|
|
615
|
-
if (current && sameFilterModel(current, applied)) {
|
|
616
|
-
callGrid(() => gridApi.setFilterModel({}));
|
|
617
|
-
}
|
|
618
|
-
}
|
|
619
|
-
catch (error) {
|
|
620
|
-
reportFailure(error, 'delete-view');
|
|
621
|
-
}
|
|
622
|
-
}
|
|
623
410
|
}
|
|
624
411
|
catch (error) {
|
|
625
412
|
reportFailure(error, 'delete-view');
|
|
626
413
|
}
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
]);
|
|
635
|
-
const issues = useMemo(() => applyIssues.length === 0
|
|
636
|
-
? writeIssues
|
|
637
|
-
: writeIssues.length === 0
|
|
638
|
-
? applyIssues
|
|
639
|
-
: [...applyIssues, ...writeIssues], [applyIssues, writeIssues]);
|
|
414
|
+
holder.setIdleViews(viewStore.listViews());
|
|
415
|
+
}, [viewStore, enabledWhenReady, holder, reportFailure]);
|
|
416
|
+
const views = useMemo(() => [...snapshot.views], [snapshot.views]);
|
|
417
|
+
const { activeViewId } = snapshot;
|
|
418
|
+
const issues = useMemo(() => {
|
|
419
|
+
const data = isReady ? snapshot.issues.filter(isDataIssue) : NO_ISSUES;
|
|
420
|
+
return data.length ? data : NO_ISSUES;
|
|
421
|
+
}, [isReady, snapshot.issues]);
|
|
640
422
|
const hookApi = useMemo(() => ({
|
|
641
423
|
shareUrl,
|
|
642
424
|
getQueryParams,
|
|
@@ -649,6 +431,7 @@ export function useAGGridUrlSync(gridApi, options = {}) {
|
|
|
649
431
|
parseUrlFilters,
|
|
650
432
|
applyFilters,
|
|
651
433
|
getFiltersAsFormat,
|
|
434
|
+
getSerializationMode,
|
|
652
435
|
getCurrentFormat,
|
|
653
436
|
views,
|
|
654
437
|
activeViewId,
|
|
@@ -667,6 +450,7 @@ export function useAGGridUrlSync(gridApi, options = {}) {
|
|
|
667
450
|
parseUrlFilters,
|
|
668
451
|
applyFilters,
|
|
669
452
|
getFiltersAsFormat,
|
|
453
|
+
getSerializationMode,
|
|
670
454
|
getCurrentFormat,
|
|
671
455
|
views,
|
|
672
456
|
activeViewId,
|