@vitessce/vit-s 3.1.3 → 3.2.1
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 +1801 -132
- 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/shared-plot-options/CellColorEncodingOption.d.ts.map +1 -1
- package/dist-tsc/shared-plot-options/CellColorEncodingOption.js +2 -1
- 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 +6 -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/shared-plot-options/CellColorEncodingOption.js +2 -1
- 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
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import {
|
|
3
|
+
initializeNestedObject,
|
|
4
|
+
nestFeatureSelectionQueryResults,
|
|
5
|
+
getFeatureSelectionQueryKeyScopeTuples,
|
|
6
|
+
nestQueryResults,
|
|
7
|
+
getQueryKeyScopeTuples,
|
|
8
|
+
} from './data-hooks-multilevel-utils.js';
|
|
9
|
+
|
|
10
|
+
describe('recursive data hook utilities for nesting and un-nesting multi-level queries', () => {
|
|
11
|
+
describe('initializeNestedObject', () => {
|
|
12
|
+
it('should initialize an empty nested object', () => {
|
|
13
|
+
const nestedObject = {};
|
|
14
|
+
initializeNestedObject(['a', 'b', 'c'], nestedObject, () => true);
|
|
15
|
+
expect(nestedObject).toEqual({ a: { b: { c: true } } });
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('should initialize a non-empty nested object at the same path', () => {
|
|
19
|
+
const nestedObject = { a: { b: { c: [1, 2, 3] } } };
|
|
20
|
+
initializeNestedObject(['a', 'b', 'c'], nestedObject, () => []);
|
|
21
|
+
expect(nestedObject).toEqual({ a: { b: { c: [1, 2, 3] } } });
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('should initialize a non-empty nested object at a different path', () => {
|
|
25
|
+
const nestedObject = { a: { b: { c: [1, 2, 3] } } };
|
|
26
|
+
initializeNestedObject(['a', 'b', 'd'], nestedObject, () => []);
|
|
27
|
+
expect(nestedObject).toEqual({ a: { b: { c: [1, 2, 3], d: [] } } });
|
|
28
|
+
});
|
|
29
|
+
it('should initialize two paths', () => {
|
|
30
|
+
const nestedObject = {};
|
|
31
|
+
initializeNestedObject(['a', 'b', 'c'], nestedObject, () => true);
|
|
32
|
+
initializeNestedObject(['d', 'e', 'f'], nestedObject, () => true);
|
|
33
|
+
expect(nestedObject).toEqual({
|
|
34
|
+
a: { b: { c: true } },
|
|
35
|
+
d: { e: { f: true } },
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
describe('nestFeatureSelectionQueryResults', () => {
|
|
40
|
+
it('should nest flat query results', () => {
|
|
41
|
+
const queryKeyScopeTuples = [
|
|
42
|
+
[['someQueryKey', 'abc'], { levelScopes: ['a', 'b', 'c'], featureIndex: 0, numFeatures: 3 }],
|
|
43
|
+
[['someQueryKey', 'abc'], { levelScopes: ['a', 'b', 'c'], featureIndex: 1, numFeatures: 3 }],
|
|
44
|
+
[['someQueryKey', 'abd'], { levelScopes: ['a', 'b', 'd'], featureIndex: 0, numFeatures: 2 }],
|
|
45
|
+
];
|
|
46
|
+
const flatQueryResults = [
|
|
47
|
+
'abc0',
|
|
48
|
+
'abc1',
|
|
49
|
+
'abd0',
|
|
50
|
+
];
|
|
51
|
+
const nestedData = nestFeatureSelectionQueryResults(queryKeyScopeTuples, flatQueryResults);
|
|
52
|
+
expect(nestedData).toEqual({
|
|
53
|
+
a: { b: { c: ['abc0', 'abc1', undefined], d: ['abd0', undefined] } },
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
describe('getFeatureSelectionQueryKeyScopeTuples', () => {
|
|
58
|
+
it('should convert nested selections and matchOn objects to array of tuples', () => {
|
|
59
|
+
const selections = { a: { b: { c: ['geneA', 'geneB', 'geneC'] } } };
|
|
60
|
+
const matchOn = { a: { b: { c: { obsType: 'cell', featureType: 'gene' } } } };
|
|
61
|
+
const queryKeyScopeTuples = getFeatureSelectionQueryKeyScopeTuples(selections, matchOn, 3, 'someDataset', 'someDataType', true);
|
|
62
|
+
expect(queryKeyScopeTuples).toEqual([
|
|
63
|
+
[
|
|
64
|
+
['someDataset', 'someDataType', { obsType: 'cell', featureType: 'gene' }, 'geneA', true, 'useFeatureSelectionMultiLevel'],
|
|
65
|
+
// scope info (for rolling up later)
|
|
66
|
+
{ levelScopes: ['a', 'b', 'c'], featureIndex: 0, numFeatures: 3 },
|
|
67
|
+
],
|
|
68
|
+
[
|
|
69
|
+
['someDataset', 'someDataType', { obsType: 'cell', featureType: 'gene' }, 'geneB', true, 'useFeatureSelectionMultiLevel'],
|
|
70
|
+
// scope info (for rolling up later)
|
|
71
|
+
{ levelScopes: ['a', 'b', 'c'], featureIndex: 1, numFeatures: 3 },
|
|
72
|
+
],
|
|
73
|
+
[
|
|
74
|
+
['someDataset', 'someDataType', { obsType: 'cell', featureType: 'gene' }, 'geneC', true, 'useFeatureSelectionMultiLevel'],
|
|
75
|
+
// scope info (for rolling up later)
|
|
76
|
+
{ levelScopes: ['a', 'b', 'c'], featureIndex: 2, numFeatures: 3 },
|
|
77
|
+
],
|
|
78
|
+
]);
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
describe('nestQueryResults', () => {
|
|
82
|
+
it('should nest flat query results', () => {
|
|
83
|
+
const queryKeyScopeTuples = [
|
|
84
|
+
[['someQueryKey', 'abc'], { levelScopes: ['a', 'b', 'c'] }],
|
|
85
|
+
[['someQueryKey', 'abd'], { levelScopes: ['a', 'b', 'd'] }],
|
|
86
|
+
];
|
|
87
|
+
const flatQueryResults = [
|
|
88
|
+
{ someKey: 'abc0' },
|
|
89
|
+
{ someKey: 'abd0' },
|
|
90
|
+
];
|
|
91
|
+
const nestedData = nestQueryResults(queryKeyScopeTuples, flatQueryResults);
|
|
92
|
+
expect(nestedData).toEqual({
|
|
93
|
+
a: { b: { c: { someKey: 'abc0' }, d: { someKey: 'abd0' } } },
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
describe('getQueryKeyScopeTuples', () => {
|
|
98
|
+
it('should convert nested selections and matchOn objects to array of tuples', () => {
|
|
99
|
+
const matchOn = { a: { b: { c: { obsType: 'cell', featureType: 'gene' } } } };
|
|
100
|
+
const queryKeyScopeTuples = getQueryKeyScopeTuples(matchOn, 3, 'someDataset', 'someDataType', true);
|
|
101
|
+
expect(queryKeyScopeTuples).toEqual([
|
|
102
|
+
[
|
|
103
|
+
['someDataset', 'someDataType', { obsType: 'cell', featureType: 'gene' }, true, 'useDataType'],
|
|
104
|
+
// scope info (for rolling up later)
|
|
105
|
+
{ levelScopes: ['a', 'b', 'c'] },
|
|
106
|
+
],
|
|
107
|
+
]);
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
});
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
import { useMemo } from 'react';
|
|
2
|
+
import { CoordinationType } from '@vitessce/constants-internal';
|
|
3
|
+
import { fromEntries } from '@vitessce/utils';
|
|
4
|
+
import {
|
|
5
|
+
useComplexCoordination,
|
|
6
|
+
useComplexCoordinationSecondary,
|
|
7
|
+
} from './state/hooks.js';
|
|
8
|
+
import {
|
|
9
|
+
useFeatureSelectionMultiLevel,
|
|
10
|
+
useObsFeatureMatrixIndicesMultiLevel,
|
|
11
|
+
useObsLocationsMultiLevel,
|
|
12
|
+
useObsSetsMultiLevel,
|
|
13
|
+
useObsLabelsMultiLevel,
|
|
14
|
+
} from './data-hooks-multilevel-utils.js';
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
export function useSegmentationMultiFeatureSelection(
|
|
18
|
+
coordinationScopes, coordinationScopesBy, loaders, dataset,
|
|
19
|
+
) {
|
|
20
|
+
const obsFeatureMatrixCoordination = useComplexCoordinationSecondary(
|
|
21
|
+
[
|
|
22
|
+
CoordinationType.OBS_TYPE,
|
|
23
|
+
CoordinationType.FEATURE_TYPE,
|
|
24
|
+
CoordinationType.FEATURE_VALUE_TYPE,
|
|
25
|
+
],
|
|
26
|
+
coordinationScopes,
|
|
27
|
+
coordinationScopesBy,
|
|
28
|
+
CoordinationType.SEGMENTATION_LAYER,
|
|
29
|
+
CoordinationType.SEGMENTATION_CHANNEL,
|
|
30
|
+
);
|
|
31
|
+
const featureSelectionCoordination = useComplexCoordinationSecondary(
|
|
32
|
+
[
|
|
33
|
+
CoordinationType.FEATURE_SELECTION,
|
|
34
|
+
],
|
|
35
|
+
coordinationScopes,
|
|
36
|
+
coordinationScopesBy,
|
|
37
|
+
CoordinationType.SEGMENTATION_LAYER,
|
|
38
|
+
CoordinationType.SEGMENTATION_CHANNEL,
|
|
39
|
+
);
|
|
40
|
+
const matchOnObj = useMemo(() => obsFeatureMatrixCoordination[0],
|
|
41
|
+
// imageCoordination reference changes each render,
|
|
42
|
+
// use coordinationScopes and coordinationScopesBy which are
|
|
43
|
+
// indirect dependencies here.
|
|
44
|
+
[coordinationScopes, coordinationScopesBy]);
|
|
45
|
+
const selections = useMemo(() => fromEntries(Object.entries(featureSelectionCoordination[0])
|
|
46
|
+
.map(([layerScope, layerVal]) => ([
|
|
47
|
+
layerScope,
|
|
48
|
+
fromEntries(
|
|
49
|
+
Object.entries(layerVal)
|
|
50
|
+
.map(([cScope, cVal]) => ([cScope, cVal.featureSelection])),
|
|
51
|
+
),
|
|
52
|
+
]))),
|
|
53
|
+
// Need to execute this more frequently, whenever the featureSelections update.
|
|
54
|
+
[coordinationScopes, coordinationScopesBy,
|
|
55
|
+
...Object.values(featureSelectionCoordination[0] || {})
|
|
56
|
+
.flatMap(layerVal => Object.values(layerVal).map(cVal => cVal.featureSelection)),
|
|
57
|
+
]);
|
|
58
|
+
const [
|
|
59
|
+
featureData, loadedSelections, extents, normData, featureStatus,
|
|
60
|
+
] = useFeatureSelectionMultiLevel(
|
|
61
|
+
loaders, dataset, false, matchOnObj, selections, 2,
|
|
62
|
+
);
|
|
63
|
+
return [featureData, loadedSelections, extents, normData, featureStatus];
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function useSpotMultiFeatureSelection(
|
|
67
|
+
coordinationScopes, coordinationScopesBy, loaders, dataset,
|
|
68
|
+
) {
|
|
69
|
+
const obsFeatureMatrixCoordination = useComplexCoordination(
|
|
70
|
+
[
|
|
71
|
+
CoordinationType.OBS_TYPE,
|
|
72
|
+
CoordinationType.FEATURE_TYPE,
|
|
73
|
+
CoordinationType.FEATURE_VALUE_TYPE,
|
|
74
|
+
],
|
|
75
|
+
coordinationScopes,
|
|
76
|
+
coordinationScopesBy,
|
|
77
|
+
CoordinationType.SPOT_LAYER,
|
|
78
|
+
);
|
|
79
|
+
const featureSelectionCoordination = useComplexCoordination(
|
|
80
|
+
[
|
|
81
|
+
CoordinationType.FEATURE_SELECTION,
|
|
82
|
+
],
|
|
83
|
+
coordinationScopes,
|
|
84
|
+
coordinationScopesBy,
|
|
85
|
+
CoordinationType.SPOT_LAYER,
|
|
86
|
+
);
|
|
87
|
+
const matchOnObj = useMemo(() => obsFeatureMatrixCoordination[0],
|
|
88
|
+
// imageCoordination reference changes each render,
|
|
89
|
+
// use coordinationScopes and coordinationScopesBy which are
|
|
90
|
+
// indirect dependencies here.
|
|
91
|
+
[coordinationScopes, coordinationScopesBy]);
|
|
92
|
+
const selections = useMemo(() => fromEntries(Object.entries(featureSelectionCoordination[0])
|
|
93
|
+
.map(([layerScope, layerVal]) => ([
|
|
94
|
+
layerScope,
|
|
95
|
+
layerVal.featureSelection,
|
|
96
|
+
]))),
|
|
97
|
+
// Need to execute this more frequently, whenever the featureSelections update.
|
|
98
|
+
[coordinationScopes, coordinationScopesBy,
|
|
99
|
+
...Object.values(featureSelectionCoordination[0] || {})
|
|
100
|
+
.flatMap(layerVal => layerVal.featureSelection),
|
|
101
|
+
]);
|
|
102
|
+
const [
|
|
103
|
+
featureData, loadedSelections, extents, normData, featureStatus,
|
|
104
|
+
] = useFeatureSelectionMultiLevel(
|
|
105
|
+
loaders, dataset, false, matchOnObj, selections, 1,
|
|
106
|
+
);
|
|
107
|
+
return [featureData, loadedSelections, extents, normData, featureStatus];
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export function useSegmentationMultiObsFeatureMatrixIndices(
|
|
111
|
+
coordinationScopes, coordinationScopesBy, loaders, dataset,
|
|
112
|
+
) {
|
|
113
|
+
const obsFeatureMatrixCoordination = useComplexCoordinationSecondary(
|
|
114
|
+
[
|
|
115
|
+
CoordinationType.OBS_TYPE,
|
|
116
|
+
CoordinationType.FEATURE_TYPE,
|
|
117
|
+
CoordinationType.FEATURE_VALUE_TYPE,
|
|
118
|
+
],
|
|
119
|
+
coordinationScopes,
|
|
120
|
+
coordinationScopesBy,
|
|
121
|
+
CoordinationType.SEGMENTATION_LAYER,
|
|
122
|
+
CoordinationType.SEGMENTATION_CHANNEL,
|
|
123
|
+
);
|
|
124
|
+
const matchOnObj = useMemo(() => obsFeatureMatrixCoordination[0],
|
|
125
|
+
// imageCoordination reference changes each render,
|
|
126
|
+
// use coordinationScopes and coordinationScopesBy which are
|
|
127
|
+
// indirect dependencies here.
|
|
128
|
+
[coordinationScopes, coordinationScopesBy]);
|
|
129
|
+
const [indicesData, indicesDataStatus] = useObsFeatureMatrixIndicesMultiLevel(
|
|
130
|
+
loaders, dataset, false, matchOnObj, 2,
|
|
131
|
+
);
|
|
132
|
+
return [indicesData, indicesDataStatus];
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export function useSpotMultiObsFeatureMatrixIndices(
|
|
136
|
+
coordinationScopes, coordinationScopesBy, loaders, dataset,
|
|
137
|
+
) {
|
|
138
|
+
const obsFeatureMatrixCoordination = useComplexCoordination(
|
|
139
|
+
[
|
|
140
|
+
CoordinationType.OBS_TYPE,
|
|
141
|
+
CoordinationType.FEATURE_TYPE,
|
|
142
|
+
CoordinationType.FEATURE_VALUE_TYPE,
|
|
143
|
+
],
|
|
144
|
+
coordinationScopes,
|
|
145
|
+
coordinationScopesBy,
|
|
146
|
+
CoordinationType.SPOT_LAYER,
|
|
147
|
+
);
|
|
148
|
+
const matchOnObj = useMemo(() => obsFeatureMatrixCoordination[0],
|
|
149
|
+
// imageCoordination reference changes each render,
|
|
150
|
+
// use coordinationScopes and coordinationScopesBy which are
|
|
151
|
+
// indirect dependencies here.
|
|
152
|
+
[coordinationScopes, coordinationScopesBy]);
|
|
153
|
+
const [indicesData, indicesDataStatus] = useObsFeatureMatrixIndicesMultiLevel(
|
|
154
|
+
loaders, dataset, false, matchOnObj, 1,
|
|
155
|
+
);
|
|
156
|
+
return [indicesData, indicesDataStatus];
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export function usePointMultiObsLabels(
|
|
160
|
+
coordinationScopes, coordinationScopesBy, loaders, dataset,
|
|
161
|
+
) {
|
|
162
|
+
const obsLabelsCoordination = useComplexCoordination(
|
|
163
|
+
[
|
|
164
|
+
CoordinationType.OBS_TYPE,
|
|
165
|
+
CoordinationType.OBS_LABELS_TYPE,
|
|
166
|
+
],
|
|
167
|
+
coordinationScopes,
|
|
168
|
+
coordinationScopesBy,
|
|
169
|
+
CoordinationType.POINT_LAYER,
|
|
170
|
+
);
|
|
171
|
+
const matchOnObj = useMemo(() => obsLabelsCoordination[0],
|
|
172
|
+
// imageCoordination reference changes each render,
|
|
173
|
+
// use coordinationScopes and coordinationScopesBy which are
|
|
174
|
+
// indirect dependencies here.
|
|
175
|
+
[coordinationScopes, coordinationScopesBy]);
|
|
176
|
+
const [indicesData, indicesDataStatus] = useObsLabelsMultiLevel(
|
|
177
|
+
loaders, dataset, false, matchOnObj, 1,
|
|
178
|
+
);
|
|
179
|
+
return [indicesData, indicesDataStatus];
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
export function useSegmentationMultiObsLocations(
|
|
183
|
+
coordinationScopes, coordinationScopesBy, loaders, dataset,
|
|
184
|
+
) {
|
|
185
|
+
const obsTypeCoordination = useComplexCoordinationSecondary(
|
|
186
|
+
[
|
|
187
|
+
CoordinationType.OBS_TYPE,
|
|
188
|
+
],
|
|
189
|
+
coordinationScopes,
|
|
190
|
+
coordinationScopesBy,
|
|
191
|
+
CoordinationType.SEGMENTATION_LAYER,
|
|
192
|
+
CoordinationType.SEGMENTATION_CHANNEL,
|
|
193
|
+
);
|
|
194
|
+
const matchOnObj = useMemo(() => obsTypeCoordination[0],
|
|
195
|
+
// imageCoordination reference changes each render,
|
|
196
|
+
// use coordinationScopes and coordinationScopesBy which are
|
|
197
|
+
// indirect dependencies here.
|
|
198
|
+
[coordinationScopes, coordinationScopesBy]);
|
|
199
|
+
const [indicesData, indicesDataStatus] = useObsLocationsMultiLevel(
|
|
200
|
+
loaders, dataset, false, matchOnObj, 2,
|
|
201
|
+
);
|
|
202
|
+
return [indicesData, indicesDataStatus];
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
export function useSegmentationMultiObsSets(
|
|
206
|
+
coordinationScopes, coordinationScopesBy, loaders, dataset,
|
|
207
|
+
) {
|
|
208
|
+
const obsTypeCoordination = useComplexCoordinationSecondary(
|
|
209
|
+
[
|
|
210
|
+
CoordinationType.OBS_TYPE,
|
|
211
|
+
],
|
|
212
|
+
coordinationScopes,
|
|
213
|
+
coordinationScopesBy,
|
|
214
|
+
CoordinationType.SEGMENTATION_LAYER,
|
|
215
|
+
CoordinationType.SEGMENTATION_CHANNEL,
|
|
216
|
+
);
|
|
217
|
+
const matchOnObj = useMemo(() => obsTypeCoordination[0],
|
|
218
|
+
// imageCoordination reference changes each render,
|
|
219
|
+
// use coordinationScopes and coordinationScopesBy which are
|
|
220
|
+
// indirect dependencies here.
|
|
221
|
+
[coordinationScopes, coordinationScopesBy]);
|
|
222
|
+
const [indicesData, indicesDataStatus] = useObsSetsMultiLevel(
|
|
223
|
+
loaders, dataset, false, matchOnObj, 2,
|
|
224
|
+
);
|
|
225
|
+
return [indicesData, indicesDataStatus];
|
|
226
|
+
}
|
package/src/data-hooks.js
CHANGED
|
@@ -3,9 +3,10 @@ import { CoordinationType, DataType, STATUS } from '@vitessce/constants-internal
|
|
|
3
3
|
import { fromEntries } from '@vitessce/utils';
|
|
4
4
|
import { useQuery, useQueries } from '@tanstack/react-query';
|
|
5
5
|
import {
|
|
6
|
-
getMatchingLoader,
|
|
7
6
|
useMultiCoordinationValues,
|
|
7
|
+
useComplexCoordination,
|
|
8
8
|
useSetWarning,
|
|
9
|
+
getMatchingLoader,
|
|
9
10
|
} from './state/hooks.js';
|
|
10
11
|
import {
|
|
11
12
|
LoaderNotFoundError,
|
|
@@ -76,6 +77,28 @@ export function useObsEmbeddingData(
|
|
|
76
77
|
);
|
|
77
78
|
}
|
|
78
79
|
|
|
80
|
+
export function useObsSpotsData(
|
|
81
|
+
loaders, dataset, isRequired,
|
|
82
|
+
coordinationSetters, initialCoordinationValues, matchOn,
|
|
83
|
+
) {
|
|
84
|
+
return useDataType(
|
|
85
|
+
DataType.OBS_SPOTS,
|
|
86
|
+
loaders, dataset, isRequired,
|
|
87
|
+
coordinationSetters, initialCoordinationValues, matchOn,
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export function useObsPointsData(
|
|
92
|
+
loaders, dataset, isRequired,
|
|
93
|
+
coordinationSetters, initialCoordinationValues, matchOn,
|
|
94
|
+
) {
|
|
95
|
+
return useDataType(
|
|
96
|
+
DataType.OBS_POINTS,
|
|
97
|
+
loaders, dataset, isRequired,
|
|
98
|
+
coordinationSetters, initialCoordinationValues, matchOn,
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
|
|
79
102
|
export function useObsLocationsData(
|
|
80
103
|
loaders, dataset, isRequired,
|
|
81
104
|
coordinationSetters, initialCoordinationValues, matchOn,
|
|
@@ -214,7 +237,7 @@ export function useFeatureSelection(
|
|
|
214
237
|
const payload = await loader.loadGeneSelection({ selection: [featureId] });
|
|
215
238
|
if (!payload) return null;
|
|
216
239
|
const { data } = payload;
|
|
217
|
-
return { data: data[0], dataKey:
|
|
240
|
+
return { data: data[0], dataKey: featureId };
|
|
218
241
|
}
|
|
219
242
|
// Loader does not implement loadGeneSelection.
|
|
220
243
|
const payload = await loader.load();
|
|
@@ -288,7 +311,9 @@ export function useObsFeatureMatrixIndices(
|
|
|
288
311
|
placeholderData: placeholderObject,
|
|
289
312
|
// Include the hook name in the queryKey to prevent the case in which an identical queryKey
|
|
290
313
|
// in a different hook would cause an accidental cache hit.
|
|
291
|
-
|
|
314
|
+
// Note: this uses the same key structure/suffix as
|
|
315
|
+
// getMatrixIndicesQueryKeyScopeTuplesAux for shared caching.
|
|
316
|
+
queryKey: [dataset, DataType.OBS_FEATURE_MATRIX, matchOn, isRequired, 'useObsFeatureMatrixIndices'],
|
|
292
317
|
// Query function should return an object
|
|
293
318
|
// { data, dataKey } where dataKey is the loaded gene selection.
|
|
294
319
|
// TODO: use TypeScript to type the return value?
|
|
@@ -350,6 +375,78 @@ export function useObsFeatureMatrixIndices(
|
|
|
350
375
|
return [loadedData, dataStatus, urls];
|
|
351
376
|
}
|
|
352
377
|
|
|
378
|
+
export function useMultiObsPoints(
|
|
379
|
+
coordinationScopes, coordinationScopesBy, loaders, dataset,
|
|
380
|
+
) {
|
|
381
|
+
const obsTypeCoordination = useComplexCoordination(
|
|
382
|
+
[
|
|
383
|
+
CoordinationType.OBS_TYPE,
|
|
384
|
+
],
|
|
385
|
+
coordinationScopes,
|
|
386
|
+
coordinationScopesBy,
|
|
387
|
+
CoordinationType.POINT_LAYER,
|
|
388
|
+
);
|
|
389
|
+
const matchOnObj = useMemo(() => obsTypeCoordination[0],
|
|
390
|
+
// imageCoordination reference changes each render,
|
|
391
|
+
// use coordinationScopes and coordinationScopesBy which are
|
|
392
|
+
// indirect dependencies here.
|
|
393
|
+
[coordinationScopes, coordinationScopesBy]);
|
|
394
|
+
const [obsPointsData, obsPointsDataStatus, obsPointsUrls] = useDataTypeMulti(
|
|
395
|
+
DataType.OBS_POINTS, loaders, dataset,
|
|
396
|
+
false, {}, {},
|
|
397
|
+
matchOnObj,
|
|
398
|
+
);
|
|
399
|
+
return [obsPointsData, obsPointsDataStatus, obsPointsUrls];
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
export function useMultiObsSpots(
|
|
403
|
+
coordinationScopes, coordinationScopesBy, loaders, dataset,
|
|
404
|
+
) {
|
|
405
|
+
const obsTypeCoordination = useComplexCoordination(
|
|
406
|
+
[
|
|
407
|
+
CoordinationType.OBS_TYPE,
|
|
408
|
+
],
|
|
409
|
+
coordinationScopes,
|
|
410
|
+
coordinationScopesBy,
|
|
411
|
+
CoordinationType.SPOT_LAYER,
|
|
412
|
+
);
|
|
413
|
+
const matchOnObj = useMemo(() => obsTypeCoordination[0],
|
|
414
|
+
// imageCoordination reference changes each render,
|
|
415
|
+
// use coordinationScopes and coordinationScopesBy which are
|
|
416
|
+
// indirect dependencies here.
|
|
417
|
+
[coordinationScopes, coordinationScopesBy]);
|
|
418
|
+
const [obsSpotsData, obsSpotsDataStatus, obsSpotsUrls] = useDataTypeMulti(
|
|
419
|
+
DataType.OBS_SPOTS, loaders, dataset,
|
|
420
|
+
false, {}, {},
|
|
421
|
+
matchOnObj,
|
|
422
|
+
);
|
|
423
|
+
return [obsSpotsData, obsSpotsDataStatus, obsSpotsUrls];
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
export function useSpotMultiObsSets(
|
|
427
|
+
coordinationScopes, coordinationScopesBy, loaders, dataset,
|
|
428
|
+
) {
|
|
429
|
+
const obsTypeCoordination = useComplexCoordination(
|
|
430
|
+
[
|
|
431
|
+
CoordinationType.OBS_TYPE,
|
|
432
|
+
],
|
|
433
|
+
coordinationScopes,
|
|
434
|
+
coordinationScopesBy,
|
|
435
|
+
CoordinationType.SPOT_LAYER,
|
|
436
|
+
);
|
|
437
|
+
const matchOnObj = useMemo(() => obsTypeCoordination[0],
|
|
438
|
+
// imageCoordination reference changes each render,
|
|
439
|
+
// use coordinationScopes and coordinationScopesBy which are
|
|
440
|
+
// indirect dependencies here.
|
|
441
|
+
[coordinationScopes, coordinationScopesBy]);
|
|
442
|
+
const [obsSetsData, obsSetsDataStatus, obsSetsUrls] = useDataTypeMulti(
|
|
443
|
+
DataType.OBS_SETS, loaders, dataset,
|
|
444
|
+
false, {}, {},
|
|
445
|
+
matchOnObj,
|
|
446
|
+
);
|
|
447
|
+
return [obsSetsData, obsSetsDataStatus, obsSetsUrls];
|
|
448
|
+
}
|
|
449
|
+
|
|
353
450
|
export function useMultiObsLabels(
|
|
354
451
|
coordinationScopes, obsType, loaders, dataset,
|
|
355
452
|
) {
|
|
@@ -363,11 +460,64 @@ export function useMultiObsLabels(
|
|
|
363
460
|
{ obsLabelsType, obsType },
|
|
364
461
|
])),
|
|
365
462
|
), [obsLabelsTypes, obsType]);
|
|
366
|
-
const [obsLabelsData, obsLabelsDataStatus] = useDataTypeMulti(
|
|
463
|
+
const [obsLabelsData, obsLabelsDataStatus, obsLabelsUrls] = useDataTypeMulti(
|
|
367
464
|
DataType.OBS_LABELS, loaders, dataset,
|
|
368
465
|
false, {}, {},
|
|
369
466
|
obsLabelsMatchOnObj,
|
|
370
467
|
);
|
|
371
|
-
|
|
372
|
-
|
|
468
|
+
return [obsLabelsTypes, obsLabelsData, obsLabelsDataStatus, obsLabelsUrls];
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
export function useMultiObsSegmentations(
|
|
472
|
+
coordinationScopes, coordinationScopesBy, loaders, dataset,
|
|
473
|
+
) {
|
|
474
|
+
const imageCoordination = useComplexCoordination(
|
|
475
|
+
[
|
|
476
|
+
CoordinationType.FILE_UID,
|
|
477
|
+
],
|
|
478
|
+
coordinationScopes,
|
|
479
|
+
coordinationScopesBy,
|
|
480
|
+
CoordinationType.SEGMENTATION_LAYER,
|
|
481
|
+
);
|
|
482
|
+
const matchOnObj = useMemo(() => imageCoordination[0],
|
|
483
|
+
// imageCoordination reference changes each render,
|
|
484
|
+
// use coordinationScopes and coordinationScopesBy which are
|
|
485
|
+
// indirect dependencies here.
|
|
486
|
+
[coordinationScopes, coordinationScopesBy]);
|
|
487
|
+
const [
|
|
488
|
+
obsSegmentationsData,
|
|
489
|
+
obsSegmentationsDataStatus,
|
|
490
|
+
obsSegmentationsUrls,
|
|
491
|
+
] = useDataTypeMulti(
|
|
492
|
+
DataType.OBS_SEGMENTATIONS, loaders, dataset,
|
|
493
|
+
false, {}, {},
|
|
494
|
+
matchOnObj,
|
|
495
|
+
);
|
|
496
|
+
return [obsSegmentationsData, obsSegmentationsDataStatus, obsSegmentationsUrls];
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
export function useMultiImages(
|
|
500
|
+
coordinationScopes, coordinationScopesBy, loaders, dataset,
|
|
501
|
+
) {
|
|
502
|
+
// TODO: delegate the generation of matchOnObj to a different hoook and pass as a parameter?
|
|
503
|
+
// (in all of the useMulti data hooks)?
|
|
504
|
+
const imageCoordination = useComplexCoordination(
|
|
505
|
+
[
|
|
506
|
+
CoordinationType.FILE_UID,
|
|
507
|
+
],
|
|
508
|
+
coordinationScopes,
|
|
509
|
+
coordinationScopesBy,
|
|
510
|
+
CoordinationType.IMAGE_LAYER,
|
|
511
|
+
);
|
|
512
|
+
const matchOnObj = useMemo(() => imageCoordination[0],
|
|
513
|
+
// imageCoordination reference changes each render,
|
|
514
|
+
// use coordinationScopes and coordinationScopesBy which are
|
|
515
|
+
// indirect dependencies here.
|
|
516
|
+
[coordinationScopes, coordinationScopesBy]);
|
|
517
|
+
const [imageData, imageDataStatus, imageUrls] = useDataTypeMulti(
|
|
518
|
+
DataType.IMAGE, loaders, dataset,
|
|
519
|
+
false, {}, {},
|
|
520
|
+
matchOnObj,
|
|
521
|
+
);
|
|
522
|
+
return [imageData, imageDataStatus, imageUrls];
|
|
373
523
|
}
|
package/src/hooks.js
CHANGED
|
@@ -151,7 +151,7 @@ export function useReady(statusValues) {
|
|
|
151
151
|
export function useUrls(urls) {
|
|
152
152
|
const mergedUrls = useMemo(
|
|
153
153
|
() => urls.filter(a => Array.isArray(a)).flat().filter((url, index, array) => {
|
|
154
|
-
const firstIndex = array.findIndex(u => u.name === url.name);
|
|
154
|
+
const firstIndex = array.findIndex(u => u && url && u.name === url.name);
|
|
155
155
|
return index === firstIndex;
|
|
156
156
|
}),
|
|
157
157
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
package/src/index.js
CHANGED
|
@@ -17,9 +17,16 @@ export {
|
|
|
17
17
|
useGridItemSize,
|
|
18
18
|
} from './hooks.js';
|
|
19
19
|
export {
|
|
20
|
+
useCoordinationScopes,
|
|
21
|
+
useCoordinationScopesBy,
|
|
20
22
|
useInitialCoordination,
|
|
21
23
|
useCoordination,
|
|
22
24
|
useComplexCoordination,
|
|
25
|
+
useComplexCoordinationSecondary,
|
|
26
|
+
useMultiCoordinationScopes,
|
|
27
|
+
useMultiCoordinationScopesNonNull,
|
|
28
|
+
useMultiCoordinationScopesSecondary,
|
|
29
|
+
useMultiCoordinationScopesSecondaryNonNull,
|
|
23
30
|
useMultiCoordinationValues,
|
|
24
31
|
useMultiDatasetCoordination,
|
|
25
32
|
useDatasetUids,
|
|
@@ -35,6 +42,8 @@ export {
|
|
|
35
42
|
useSetWarning,
|
|
36
43
|
useAuxiliaryCoordination,
|
|
37
44
|
useComponentLayout,
|
|
45
|
+
useRemoveImageChannelInMetaCoordinationScopes,
|
|
46
|
+
useAddImageChannelInMetaCoordinationScopes,
|
|
38
47
|
} from './state/hooks.js';
|
|
39
48
|
export {
|
|
40
49
|
useDescription,
|
|
@@ -44,7 +53,14 @@ export {
|
|
|
44
53
|
useFeatureSelection,
|
|
45
54
|
useObsFeatureMatrixIndices,
|
|
46
55
|
useMultiObsLabels,
|
|
56
|
+
useMultiObsSpots,
|
|
57
|
+
useMultiObsPoints,
|
|
58
|
+
useSpotMultiObsSets,
|
|
59
|
+
useMultiObsSegmentations,
|
|
60
|
+
useMultiImages,
|
|
47
61
|
|
|
62
|
+
useObsSpotsData,
|
|
63
|
+
useObsPointsData,
|
|
48
64
|
useObsLocationsData,
|
|
49
65
|
useObsSegmentationsData,
|
|
50
66
|
useNeighborhoodsData,
|
|
@@ -54,6 +70,15 @@ export {
|
|
|
54
70
|
useFeatureLabelsData,
|
|
55
71
|
useGenomicProfilesData,
|
|
56
72
|
} from './data-hooks.js';
|
|
73
|
+
export {
|
|
74
|
+
usePointMultiObsLabels,
|
|
75
|
+
useSpotMultiFeatureSelection,
|
|
76
|
+
useSpotMultiObsFeatureMatrixIndices,
|
|
77
|
+
useSegmentationMultiFeatureSelection,
|
|
78
|
+
useSegmentationMultiObsFeatureMatrixIndices,
|
|
79
|
+
useSegmentationMultiObsLocations,
|
|
80
|
+
useSegmentationMultiObsSets,
|
|
81
|
+
} from './data-hooks-multilevel.js';
|
|
57
82
|
export {
|
|
58
83
|
useHasLoader,
|
|
59
84
|
} from './data-hook-utils.js';
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
ClickAwayListener,
|
|
9
9
|
Fade,
|
|
10
10
|
} from '@material-ui/core';
|
|
11
|
+
import clsx from 'clsx';
|
|
11
12
|
import { useVitessceContainer } from '../hooks.js';
|
|
12
13
|
|
|
13
14
|
const useStyles = makeStyles(() => ({
|
|
@@ -30,6 +31,8 @@ export function PopperMenu(props) {
|
|
|
30
31
|
children,
|
|
31
32
|
buttonClassName,
|
|
32
33
|
placement = 'bottom-end',
|
|
34
|
+
withPaper = true,
|
|
35
|
+
containerClassName,
|
|
33
36
|
'aria-label': ariaLabel,
|
|
34
37
|
} = props;
|
|
35
38
|
const classes = useStyles();
|
|
@@ -49,7 +52,7 @@ export function PopperMenu(props) {
|
|
|
49
52
|
const getTooltipContainer = useVitessceContainer(anchorRef);
|
|
50
53
|
|
|
51
54
|
return (
|
|
52
|
-
<div ref={anchorRef} className={classes.container}>
|
|
55
|
+
<div ref={anchorRef} className={clsx(classes.container, containerClassName)}>
|
|
53
56
|
<IconButton
|
|
54
57
|
aria-describedby={id}
|
|
55
58
|
onClick={handleClick}
|
|
@@ -72,9 +75,11 @@ export function PopperMenu(props) {
|
|
|
72
75
|
{({ TransitionProps }) => (
|
|
73
76
|
<ClickAwayListener onClickAway={handleClose}>
|
|
74
77
|
<Fade {...TransitionProps} timeout={100}>
|
|
75
|
-
|
|
76
|
-
<
|
|
77
|
-
|
|
78
|
+
{withPaper ? (
|
|
79
|
+
<Paper elevation={4} className={classes.paper}>
|
|
80
|
+
<MenuList>{children}</MenuList>
|
|
81
|
+
</Paper>
|
|
82
|
+
) : children}
|
|
78
83
|
</Fade>
|
|
79
84
|
</ClickAwayListener>
|
|
80
85
|
)}
|