@vitessce/vit-s 3.1.3 → 3.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/dist/index.js +1673 -130
- package/dist-tsc/data-hook-utils.d.ts +1 -0
- package/dist-tsc/data-hook-utils.d.ts.map +1 -1
- package/dist-tsc/data-hook-utils.js +12 -4
- package/dist-tsc/data-hooks-multilevel-utils.d.ts +77 -0
- package/dist-tsc/data-hooks-multilevel-utils.d.ts.map +1 -0
- package/dist-tsc/data-hooks-multilevel-utils.js +418 -0
- package/dist-tsc/data-hooks-multilevel-utils.test.d.ts +2 -0
- package/dist-tsc/data-hooks-multilevel-utils.test.d.ts.map +1 -0
- package/dist-tsc/data-hooks-multilevel-utils.test.js +101 -0
- package/dist-tsc/data-hooks-multilevel.d.ts +8 -0
- package/dist-tsc/data-hooks-multilevel.d.ts.map +1 -0
- package/dist-tsc/data-hooks-multilevel.js +125 -0
- package/dist-tsc/data-hooks.d.ts +7 -0
- package/dist-tsc/data-hooks.d.ts.map +1 -1
- package/dist-tsc/data-hooks.js +75 -6
- package/dist-tsc/hooks.js +1 -1
- package/dist-tsc/index.d.ts +3 -2
- package/dist-tsc/index.js +3 -2
- package/dist-tsc/shared-mui/components.d.ts.map +1 -1
- package/dist-tsc/shared-mui/components.js +3 -2
- package/dist-tsc/state/hooks.d.ts +80 -21
- package/dist-tsc/state/hooks.d.ts.map +1 -1
- package/dist-tsc/state/hooks.js +428 -103
- package/dist-tsc/state/hooks.test.d.ts +2 -0
- package/dist-tsc/state/hooks.test.d.ts.map +1 -0
- package/dist-tsc/state/hooks.test.js +149 -0
- package/dist-tsc/state/spatial-reducers.d.ts +25 -0
- package/dist-tsc/state/spatial-reducers.d.ts.map +1 -0
- package/dist-tsc/state/spatial-reducers.js +251 -0
- package/dist-tsc/state/spatial-reducers.test.d.ts +2 -0
- package/dist-tsc/state/spatial-reducers.test.d.ts.map +1 -0
- package/dist-tsc/state/spatial-reducers.test.js +1382 -0
- package/package.json +5 -5
- package/src/data-hook-utils.js +19 -3
- package/src/data-hooks-multilevel-utils.js +571 -0
- package/src/data-hooks-multilevel-utils.test.js +110 -0
- package/src/data-hooks-multilevel.js +226 -0
- package/src/data-hooks.js +156 -6
- package/src/hooks.js +1 -1
- package/src/index.js +25 -0
- package/src/shared-mui/components.js +9 -4
- package/src/state/hooks.js +485 -104
- package/src/state/hooks.test.js +182 -0
- package/src/state/spatial-reducers.js +291 -0
- package/src/state/spatial-reducers.test.js +1432 -0
package/dist-tsc/state/hooks.js
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
|
+
/* eslint-disable max-len */
|
|
1
2
|
/* eslint-disable react-refresh/only-export-components */
|
|
2
3
|
import { useRef, useCallback, useMemo } from 'react';
|
|
3
4
|
import create from 'zustand';
|
|
4
5
|
import createContext from 'zustand/context';
|
|
5
6
|
import shallow from 'zustand/shallow';
|
|
6
|
-
import { isMatch, cloneDeep } from 'lodash-es';
|
|
7
|
+
import { isMatch, merge, cloneDeep } from 'lodash-es';
|
|
7
8
|
import { CoordinationType } from '@vitessce/constants-internal';
|
|
8
9
|
import { fromEntries, capitalize } from '@vitessce/utils';
|
|
10
|
+
import { removeImageChannelInMetaCoordinationScopesHelper, addImageChannelInMetaCoordinationScopesHelper, } from './spatial-reducers.js';
|
|
9
11
|
// Reference: https://github.com/pmndrs/zustand#react-context
|
|
10
12
|
// Reference: https://github.com/pmndrs/zustand/blob/e47ea03/tests/context.test.tsx#L60
|
|
11
13
|
const { Provider: ViewConfigProviderLocal, useStore: useViewConfigStoreLocal, useStoreApi: useViewConfigStoreApiLocal, } = createContext();
|
|
@@ -15,6 +17,85 @@ export const useViewConfigStoreApi = useViewConfigStoreApiLocal;
|
|
|
15
17
|
const { Provider: AuxiliaryProviderLocal, useStore: useAuxiliaryStoreLocal, } = createContext();
|
|
16
18
|
export const AuxiliaryProvider = AuxiliaryProviderLocal;
|
|
17
19
|
export const useAuxiliaryStore = useAuxiliaryStoreLocal;
|
|
20
|
+
/**
|
|
21
|
+
* Get the "computed" coordinationScopes after accounting for
|
|
22
|
+
* meta-coordination.
|
|
23
|
+
* @param {*} coordinationScopes The coordinationScopes for a view.
|
|
24
|
+
* @param {*} coordinationSpace The coordinationSpace for a config.
|
|
25
|
+
* @returns {string|undefined} The coordinationScopesBy after meta-coordination.
|
|
26
|
+
*/
|
|
27
|
+
export function getScopes(coordinationScopes, metaSpace) {
|
|
28
|
+
let result = { ...coordinationScopes };
|
|
29
|
+
// Check if there is a matching meta-scope.
|
|
30
|
+
if (metaSpace) {
|
|
31
|
+
// Determine if there is a meta-scope that would take precedence.
|
|
32
|
+
const metaScopes = coordinationScopes[CoordinationType.META_COORDINATION_SCOPES];
|
|
33
|
+
if (metaScopes && metaSpace) {
|
|
34
|
+
// The view.coordinationScopes.metaCoordinationScopes might be an array or a string.
|
|
35
|
+
// Convert to an array.
|
|
36
|
+
const metaScopesArr = Array.isArray(metaScopes) ? metaScopes : [metaScopes];
|
|
37
|
+
metaScopesArr.forEach((metaScope) => {
|
|
38
|
+
// Merge the original coordinationScopes with the matching meta-coordinationScopes
|
|
39
|
+
// from the coordinationSpace.
|
|
40
|
+
result = merge(result, metaSpace[metaScope]);
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return result;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Get the "computed" coordinationScopesBy after accounting for
|
|
48
|
+
* meta-coordination.
|
|
49
|
+
* @param {*} coordinationScopes The coordinationScopes for a view.
|
|
50
|
+
* @param {*} coordinationScopesBy The coordinationScopesBy for a view.
|
|
51
|
+
* @param {*} coordinationSpace The coordinationSpace for a config.
|
|
52
|
+
* @returns {string|undefined} The coordinationScopesBy after meta-coordination.
|
|
53
|
+
*/
|
|
54
|
+
export function getScopesBy(coordinationScopes, coordinationScopesBy, metaSpaceBy) {
|
|
55
|
+
let result = { ...coordinationScopesBy };
|
|
56
|
+
// Check if there is a matching meta-scope.
|
|
57
|
+
if (metaSpaceBy) {
|
|
58
|
+
// Determine if there is a meta-scope that would take precedence.
|
|
59
|
+
const metaScopesBy = coordinationScopes[CoordinationType.META_COORDINATION_SCOPES_BY];
|
|
60
|
+
if (metaSpaceBy && metaScopesBy) {
|
|
61
|
+
// The view.coordinationScopes.metaCoordinationScopes might be an array or a string.
|
|
62
|
+
// Convert to an array.
|
|
63
|
+
const metaScopesArr = Array.isArray(metaScopesBy) ? metaScopesBy : [metaScopesBy];
|
|
64
|
+
metaScopesArr.forEach((metaScope) => {
|
|
65
|
+
// Merge the original coordinationScopesBy with the matching meta-coordinationScopesBy
|
|
66
|
+
// from the coordinationSpace.
|
|
67
|
+
result = merge(result, metaSpaceBy[metaScope]);
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return result;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Get the matching parameter scope.
|
|
75
|
+
* @param {string} parameter A coordination type.
|
|
76
|
+
* @param {*} coordinationScopes The coordinationScopes for a view.
|
|
77
|
+
* @returns {string|undefined} The coordination scope that matches.
|
|
78
|
+
*/
|
|
79
|
+
export function getParameterScope(parameter, coordinationScopes) {
|
|
80
|
+
return coordinationScopes[parameter];
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Get the matching parameter scope.
|
|
84
|
+
* @param {string} parameter A coordination type.
|
|
85
|
+
* @param {*} coordinationScopes The coordinationScopes for a view.
|
|
86
|
+
* @param {*} coordinationScopesBy The coordinationScopesBy for a view.
|
|
87
|
+
* @returns {string|undefined} The coordination scope that matches.
|
|
88
|
+
*/
|
|
89
|
+
export function getParameterScopeBy(parameter, byType, typeScope, coordinationScopes, coordinationScopesBy) {
|
|
90
|
+
const parameterScopeGlobal = coordinationScopes[parameter];
|
|
91
|
+
// Set parameterScope to the non-meta-value first.
|
|
92
|
+
const parameterScopeByType = coordinationScopesBy?.[byType]?.[parameter];
|
|
93
|
+
if (parameterScopeByType && parameterScopeByType[typeScope]) {
|
|
94
|
+
return parameterScopeByType[typeScope];
|
|
95
|
+
}
|
|
96
|
+
// console.error(`coordination scope for ${parameter} was not found.`);
|
|
97
|
+
return parameterScopeGlobal;
|
|
98
|
+
}
|
|
18
99
|
/**
|
|
19
100
|
* The useViewConfigStore hook is initialized via the zustand
|
|
20
101
|
* create() function, which sets up both the state variables
|
|
@@ -39,18 +120,50 @@ export const createViewConfigStore = (initialLoaders, initialConfig) => create(s
|
|
|
39
120
|
// (although technically also part of state):
|
|
40
121
|
setViewConfig: viewConfig => set({ viewConfig, initialViewConfig: viewConfig }),
|
|
41
122
|
setLoaders: loaders => set({ loaders }),
|
|
42
|
-
setCoordinationValue: ({ parameter,
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
123
|
+
setCoordinationValue: ({ parameter, value, coordinationScopes, byType, typeScope, coordinationScopesBy, }) => set((state) => {
|
|
124
|
+
const { coordinationSpace } = state.viewConfig;
|
|
125
|
+
let scope;
|
|
126
|
+
if (!byType) {
|
|
127
|
+
scope = getParameterScope(parameter, coordinationScopes);
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
scope = getParameterScopeBy(parameter, byType, typeScope, coordinationScopes, coordinationScopesBy);
|
|
131
|
+
if (!scope) {
|
|
132
|
+
// Fall back to using the view-level scope.
|
|
133
|
+
scope = getParameterScope(parameter, coordinationScopes);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return {
|
|
137
|
+
viewConfig: {
|
|
138
|
+
...state.viewConfig,
|
|
139
|
+
coordinationSpace: {
|
|
140
|
+
...coordinationSpace,
|
|
141
|
+
[parameter]: {
|
|
142
|
+
...coordinationSpace[parameter],
|
|
143
|
+
[scope]: value,
|
|
144
|
+
},
|
|
50
145
|
},
|
|
51
146
|
},
|
|
52
|
-
}
|
|
53
|
-
})
|
|
147
|
+
};
|
|
148
|
+
}),
|
|
149
|
+
removeImageChannelInMetaCoordinationScopes: (coordinationScopesRaw, layerScope, channelScope) => set((state) => {
|
|
150
|
+
const { coordinationSpace } = state.viewConfig;
|
|
151
|
+
return {
|
|
152
|
+
viewConfig: {
|
|
153
|
+
...state.viewConfig,
|
|
154
|
+
coordinationSpace: removeImageChannelInMetaCoordinationScopesHelper(coordinationScopesRaw, layerScope, channelScope, coordinationSpace),
|
|
155
|
+
},
|
|
156
|
+
};
|
|
157
|
+
}),
|
|
158
|
+
addImageChannelInMetaCoordinationScopes: (coordinationScopesRaw, layerScope) => set((state) => {
|
|
159
|
+
const { coordinationSpace } = state.viewConfig;
|
|
160
|
+
return {
|
|
161
|
+
viewConfig: {
|
|
162
|
+
...state.viewConfig,
|
|
163
|
+
coordinationSpace: addImageChannelInMetaCoordinationScopesHelper(coordinationScopesRaw, layerScope, coordinationSpace),
|
|
164
|
+
},
|
|
165
|
+
};
|
|
166
|
+
}),
|
|
54
167
|
removeComponent: uid => set((state) => {
|
|
55
168
|
const newLayout = state.viewConfig.layout.filter(c => c.uid !== uid);
|
|
56
169
|
return {
|
|
@@ -203,9 +316,12 @@ export function useCoordination(parameters, coordinationScopes) {
|
|
|
203
316
|
const values = useViewConfigStore((state) => {
|
|
204
317
|
const { coordinationSpace } = state.viewConfig;
|
|
205
318
|
return fromEntries(parameters.map((parameter) => {
|
|
206
|
-
if (coordinationSpace
|
|
207
|
-
const
|
|
208
|
-
|
|
319
|
+
if (coordinationSpace) {
|
|
320
|
+
const parameterScope = getParameterScope(parameter, coordinationScopes);
|
|
321
|
+
if (coordinationSpace && coordinationSpace[parameter]) {
|
|
322
|
+
const value = coordinationSpace[parameter][parameterScope];
|
|
323
|
+
return [parameter, value];
|
|
324
|
+
}
|
|
209
325
|
}
|
|
210
326
|
return [parameter, undefined];
|
|
211
327
|
}));
|
|
@@ -214,7 +330,7 @@ export function useCoordination(parameters, coordinationScopes) {
|
|
|
214
330
|
const setterName = `set${capitalize(parameter)}`;
|
|
215
331
|
const setterFunc = value => setCoordinationValue({
|
|
216
332
|
parameter,
|
|
217
|
-
|
|
333
|
+
coordinationScopes,
|
|
218
334
|
value,
|
|
219
335
|
});
|
|
220
336
|
return [setterName, setterFunc];
|
|
@@ -222,8 +338,125 @@ export function useCoordination(parameters, coordinationScopes) {
|
|
|
222
338
|
})), [parameters, coordinationScopes]);
|
|
223
339
|
return [values, setters];
|
|
224
340
|
}
|
|
341
|
+
export function useMultiCoordinationScopes(parameter, coordinationScopes) {
|
|
342
|
+
return useMemo(() => {
|
|
343
|
+
const scopes = coordinationScopes[parameter];
|
|
344
|
+
return Array.isArray(scopes) ? scopes : [scopes];
|
|
345
|
+
}, [parameter, coordinationScopes]);
|
|
346
|
+
}
|
|
347
|
+
export function useMultiCoordinationScopesNonNull(parameter, coordinationScopes) {
|
|
348
|
+
const scopes = getParameterScope(parameter, coordinationScopes);
|
|
349
|
+
// Return array of coordination scopes,
|
|
350
|
+
// but filter out any whose value is null / falsey.
|
|
351
|
+
const parameterSpace = useViewConfigStore((state) => {
|
|
352
|
+
const { coordinationSpace } = state.viewConfig;
|
|
353
|
+
return coordinationSpace?.[parameter];
|
|
354
|
+
}, shallow);
|
|
355
|
+
const nonNullScopes = useMemo(() => {
|
|
356
|
+
// Convert a single scope to an array of scopes to be consistent.
|
|
357
|
+
const scopesArr = Array.isArray(scopes) ? scopes : [scopes];
|
|
358
|
+
return scopesArr.filter((scope) => {
|
|
359
|
+
if (parameterSpace) {
|
|
360
|
+
const value = parameterSpace[scope];
|
|
361
|
+
return value !== null;
|
|
362
|
+
}
|
|
363
|
+
return false;
|
|
364
|
+
});
|
|
365
|
+
}, [parameter, coordinationScopes, scopes, parameterSpace]);
|
|
366
|
+
return nonNullScopes;
|
|
367
|
+
}
|
|
368
|
+
export function useMultiCoordinationScopesSecondary(parameter, byType, coordinationScopes, coordinationScopesBy) {
|
|
369
|
+
return useMemo(() => {
|
|
370
|
+
const scopes = getParameterScope(byType, coordinationScopes);
|
|
371
|
+
if (scopes && coordinationScopesBy?.[byType]?.[parameter]) {
|
|
372
|
+
const scopesArr = Array.isArray(scopes) ? scopes : [scopes];
|
|
373
|
+
return [scopesArr, fromEntries(scopesArr.map((scope) => {
|
|
374
|
+
const secondaryScopes = coordinationScopesBy[byType][parameter][scope];
|
|
375
|
+
const secondaryScopesArr = Array.isArray(secondaryScopes)
|
|
376
|
+
? secondaryScopes
|
|
377
|
+
: [secondaryScopes];
|
|
378
|
+
return [scope, secondaryScopesArr];
|
|
379
|
+
}))];
|
|
380
|
+
}
|
|
381
|
+
// Fallback from fine-grained to coarse-grained.
|
|
382
|
+
if (scopes && !coordinationScopesBy?.[byType]?.[parameter] && coordinationScopes?.[parameter]) {
|
|
383
|
+
const scopesArr = Array.isArray(scopes) ? scopes : [scopes];
|
|
384
|
+
return [scopesArr, fromEntries(scopesArr.map((scope) => {
|
|
385
|
+
const secondaryScopes = coordinationScopes?.[parameter];
|
|
386
|
+
const secondaryScopesArr = Array.isArray(secondaryScopes)
|
|
387
|
+
? secondaryScopes
|
|
388
|
+
: [secondaryScopes];
|
|
389
|
+
return [scope, secondaryScopesArr];
|
|
390
|
+
}))];
|
|
391
|
+
}
|
|
392
|
+
return [[], {}];
|
|
393
|
+
}, [parameter, byType, coordinationScopes, coordinationScopesBy]);
|
|
394
|
+
}
|
|
395
|
+
export function useMultiCoordinationScopesSecondaryNonNull(parameter, byType, coordinationScopes, coordinationScopesBy) {
|
|
396
|
+
const scopes = getParameterScope(byType, coordinationScopes);
|
|
397
|
+
const parameterSpace = useViewConfigStore((state) => {
|
|
398
|
+
const { coordinationSpace } = state.viewConfig;
|
|
399
|
+
return coordinationSpace?.[parameter];
|
|
400
|
+
}, shallow);
|
|
401
|
+
const byTypeSpace = useViewConfigStore((state) => {
|
|
402
|
+
const { coordinationSpace } = state.viewConfig;
|
|
403
|
+
return coordinationSpace?.[byType];
|
|
404
|
+
}, shallow);
|
|
405
|
+
return useMemo(() => {
|
|
406
|
+
if (scopes && coordinationScopesBy?.[byType]?.[parameter]) {
|
|
407
|
+
const scopesArr = Array.isArray(scopes) ? scopes : [scopes];
|
|
408
|
+
const scopesNonNull = scopesArr.filter((scope) => {
|
|
409
|
+
if (byTypeSpace) {
|
|
410
|
+
const value = byTypeSpace[scope];
|
|
411
|
+
return value !== null;
|
|
412
|
+
}
|
|
413
|
+
return false;
|
|
414
|
+
});
|
|
415
|
+
return [scopesNonNull, fromEntries(scopesNonNull.map((scope) => {
|
|
416
|
+
const secondaryScopes = coordinationScopesBy[byType][parameter][scope];
|
|
417
|
+
const secondaryScopesArr = Array.isArray(secondaryScopes)
|
|
418
|
+
? secondaryScopes
|
|
419
|
+
: [secondaryScopes];
|
|
420
|
+
const secondaryScopesNonNull = secondaryScopesArr.filter((innerScope) => {
|
|
421
|
+
if (parameterSpace) {
|
|
422
|
+
const value = parameterSpace[innerScope];
|
|
423
|
+
return value !== null;
|
|
424
|
+
}
|
|
425
|
+
return false;
|
|
426
|
+
});
|
|
427
|
+
return [scope, secondaryScopesNonNull];
|
|
428
|
+
}))];
|
|
429
|
+
}
|
|
430
|
+
// Fallback from fine-grained to coarse-grained.
|
|
431
|
+
if (scopes && !coordinationScopesBy?.[byType]?.[parameter] && coordinationScopes?.[parameter]) {
|
|
432
|
+
const scopesArr = Array.isArray(scopes) ? scopes : [scopes];
|
|
433
|
+
const scopesNonNull = scopesArr.filter((scope) => {
|
|
434
|
+
if (byTypeSpace) {
|
|
435
|
+
const value = byTypeSpace[scope];
|
|
436
|
+
return value !== null;
|
|
437
|
+
}
|
|
438
|
+
return false;
|
|
439
|
+
});
|
|
440
|
+
return [scopesNonNull, fromEntries(scopesNonNull.map((scope) => {
|
|
441
|
+
const secondaryScopes = coordinationScopes?.[parameter];
|
|
442
|
+
const secondaryScopesArr = Array.isArray(secondaryScopes)
|
|
443
|
+
? secondaryScopes
|
|
444
|
+
: [secondaryScopes];
|
|
445
|
+
const secondaryScopesNonNull = secondaryScopesArr.filter((innerScope) => {
|
|
446
|
+
if (parameterSpace) {
|
|
447
|
+
const value = parameterSpace[innerScope];
|
|
448
|
+
return value !== null;
|
|
449
|
+
}
|
|
450
|
+
return false;
|
|
451
|
+
});
|
|
452
|
+
return [scope, secondaryScopesNonNull];
|
|
453
|
+
}))];
|
|
454
|
+
}
|
|
455
|
+
return [[], {}];
|
|
456
|
+
}, [parameter, byType, scopes, coordinationScopes, coordinationScopesBy, parameterSpace, byTypeSpace]);
|
|
457
|
+
}
|
|
225
458
|
export function useMultiCoordinationValues(parameter, coordinationScopes) {
|
|
226
|
-
const scopes = coordinationScopes
|
|
459
|
+
const scopes = getParameterScope(parameter, coordinationScopes);
|
|
227
460
|
// Mapping from dataset coordination scope name to dataset uid
|
|
228
461
|
const vals = useViewConfigStore((state) => {
|
|
229
462
|
const { coordinationSpace } = state.viewConfig;
|
|
@@ -264,64 +497,173 @@ export function useDatasetUids(coordinationScopes) {
|
|
|
264
497
|
*/
|
|
265
498
|
export function useComplexCoordination(parameters, coordinationScopes, coordinationScopesBy, byType) {
|
|
266
499
|
const setCoordinationValue = useViewConfigStore(state => state.setCoordinationValue);
|
|
267
|
-
const
|
|
268
|
-
const byTypeScopes = coordinationScopesBy[byType];
|
|
269
|
-
// Convert a single scope to an array of scopes to be consistent.
|
|
270
|
-
const typeScopesArr = Array.isArray(typeScopes) ? typeScopes : [typeScopes];
|
|
271
|
-
const values = useViewConfigStore((state) => {
|
|
500
|
+
const parameterSpaces = useViewConfigStore((state) => {
|
|
272
501
|
const { coordinationSpace } = state.viewConfig;
|
|
273
|
-
return
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
502
|
+
return parameters.map(parameter => coordinationSpace[parameter]);
|
|
503
|
+
}, shallow);
|
|
504
|
+
const values = useMemo(() => {
|
|
505
|
+
const typeScopes = getParameterScope(byType, coordinationScopes);
|
|
506
|
+
if (typeScopes) {
|
|
507
|
+
// Convert a single scope to an array of scopes to be consistent.
|
|
508
|
+
const typeScopesArr = Array.isArray(typeScopes) ? typeScopes : [typeScopes];
|
|
509
|
+
return fromEntries(typeScopesArr.map((datasetScope) => {
|
|
510
|
+
const datasetValues = fromEntries(parameters.map((parameter, i) => {
|
|
511
|
+
if (parameterSpaces[i]) {
|
|
512
|
+
const parameterSpace = parameterSpaces[i];
|
|
513
|
+
const parameterScope = getParameterScopeBy(parameter, byType, datasetScope, coordinationScopes, coordinationScopesBy);
|
|
514
|
+
if (parameterScope) {
|
|
515
|
+
const value = parameterSpace[parameterScope];
|
|
516
|
+
return [parameter, value];
|
|
517
|
+
}
|
|
518
|
+
// Fall back to global scope for this parameter.
|
|
519
|
+
const globalParameterScope = getParameterScope(parameter, coordinationScopes);
|
|
520
|
+
const globalValue = parameterSpace[globalParameterScope];
|
|
521
|
+
return [parameter, globalValue];
|
|
288
522
|
}
|
|
289
|
-
return [parameter,
|
|
290
|
-
}
|
|
291
|
-
return [
|
|
523
|
+
return [parameter, undefined];
|
|
524
|
+
}));
|
|
525
|
+
return [datasetScope, datasetValues];
|
|
292
526
|
}));
|
|
293
|
-
|
|
294
|
-
}
|
|
527
|
+
}
|
|
528
|
+
return {};
|
|
529
|
+
}, [byType, coordinationScopes, coordinationScopesBy, parameterSpaces]);
|
|
530
|
+
const setters = useMemo(() => {
|
|
531
|
+
const typeScopes = getParameterScope(byType, coordinationScopes);
|
|
532
|
+
if (typeScopes) {
|
|
533
|
+
// Convert a single scope to an array of scopes to be consistent.
|
|
534
|
+
const typeScopesArr = Array.isArray(typeScopes) ? typeScopes : [typeScopes];
|
|
535
|
+
return fromEntries(typeScopesArr.map((datasetScope) => {
|
|
536
|
+
const datasetSetters = fromEntries(parameters.map((parameter) => {
|
|
537
|
+
const setterName = `set${capitalize(parameter)}`;
|
|
538
|
+
const setterFunc = value => setCoordinationValue({
|
|
539
|
+
parameter,
|
|
540
|
+
byType,
|
|
541
|
+
typeScope: datasetScope,
|
|
542
|
+
coordinationScopes,
|
|
543
|
+
coordinationScopesBy,
|
|
544
|
+
value,
|
|
545
|
+
});
|
|
546
|
+
return [setterName, setterFunc];
|
|
547
|
+
}));
|
|
548
|
+
return [datasetScope, datasetSetters];
|
|
549
|
+
}));
|
|
550
|
+
}
|
|
551
|
+
return {};
|
|
552
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
553
|
+
// parameters is assumed to be a constant array.
|
|
554
|
+
}, [coordinationScopes]);
|
|
555
|
+
return [values, setters];
|
|
556
|
+
}
|
|
557
|
+
/**
|
|
558
|
+
* Get the "computed" (i.e., after accounting for meta-coordination)
|
|
559
|
+
* value for coordinationScopes.
|
|
560
|
+
* @param {object} coordinationScopes The original coordinationScopes passed to the view.
|
|
561
|
+
* @returns {object} The coordinationScopes after filling in with meta-coordinationScopes.
|
|
562
|
+
*/
|
|
563
|
+
export function useCoordinationScopes(coordinationScopes) {
|
|
564
|
+
const metaSpace = useViewConfigStore((state) => {
|
|
565
|
+
const { coordinationSpace } = state.viewConfig;
|
|
566
|
+
return coordinationSpace?.[CoordinationType.META_COORDINATION_SCOPES];
|
|
567
|
+
}, shallow);
|
|
568
|
+
const vals = useMemo(() => {
|
|
569
|
+
const scopes = getScopes(coordinationScopes, metaSpace);
|
|
570
|
+
// Prevent infinite loop, delete metaCoordinationScopes now that they are computed.
|
|
571
|
+
delete scopes[CoordinationType.META_COORDINATION_SCOPES];
|
|
572
|
+
return scopes;
|
|
573
|
+
}, [coordinationScopes, metaSpace]);
|
|
574
|
+
return vals;
|
|
575
|
+
}
|
|
576
|
+
/**
|
|
577
|
+
* Get the "computed" (i.e., after accounting for meta-coordination)
|
|
578
|
+
* value for coordinationScopesBy.
|
|
579
|
+
* @param {object} coordinationScopes The original coordinationScopes passed to the view.
|
|
580
|
+
* @param {object} coordinationScopesBy The original coordinationScopesBy passed to the view.
|
|
581
|
+
* @returns {object} The coordinationScopesBy after filling in with meta-coordinationScopesBy.
|
|
582
|
+
*/
|
|
583
|
+
export function useCoordinationScopesBy(coordinationScopes, coordinationScopesBy) {
|
|
584
|
+
const metaSpaceBy = useViewConfigStore((state) => {
|
|
585
|
+
const { coordinationSpace } = state.viewConfig;
|
|
586
|
+
return coordinationSpace?.[CoordinationType.META_COORDINATION_SCOPES_BY];
|
|
295
587
|
}, shallow);
|
|
296
|
-
const
|
|
297
|
-
const
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
588
|
+
const vals = useMemo(() => {
|
|
589
|
+
const scopesBy = getScopesBy(coordinationScopes, coordinationScopesBy, metaSpaceBy);
|
|
590
|
+
return scopesBy;
|
|
591
|
+
}, [coordinationScopes, coordinationScopesBy, metaSpaceBy]);
|
|
592
|
+
return vals;
|
|
593
|
+
}
|
|
594
|
+
/**
|
|
595
|
+
* Use a second level of complex coordination.
|
|
596
|
+
* @param {string[]} parameters Array of coordination types.
|
|
597
|
+
* @param {object} coordinationScopesBy The coordinationScopesBy object from the view definition.
|
|
598
|
+
* @param {string} primaryType The first-level coordination type, such as spatialImageLayer.
|
|
599
|
+
* @param {string} secondaryType The second-level coordination type, such as spatialImageChannel.
|
|
600
|
+
* @returns The results of useComplexCoordination.
|
|
601
|
+
*/
|
|
602
|
+
export function useComplexCoordinationSecondary(parameters, coordinationScopes, coordinationScopesBy, primaryType, secondaryType) {
|
|
603
|
+
const coordinationScopesFake = useMemo(() => {
|
|
604
|
+
if (coordinationScopesBy?.[primaryType]?.[secondaryType]) {
|
|
605
|
+
return {
|
|
606
|
+
...coordinationScopes,
|
|
607
|
+
[secondaryType]: Object.values(coordinationScopesBy[primaryType][secondaryType]).flat(),
|
|
608
|
+
};
|
|
609
|
+
}
|
|
610
|
+
// First fallback: try coarser (_Channel -> _Layer -> view)
|
|
611
|
+
// coordination values when finer ones are null/undefined.
|
|
612
|
+
if (coordinationScopes?.[secondaryType] && Array.isArray(coordinationScopes[secondaryType])) {
|
|
613
|
+
return {
|
|
614
|
+
...coordinationScopes,
|
|
615
|
+
[secondaryType]: coordinationScopes[secondaryType],
|
|
616
|
+
};
|
|
617
|
+
}
|
|
618
|
+
// Finally, fall back to empty array.
|
|
619
|
+
return { [secondaryType]: [] };
|
|
620
|
+
}, [coordinationScopesBy, primaryType, secondaryType]);
|
|
621
|
+
const [flatValues, flatSetters] = useComplexCoordination(parameters, coordinationScopesFake, coordinationScopesBy, secondaryType);
|
|
622
|
+
const nestedValues = useMemo(() => {
|
|
623
|
+
// Re-nest
|
|
624
|
+
const result = {};
|
|
625
|
+
if (coordinationScopesBy?.[primaryType]?.[secondaryType]) {
|
|
626
|
+
Object.entries(coordinationScopesBy[primaryType][secondaryType])
|
|
627
|
+
.forEach(([layerScope, channelScopes]) => {
|
|
628
|
+
result[layerScope] = {};
|
|
629
|
+
channelScopes.forEach((channelScope) => {
|
|
630
|
+
result[layerScope][channelScope] = flatValues[channelScope];
|
|
307
631
|
});
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
632
|
+
});
|
|
633
|
+
}
|
|
634
|
+
else if (coordinationScopes?.[secondaryType] && Array.isArray(coordinationScopes[secondaryType])) {
|
|
635
|
+
// Re-nesting for fallback case.
|
|
636
|
+
const layerScopes = coordinationScopes[primaryType];
|
|
637
|
+
layerScopes.forEach((layerScope) => {
|
|
638
|
+
// eslint-disable-next-line prefer-destructuring
|
|
639
|
+
result[layerScope] = flatValues;
|
|
640
|
+
});
|
|
641
|
+
}
|
|
642
|
+
return result;
|
|
643
|
+
}, [flatValues]);
|
|
644
|
+
const nestedSetters = useMemo(() => {
|
|
645
|
+
// Re-nest
|
|
646
|
+
const result = {};
|
|
647
|
+
if (coordinationScopesBy?.[primaryType]?.[secondaryType]) {
|
|
648
|
+
Object.entries(coordinationScopesBy[primaryType][secondaryType])
|
|
649
|
+
.forEach(([layerScope, channelScopes]) => {
|
|
650
|
+
result[layerScope] = {};
|
|
651
|
+
channelScopes.forEach((channelScope) => {
|
|
652
|
+
result[layerScope][channelScope] = flatSetters[channelScope];
|
|
314
653
|
});
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
654
|
+
});
|
|
655
|
+
}
|
|
656
|
+
else if (coordinationScopes?.[secondaryType] && Array.isArray(coordinationScopes[secondaryType])) {
|
|
657
|
+
// Re-nesting for fallback case.
|
|
658
|
+
const layerScopes = coordinationScopes[primaryType];
|
|
659
|
+
layerScopes.forEach((layerScope) => {
|
|
660
|
+
// eslint-disable-next-line prefer-destructuring
|
|
661
|
+
result[layerScope] = flatSetters;
|
|
662
|
+
});
|
|
663
|
+
}
|
|
664
|
+
return result;
|
|
665
|
+
}, [flatSetters]);
|
|
666
|
+
return [nestedValues, nestedSetters];
|
|
325
667
|
}
|
|
326
668
|
/**
|
|
327
669
|
* Use coordination values and coordination setter functions corresponding to
|
|
@@ -458,41 +800,6 @@ export function getMatchingLoader(loaders, dataset, dataType, viewCoordinationVa
|
|
|
458
800
|
export function useMatchingLoader(loaders, dataset, dataType, viewCoordinationValues) {
|
|
459
801
|
return useMemo(() => getMatchingLoader(loaders, dataset, dataType, viewCoordinationValues), [loaders, dataset, dataType, viewCoordinationValues]);
|
|
460
802
|
}
|
|
461
|
-
/**
|
|
462
|
-
* Find a specific loader instance for a particular dataset, data type, and view
|
|
463
|
-
* coordination values (mapping from coordination types to coordination values).
|
|
464
|
-
* Uses lodash/isMatch to perform matching against the file definition's
|
|
465
|
-
* coordination value mapping.
|
|
466
|
-
* TODO: can this function be removed?
|
|
467
|
-
* @param {object} loaders The value returned by useLoaders.
|
|
468
|
-
* @param {string} dataset The dataset UID.
|
|
469
|
-
* @param {string} dataType The data type for the matching file.
|
|
470
|
-
* @param {object} viewCoordinationValues Current coordination values
|
|
471
|
-
* from the view. Match these against a subset of file definition coordination
|
|
472
|
-
* values.
|
|
473
|
-
* @returns The matching loader instance or `null`.
|
|
474
|
-
*/
|
|
475
|
-
export function useMatchingLoaders(loaders, dataset, dataType, viewCoordinationValuesObj) {
|
|
476
|
-
return useMemo(() => {
|
|
477
|
-
if (!loaders[dataset]) {
|
|
478
|
-
return null;
|
|
479
|
-
}
|
|
480
|
-
const loaderInternMap = loaders[dataset].loaders[dataType];
|
|
481
|
-
if (!loaderInternMap) {
|
|
482
|
-
return null;
|
|
483
|
-
}
|
|
484
|
-
const loaderKeys = Array.from(loaderInternMap.keys());
|
|
485
|
-
function getLoader(viewCoordinationValues) {
|
|
486
|
-
const matchingKey = loaderKeys
|
|
487
|
-
.find(fileCoordinationValues => isMatch(fileCoordinationValues, viewCoordinationValues));
|
|
488
|
-
return loaderInternMap.get(matchingKey);
|
|
489
|
-
}
|
|
490
|
-
return fromEntries(Object.entries(viewCoordinationValuesObj).map(([key, viewCoordinationValues]) => ([
|
|
491
|
-
key,
|
|
492
|
-
getLoader(viewCoordinationValues),
|
|
493
|
-
])));
|
|
494
|
-
}, [loaders, dataset, dataType, viewCoordinationValuesObj]);
|
|
495
|
-
}
|
|
496
803
|
/**
|
|
497
804
|
* Obtain the view config layout array from
|
|
498
805
|
* the global app state.
|
|
@@ -511,6 +818,24 @@ export function useLayout() {
|
|
|
511
818
|
export function useRemoveComponent() {
|
|
512
819
|
return useViewConfigStore(state => state.removeComponent);
|
|
513
820
|
}
|
|
821
|
+
/**
|
|
822
|
+
* Obtain the component removal function from
|
|
823
|
+
* the global app state.
|
|
824
|
+
* @returns {function} The remove component function
|
|
825
|
+
* in the `useViewInfoStore` store.
|
|
826
|
+
*/
|
|
827
|
+
export function useRemoveImageChannelInMetaCoordinationScopes() {
|
|
828
|
+
return useViewConfigStore(state => state.removeImageChannelInMetaCoordinationScopes);
|
|
829
|
+
}
|
|
830
|
+
/**
|
|
831
|
+
* Obtain the component removal function from
|
|
832
|
+
* the global app state.
|
|
833
|
+
* @returns {function} The remove component function
|
|
834
|
+
* in the `useViewInfoStore` store.
|
|
835
|
+
*/
|
|
836
|
+
export function useAddImageChannelInMetaCoordinationScopes() {
|
|
837
|
+
return useViewConfigStore(state => state.addImageChannelInMetaCoordinationScopes);
|
|
838
|
+
}
|
|
514
839
|
/**
|
|
515
840
|
* Obtain the component prop setter function from
|
|
516
841
|
* the global app state.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hooks.test.d.ts","sourceRoot":"","sources":["../../src/state/hooks.test.js"],"names":[],"mappings":""}
|