@vitessce/vit-s 3.0.0 → 3.0.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.
Files changed (105) hide show
  1. package/dist/index.js +28348 -25624
  2. package/dist-tsc/VitS.d.ts +7 -2
  3. package/dist-tsc/VitS.d.ts.map +1 -1
  4. package/dist-tsc/VitS.js +43 -23
  5. package/dist-tsc/VitessceGrid.d.ts.map +1 -1
  6. package/dist-tsc/VitessceGrid.js +14 -11
  7. package/dist-tsc/VitessceGrid.test.js +5 -2
  8. package/dist-tsc/Warning.js +2 -2
  9. package/dist-tsc/data/LoaderResult.d.ts +8 -3
  10. package/dist-tsc/data/LoaderResult.d.ts.map +1 -1
  11. package/dist-tsc/data/LoaderResult.js +5 -0
  12. package/dist-tsc/data-hook-utils.d.ts +3 -7
  13. package/dist-tsc/data-hook-utils.d.ts.map +1 -1
  14. package/dist-tsc/data-hook-utils.js +97 -79
  15. package/dist-tsc/data-hooks.d.ts +12 -16
  16. package/dist-tsc/data-hooks.d.ts.map +1 -1
  17. package/dist-tsc/data-hooks.js +140 -127
  18. package/dist-tsc/errors/DataSourceFetchError.js +1 -1
  19. package/dist-tsc/hooks.d.ts +3 -7
  20. package/dist-tsc/hooks.d.ts.map +1 -1
  21. package/dist-tsc/hooks.js +9 -18
  22. package/dist-tsc/index.d.ts +2 -1
  23. package/dist-tsc/index.js +2 -1
  24. package/dist-tsc/mui-utils.d.ts +2 -0
  25. package/dist-tsc/mui-utils.d.ts.map +1 -0
  26. package/dist-tsc/mui-utils.js +44 -0
  27. package/dist-tsc/shared-plot-options/OptionSelect.js +1 -1
  28. package/dist-tsc/shared-plot-options/styles.js +1 -1
  29. package/dist-tsc/state/hooks.d.ts +27 -1
  30. package/dist-tsc/state/hooks.d.ts.map +1 -1
  31. package/dist-tsc/state/hooks.js +61 -21
  32. package/dist-tsc/view-config-utils.d.ts +1 -0
  33. package/dist-tsc/view-config-utils.d.ts.map +1 -1
  34. package/dist-tsc/view-config-utils.js +7 -1
  35. package/dist-tsc/view-config-utils.test.js +1 -0
  36. package/dist-tsc/vitessce-grid-layout/VitessceGridLayout.test.js +1 -1
  37. package/dist-tsc/vitessce-grid-layout/layout-utils.test.js +1 -0
  38. package/package.json +10 -9
  39. package/src/VitS.js +79 -43
  40. package/src/VitessceGrid.js +16 -10
  41. package/src/VitessceGrid.test.jsx +8 -2
  42. package/src/Warning.js +2 -2
  43. package/src/data/LoaderResult.js +5 -0
  44. package/src/data-hook-utils.js +116 -81
  45. package/src/data-hooks.js +148 -129
  46. package/src/errors/DataSourceFetchError.js +1 -1
  47. package/src/hooks.js +11 -21
  48. package/src/index.js +2 -0
  49. package/src/mui-utils.js +52 -0
  50. package/src/shared-plot-options/OptionSelect.js +1 -1
  51. package/src/shared-plot-options/styles.js +1 -1
  52. package/src/state/hooks.js +66 -21
  53. package/src/view-config-utils.js +8 -0
  54. package/src/view-config-utils.test.jsx +1 -0
  55. package/src/vitessce-grid-layout/VitessceGridLayout.test.jsx +1 -1
  56. package/src/vitessce-grid-layout/layout-utils.test.js +1 -0
  57. package/dist-tsc/schemas/config-0.1.0.schema.json +0 -84
  58. package/dist-tsc/schemas/config-1.0.0.schema.json +0 -733
  59. package/dist-tsc/schemas/config-1.0.1.schema.json +0 -909
  60. package/dist-tsc/schemas/config-1.0.10.schema.json +0 -969
  61. package/dist-tsc/schemas/config-1.0.11.schema.json +0 -990
  62. package/dist-tsc/schemas/config-1.0.12.schema.json +0 -997
  63. package/dist-tsc/schemas/config-1.0.13.schema.json +0 -1013
  64. package/dist-tsc/schemas/config-1.0.14.schema.json +0 -1048
  65. package/dist-tsc/schemas/config-1.0.15.schema.json +0 -1048
  66. package/dist-tsc/schemas/config-1.0.16.schema.json +0 -1089
  67. package/dist-tsc/schemas/config-1.0.2.schema.json +0 -910
  68. package/dist-tsc/schemas/config-1.0.3.schema.json +0 -910
  69. package/dist-tsc/schemas/config-1.0.4.schema.json +0 -949
  70. package/dist-tsc/schemas/config-1.0.5.schema.json +0 -949
  71. package/dist-tsc/schemas/config-1.0.6.schema.json +0 -949
  72. package/dist-tsc/schemas/config-1.0.7.schema.json +0 -949
  73. package/dist-tsc/schemas/config-1.0.8.schema.json +0 -965
  74. package/dist-tsc/schemas/config-1.0.9.schema.json +0 -965
  75. package/dist-tsc/schemas/obsSets.schema.d.ts +0 -179
  76. package/dist-tsc/schemas/obsSets.schema.d.ts.map +0 -1
  77. package/dist-tsc/schemas/obsSets.schema.js +0 -195
  78. package/dist-tsc/schemas/obsSetsTabular.schema.d.ts +0 -57
  79. package/dist-tsc/schemas/obsSetsTabular.schema.d.ts.map +0 -1
  80. package/dist-tsc/schemas/obsSetsTabular.schema.js +0 -38
  81. package/dist-tsc/schemas/raster.schema.d.ts +0 -239
  82. package/dist-tsc/schemas/raster.schema.d.ts.map +0 -1
  83. package/dist-tsc/schemas/raster.schema.js +0 -120
  84. package/src/schemas/config-0.1.0.schema.json +0 -85
  85. package/src/schemas/config-1.0.0.schema.json +0 -733
  86. package/src/schemas/config-1.0.1.schema.json +0 -909
  87. package/src/schemas/config-1.0.10.schema.json +0 -969
  88. package/src/schemas/config-1.0.11.schema.json +0 -990
  89. package/src/schemas/config-1.0.12.schema.json +0 -997
  90. package/src/schemas/config-1.0.13.schema.json +0 -1013
  91. package/src/schemas/config-1.0.14.schema.json +0 -1048
  92. package/src/schemas/config-1.0.15.schema.json +0 -1048
  93. package/src/schemas/config-1.0.16.schema.json +0 -1089
  94. package/src/schemas/config-1.0.2.schema.json +0 -911
  95. package/src/schemas/config-1.0.3.schema.json +0 -911
  96. package/src/schemas/config-1.0.4.schema.json +0 -949
  97. package/src/schemas/config-1.0.5.schema.json +0 -949
  98. package/src/schemas/config-1.0.6.schema.json +0 -950
  99. package/src/schemas/config-1.0.7.schema.json +0 -950
  100. package/src/schemas/config-1.0.8.schema.json +0 -966
  101. package/src/schemas/config-1.0.9.schema.json +0 -965
  102. package/src/schemas/obsSets.schema.js +0 -195
  103. package/src/schemas/obsSetsTabular.schema.js +0 -39
  104. package/src/schemas/raster.schema.js +0 -121
  105. package/src/schemas/schema-schema.sh +0 -34
package/src/data-hooks.js CHANGED
@@ -1,7 +1,12 @@
1
1
  import { useState, useEffect, useMemo } from 'react';
2
2
  import { CoordinationType, DataType, STATUS } from '@vitessce/constants-internal';
3
3
  import { fromEntries } from '@vitessce/utils';
4
- import { useMatchingLoader, useMultiCoordinationValues, useSetWarning } from './state/hooks.js';
4
+ import { useQuery, useQueries } from '@tanstack/react-query';
5
+ import {
6
+ getMatchingLoader,
7
+ useMultiCoordinationValues,
8
+ useSetWarning,
9
+ } from './state/hooks.js';
5
10
  import {
6
11
  LoaderNotFoundError,
7
12
  } from './errors/index.js';
@@ -48,8 +53,6 @@ export function useDescription(loaders, dataset) {
48
53
  * datasets and data types to loader instances.
49
54
  * @param {string} dataset The key for a dataset,
50
55
  * used to identify which loader to use.
51
- * @param {function} addUrl A function to call to update
52
- * the URL list.
53
56
  * @param {boolean} isRequired Should a warning be thrown if
54
57
  * loading is unsuccessful?
55
58
  * @param {object} coordinationSetters Object where
@@ -63,111 +66,111 @@ export function useDescription(loaders, dataset) {
63
66
  * number of items in the cells object.
64
67
  */
65
68
  export function useObsEmbeddingData(
66
- loaders, dataset, addUrl, isRequired,
69
+ loaders, dataset, isRequired,
67
70
  coordinationSetters, initialCoordinationValues, matchOn,
68
71
  ) {
69
72
  return useDataType(
70
73
  DataType.OBS_EMBEDDING,
71
- loaders, dataset, addUrl, isRequired,
74
+ loaders, dataset, isRequired,
72
75
  coordinationSetters, initialCoordinationValues, matchOn,
73
76
  );
74
77
  }
75
78
 
76
79
  export function useObsLocationsData(
77
- loaders, dataset, addUrl, isRequired,
80
+ loaders, dataset, isRequired,
78
81
  coordinationSetters, initialCoordinationValues, matchOn,
79
82
  ) {
80
83
  return useDataType(
81
84
  DataType.OBS_LOCATIONS,
82
- loaders, dataset, addUrl, isRequired,
85
+ loaders, dataset, isRequired,
83
86
  coordinationSetters, initialCoordinationValues, matchOn,
84
87
  );
85
88
  }
86
89
 
87
90
  export function useObsLabelsData(
88
- loaders, dataset, addUrl, isRequired,
91
+ loaders, dataset, isRequired,
89
92
  coordinationSetters, initialCoordinationValues, matchOn,
90
93
  ) {
91
94
  return useDataType(
92
95
  DataType.OBS_LABELS,
93
- loaders, dataset, addUrl, isRequired,
96
+ loaders, dataset, isRequired,
94
97
  coordinationSetters, initialCoordinationValues, matchOn,
95
98
  );
96
99
  }
97
100
 
98
101
  export function useObsSegmentationsData(
99
- loaders, dataset, addUrl, isRequired,
102
+ loaders, dataset, isRequired,
100
103
  coordinationSetters, initialCoordinationValues, matchOn,
101
104
  ) {
102
105
  return useDataType(
103
106
  DataType.OBS_SEGMENTATIONS,
104
- loaders, dataset, addUrl, isRequired,
107
+ loaders, dataset, isRequired,
105
108
  coordinationSetters, initialCoordinationValues, matchOn,
106
109
  );
107
110
  }
108
111
 
109
112
  export function useObsSetsData(
110
- loaders, dataset, addUrl, isRequired,
113
+ loaders, dataset, isRequired,
111
114
  coordinationSetters, initialCoordinationValues, matchOn,
112
115
  ) {
113
116
  return useDataType(
114
117
  DataType.OBS_SETS,
115
- loaders, dataset, addUrl, isRequired,
118
+ loaders, dataset, isRequired,
116
119
  coordinationSetters, initialCoordinationValues, matchOn,
117
120
  );
118
121
  }
119
122
 
120
123
  export function useObsFeatureMatrixData(
121
- loaders, dataset, addUrl, isRequired,
124
+ loaders, dataset, isRequired,
122
125
  coordinationSetters, initialCoordinationValues, matchOn,
123
126
  ) {
124
127
  return useDataType(
125
128
  DataType.OBS_FEATURE_MATRIX,
126
- loaders, dataset, addUrl, isRequired,
129
+ loaders, dataset, isRequired,
127
130
  coordinationSetters, initialCoordinationValues, matchOn,
128
131
  );
129
132
  }
130
133
 
131
134
  export function useFeatureLabelsData(
132
- loaders, dataset, addUrl, isRequired,
135
+ loaders, dataset, isRequired,
133
136
  coordinationSetters, initialCoordinationValues, matchOn,
134
137
  ) {
135
138
  return useDataType(
136
139
  DataType.FEATURE_LABELS,
137
- loaders, dataset, addUrl, isRequired,
140
+ loaders, dataset, isRequired,
138
141
  coordinationSetters, initialCoordinationValues, matchOn,
139
142
  );
140
143
  }
141
144
 
142
145
  export function useImageData(
143
- loaders, dataset, addUrl, isRequired,
146
+ loaders, dataset, isRequired,
144
147
  coordinationSetters, initialCoordinationValues, matchOn,
145
148
  ) {
146
149
  return useDataType(
147
150
  DataType.IMAGE,
148
- loaders, dataset, addUrl, isRequired,
151
+ loaders, dataset, isRequired,
149
152
  coordinationSetters, initialCoordinationValues, matchOn,
150
153
  );
151
154
  }
152
155
 
153
156
  export function useGenomicProfilesData(
154
- loaders, dataset, addUrl, isRequired,
157
+ loaders, dataset, isRequired,
155
158
  coordinationSetters, initialCoordinationValues, matchOn,
156
159
  ) {
157
160
  return useDataType(
158
161
  DataType.GENOMIC_PROFILES,
159
- loaders, dataset, addUrl, isRequired,
162
+ loaders, dataset, isRequired,
160
163
  coordinationSetters, initialCoordinationValues, matchOn,
161
164
  );
162
165
  }
163
166
 
164
167
  export function useNeighborhoodsData(
165
- loaders, dataset, addUrl, isRequired,
168
+ loaders, dataset, isRequired,
166
169
  coordinationSetters, initialCoordinationValues, matchOn,
167
170
  ) {
168
171
  return useDataType(
169
172
  DataType.NEIGHBORHOODS,
170
- loaders, dataset, addUrl, isRequired,
173
+ loaders, dataset, isRequired,
171
174
  coordinationSetters, initialCoordinationValues, matchOn,
172
175
  );
173
176
  }
@@ -193,73 +196,71 @@ export function useFeatureSelection(
193
196
  selection,
194
197
  matchOn,
195
198
  ) {
196
- const [geneData, setGeneData] = useState();
197
- const [status, setStatus] = useState(STATUS.LOADING);
198
- const [loadedGeneName, setLoadedGeneName] = useState(null);
199
-
200
199
  const setWarning = useSetWarning();
201
- const loader = useMatchingLoader(loaders, dataset, DataType.OBS_FEATURE_MATRIX, matchOn);
202
-
203
- useEffect(() => {
204
- if (!selection) {
205
- setGeneData(null);
206
- setLoadedGeneName(null);
207
- setStatus(STATUS.SUCCESS);
208
- return;
209
- }
210
- if (loader) {
211
- setGeneData(null);
212
- setLoadedGeneName(null);
213
- setStatus(STATUS.LOADING);
214
- const implementsGeneSelection = typeof loader.loadGeneSelection === 'function';
215
- if (implementsGeneSelection) {
216
- loader
217
- .loadGeneSelection({ selection })
218
- .catch(e => warn(e, setWarning))
219
- .then((payload) => {
220
- if (!payload) return;
200
+ const featureQueries = useQueries({
201
+ queries: selection?.map(featureId => ({
202
+ structuralSharing: false,
203
+ placeholderData: null,
204
+ queryKey: [dataset, DataType.OBS_FEATURE_MATRIX, matchOn, featureId, 'useFeatureSelection'],
205
+ // Query function should return an object
206
+ // { data, dataKey } where dataKey is the loaded gene selection.
207
+ queryFn: async (ctx) => {
208
+ const loader = getMatchingLoader(
209
+ ctx.meta.loaders, ctx.queryKey[0], ctx.queryKey[1], ctx.queryKey[2],
210
+ );
211
+ if (loader) {
212
+ const implementsGeneSelection = typeof loader.loadGeneSelection === 'function';
213
+ if (implementsGeneSelection) {
214
+ const payload = await loader.loadGeneSelection({ selection: [featureId] });
215
+ if (!payload) return null;
221
216
  const { data } = payload;
222
- setGeneData(data);
223
- setStatus(STATUS.SUCCESS);
224
- setLoadedGeneName(selection);
225
- });
226
- } else {
227
- loader.load().catch(e => warn(e, setWarning)).then((payload) => {
228
- if (!payload) return;
217
+ return { data: data[0], dataKey: selection };
218
+ }
219
+ // Loader does not implement loadGeneSelection.
220
+ const payload = await loader.load();
221
+ if (!payload) return null;
229
222
  const { data } = payload;
230
223
  const { obsIndex, featureIndex, obsFeatureMatrix } = data;
231
- const expressionDataForSelection = selection.map((sel) => {
232
- const geneIndex = featureIndex.indexOf(sel);
233
- const numGenes = featureIndex.length;
234
- const numCells = obsIndex.length;
235
- const expressionData = new Float32Array(numCells);
236
- for (let cellIndex = 0; cellIndex < numCells; cellIndex += 1) {
237
- expressionData[cellIndex] = obsFeatureMatrix.data[cellIndex * numGenes + geneIndex];
238
- }
239
- return expressionData;
240
- });
241
- setGeneData(expressionDataForSelection);
242
- setStatus(STATUS.SUCCESS);
243
- setLoadedGeneName(selection);
244
- });
245
- }
246
- } else {
247
- setGeneData(null);
248
- setLoadedGeneName(null);
249
- if (isRequired) {
250
- warn(
251
- new LoaderNotFoundError(loaders, dataset, DataType.OBS_FEATURE_MATRIX, matchOn),
252
- setWarning,
253
- );
254
- setStatus(STATUS.ERROR);
255
- } else {
256
- setStatus(STATUS.SUCCESS);
257
- }
258
- }
224
+ // Compute expressionData
225
+ const geneIndex = featureIndex.indexOf(featureId);
226
+ const numGenes = featureIndex.length;
227
+ const numCells = obsIndex.length;
228
+ const expressionData = new Float32Array(numCells);
229
+ for (let cellIndex = 0; cellIndex < numCells; cellIndex += 1) {
230
+ expressionData[cellIndex] = obsFeatureMatrix.data[cellIndex * numGenes + geneIndex];
231
+ }
232
+ return { data: expressionData, dataKey: featureId };
233
+ }
234
+ // No loader was found.
235
+ if (isRequired) {
236
+ throw new LoaderNotFoundError(loaders, dataset, DataType.OBS_FEATURE_MATRIX, matchOn);
237
+ } else {
238
+ return { data: null, dataKey: null };
239
+ }
240
+ },
241
+ meta: { loaders },
242
+ })) || [],
243
+ });
244
+ const anyLoading = featureQueries.some(q => q.isFetching);
245
+ const anyError = featureQueries.some(q => q.isError);
246
+ // eslint-disable-next-line no-nested-ternary
247
+ const dataStatus = anyLoading ? STATUS.LOADING : (anyError ? STATUS.ERROR : STATUS.SUCCESS);
248
+ const isSuccess = dataStatus === STATUS.SUCCESS;
249
+ const geneData = isSuccess ? featureQueries.map(q => q.data?.data || null) : null;
250
+ const loadedGeneName = isSuccess ? featureQueries.map(q => q.data?.dataKey || null) : null;
251
+
252
+ useEffect(() => {
253
+ featureQueries
254
+ .map(q => q.error)
255
+ .filter(e => Boolean(e))
256
+ .forEach((error) => {
257
+ warn(error, setWarning);
258
+ });
259
+ // Deliberate dependency omissions: use indirect dependencies for efficiency.
259
260
  // eslint-disable-next-line react-hooks/exhaustive-deps
260
- }, [loader, selection]);
261
+ }, [anyError, setWarning]);
261
262
 
262
- return [geneData, loadedGeneName, status];
263
+ return [geneData, loadedGeneName, dataStatus];
263
264
  }
264
265
 
265
266
  /**
@@ -272,68 +273,85 @@ export function useFeatureSelection(
272
273
  * datasets and data types to loader instances.
273
274
  * @param {string} dataset The key for a dataset,
274
275
  * used to identify which loader to use.
275
- * @param {function} addUrl A function to call to update
276
- * the URL list.
277
276
  * @param {boolean} isRequired Should a warning be thrown if
278
277
  * loading is unsuccessful?
279
278
  * @returns {object} [attrs] { rows, cols } object containing cell and gene names.
280
279
  */
281
280
  export function useObsFeatureMatrixIndices(
282
- loaders, dataset, addUrl, isRequired, matchOn,
281
+ loaders, dataset, isRequired, matchOn,
283
282
  ) {
284
- const [data, setData] = useState({});
285
- const [status, setStatus] = useState(STATUS.LOADING);
286
-
287
283
  const setWarning = useSetWarning();
288
- const loader = useMatchingLoader(loaders, dataset, DataType.OBS_FEATURE_MATRIX, matchOn);
289
-
290
- useEffect(() => {
291
- if (loader) {
292
- setStatus(STATUS.LOADING);
293
- const implementsLoadAttrs = typeof loader.loadAttrs === 'function';
294
- if (implementsLoadAttrs) {
295
- loader.loadAttrs().catch(e => warn(e, setWarning)).then((payload) => {
296
- if (!payload) return;
297
- const { data: payloadData, url } = payload;
298
- setData({
299
- obsIndex: payloadData.rows,
300
- featureIndex: payloadData.cols,
301
- });
302
- addUrl(url, DataType.OBS_FEATURE_MATRIX);
303
- setStatus(STATUS.SUCCESS);
304
- });
305
- } else {
306
- loader.load().catch(e => warn(e, setWarning)).then((payload) => {
307
- if (!payload) return;
284
+ const placeholderObject = useMemo(() => ({}), []);
285
+ const indicesQuery = useQuery({
286
+ // TODO: only enable once `loaders` is available?
287
+ structuralSharing: false,
288
+ placeholderData: placeholderObject,
289
+ // Include the hook name in the queryKey to prevent the case in which an identical queryKey
290
+ // in a different hook would cause an accidental cache hit.
291
+ queryKey: [dataset, DataType.OBS_FEATURE_MATRIX, matchOn, 'useObsFeatureMatrixIndices'],
292
+ // Query function should return an object
293
+ // { data, dataKey } where dataKey is the loaded gene selection.
294
+ // TODO: use TypeScript to type the return value?
295
+ queryFn: async (ctx) => {
296
+ const loader = getMatchingLoader(
297
+ ctx.meta.loaders, ctx.queryKey[0], ctx.queryKey[1], ctx.queryKey[2],
298
+ );
299
+ if (loader) {
300
+ const implementsLoadAttrs = typeof loader.loadAttrs === 'function';
301
+ if (implementsLoadAttrs) {
302
+ // Has loadAttrs function.
303
+ const payload = await loader.loadAttrs();
304
+ if (!payload) return placeholderObject;
308
305
  const { data: payloadData, url } = payload;
309
- setData({
306
+
307
+ return {
308
+ data: {
309
+ obsIndex: payloadData.rows,
310
+ featureIndex: payloadData.cols,
311
+ },
312
+ urls: [{ url, name: DataType.OBS_FEATURE_MATRIX }],
313
+ };
314
+ }
315
+ // No loadAttrs function.
316
+ const payload = await loader.load();
317
+ if (!payload) return placeholderObject;
318
+ const { data: payloadData, url } = payload;
319
+ return {
320
+ data: {
310
321
  obsIndex: payloadData.obsIndex,
311
322
  featureIndex: payloadData.featureIndex,
312
- });
313
- addUrl(url, DataType.OBS_FEATURE_MATRIX);
314
- setStatus(STATUS.SUCCESS);
315
- });
323
+ },
324
+ urls: [{ url, name: DataType.OBS_FEATURE_MATRIX }],
325
+ };
316
326
  }
317
- } else {
318
- setData({});
327
+ // No loader was found.
319
328
  if (isRequired) {
320
- warn(
321
- new LoaderNotFoundError(loaders, dataset, DataType.OBS_FEATURE_MATRIX, matchOn),
322
- setWarning,
323
- );
324
- setStatus(STATUS.ERROR);
329
+ // Status: error
330
+ throw new LoaderNotFoundError(loaders, dataset, DataType.OBS_FEATURE_MATRIX, matchOn);
325
331
  } else {
326
- setStatus(STATUS.SUCCESS);
332
+ // Status: success
333
+ return { data: placeholderObject, dataKey: null };
327
334
  }
335
+ },
336
+ meta: { loaders },
337
+ });
338
+ const { data, status, isFetching, error } = indicesQuery;
339
+ const loadedData = data?.data || placeholderObject;
340
+
341
+ const dataStatus = isFetching ? STATUS.LOADING : status;
342
+ const urls = data?.urls;
343
+
344
+ useEffect(() => {
345
+ if (error) {
346
+ setWarning(error.message);
328
347
  }
329
- // eslint-disable-next-line react-hooks/exhaustive-deps
330
- }, [loader]);
348
+ }, [error, setWarning]);
331
349
 
332
- return [data, status];
350
+ return [loadedData, dataStatus, urls];
333
351
  }
334
352
 
335
353
  export function useMultiObsLabels(
336
- coordinationScopes, obsType, loaders, dataset, addUrl,
354
+ coordinationScopes, obsType, loaders, dataset,
337
355
  ) {
338
356
  const obsLabelsTypes = useMultiCoordinationValues(
339
357
  CoordinationType.OBS_LABELS_TYPE,
@@ -347,8 +365,9 @@ export function useMultiObsLabels(
347
365
  ), [obsLabelsTypes, obsType]);
348
366
  const [obsLabelsData, obsLabelsDataStatus] = useDataTypeMulti(
349
367
  DataType.OBS_LABELS, loaders, dataset,
350
- addUrl, false, {}, {},
368
+ false, {}, {},
351
369
  obsLabelsMatchOnObj,
352
370
  );
353
- return [obsLabelsTypes, obsLabelsData, obsLabelsDataStatus];
371
+ const urls = null; // TODO?
372
+ return [obsLabelsTypes, obsLabelsData, obsLabelsDataStatus, urls];
354
373
  }
@@ -10,6 +10,6 @@ export default class DataSourceFetchError extends AbstractLoaderError {
10
10
 
11
11
  warnInConsole() {
12
12
  const { source, url, headers } = this;
13
- console.warn(`${source} failed to fetch from ${url} with headers ${headers}`);
13
+ console.warn(`${source} failed to fetch from ${url} with headers ${JSON.stringify(headers)}`);
14
14
  }
15
15
  }
package/src/hooks.js CHANGED
@@ -4,6 +4,7 @@ import {
4
4
  import { debounce, every } from 'lodash-es';
5
5
  import { extent } from 'd3-array';
6
6
  import { capitalize, fromEntries } from '@vitessce/utils';
7
+ import { STATUS } from '@vitessce/constants-internal';
7
8
  import { useGridResize, useEmitGridResize } from './state/hooks.js';
8
9
  import { VITESSCE_CONTAINER } from './classNames.js';
9
10
 
@@ -137,34 +138,23 @@ export function useDeckCanvasSize() {
137
138
  */
138
139
  export function useReady(statusValues) {
139
140
  return useMemo(() => every(
140
- statusValues, val => val === 'success',
141
+ statusValues, val => val !== STATUS.LOADING,
141
142
  // eslint-disable-next-line react-hooks/exhaustive-deps
142
143
  ), statusValues);
143
144
  }
144
145
 
145
146
  /**
146
147
  * This hook helps manage a list of URLs.
147
- * @param {object} loaders The loaders dependency.
148
- * @param {string} dataset The dataset UID dependency.
149
- * @returns {array} An array
150
- * [urls, addUrl]
151
- * where urls is the array of URL objects,
152
- * and addUrl is a function for adding a URL to the array.
148
+ * @param {(null|object[])[]} urls Array of (null or array of { url, name }).
149
+ * @returns {array} An array of { url, name } objects (flattened from the input).
153
150
  */
154
- export function useUrls(loaders, dataset) {
155
- const [urls, setUrls] = useState([]);
156
-
157
- const addUrl = useCallback((url, name) => {
158
- if (url) {
159
- setUrls(prev => ([...prev, { url, name }]));
160
- }
161
- }, [setUrls]);
162
-
163
- useEffect(() => {
164
- setUrls([]);
165
- }, [loaders, dataset]);
166
-
167
- return [urls, addUrl];
151
+ export function useUrls(urls) {
152
+ const mergedUrls = useMemo(
153
+ () => urls.filter(a => Array.isArray(a)).flat(),
154
+ // eslint-disable-next-line react-hooks/exhaustive-deps
155
+ urls,
156
+ );
157
+ return mergedUrls;
168
158
  }
169
159
 
170
160
  /**
package/src/index.js CHANGED
@@ -17,6 +17,7 @@ export {
17
17
  useGridItemSize,
18
18
  } from './hooks.js';
19
19
  export {
20
+ useInitialCoordination,
20
21
  useCoordination,
21
22
  useComplexCoordination,
22
23
  useMultiCoordinationValues,
@@ -74,3 +75,4 @@ export {
74
75
  OptionSelect,
75
76
  usePlotOptionsStyles,
76
77
  } from './shared-plot-options/index.js';
78
+ export { logConfig } from './view-config-utils.js';
@@ -0,0 +1,52 @@
1
+ // This function has been copied from MUI v4
2
+ // with logic related to randomness / random numbers removed.
3
+ // Reference: https://github.com/mui/material-ui/blob/0adada09/packages/mui-styles/src/createGenerateClassName/createGenerateClassName.js#L29
4
+ /**
5
+ * This is the list of the style rule name we use as drop in replacement for the built-in
6
+ * pseudo classes (:checked, :disabled, :focused, etc.).
7
+ *
8
+ * Why do they exist in the first place?
9
+ * These classes are used at a specificity of 2.
10
+ * It allows them to override previously defined styles as well as
11
+ * being untouched by simple user overrides.
12
+ */
13
+ const stateClasses = [
14
+ 'checked',
15
+ 'disabled',
16
+ 'error',
17
+ 'focused',
18
+ 'focusVisible',
19
+ 'required',
20
+ 'expanded',
21
+ 'selected',
22
+ ];
23
+
24
+ const defaultPrefix = 'vit-';
25
+
26
+ export function createGenerateClassName(customPrefix) {
27
+ return (rule, styleSheet) => {
28
+ const {
29
+ name: styleSheetName,
30
+ link: styleSheetLink,
31
+ classNamePrefix,
32
+ } = styleSheet.options;
33
+ const { key: ruleKey } = rule;
34
+
35
+ const seedPrefix = customPrefix ? `${customPrefix}-` : defaultPrefix;
36
+
37
+ // Is a global static MUI style?
38
+ if (styleSheetName && styleSheetName.indexOf('Mui') === 0 && !styleSheetLink) {
39
+ // We can use a shorthand class name, we never use the keys to style the components.
40
+ if (stateClasses.indexOf(ruleKey) !== -1) {
41
+ return `Mui-${ruleKey}`;
42
+ }
43
+ const prefix = `${seedPrefix}${styleSheetName}-${ruleKey}`;
44
+ return prefix;
45
+ }
46
+ // Help with debuggability.
47
+ if (classNamePrefix) {
48
+ return `${seedPrefix}${classNamePrefix}-${ruleKey}`;
49
+ }
50
+ return `${seedPrefix}${ruleKey}`;
51
+ };
52
+ }
@@ -11,7 +11,7 @@ export default function OptionSelect(props) {
11
11
  disableUnderline
12
12
  {...props}
13
13
  classes={{
14
- root: classes.selectRoot,
14
+ root: classes.optionSelectRoot,
15
15
  ...classesProp,
16
16
  }}
17
17
  />
@@ -42,7 +42,7 @@ export const useStyles = makeStyles(theme => ({
42
42
  fontSize: '14px',
43
43
  },
44
44
  },
45
- selectRoot: {
45
+ optionSelectRoot: {
46
46
  padding: 0,
47
47
  height: 'auto',
48
48
  },