@vitessce/vit-s 3.0.0 → 3.1.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.
Files changed (105) hide show
  1. package/dist/index.js +28430 -25685
  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 +12 -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 +14 -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
@@ -1,7 +1,12 @@
1
- import { useState, useEffect } from 'react';
2
- import { capitalize } from '@vitessce/utils';
1
+ import { useEffect, useMemo } from 'react';
2
+ import { capitalize, fromEntries } from '@vitessce/utils';
3
3
  import { STATUS } from '@vitessce/constants-internal';
4
- import { useMatchingLoader, useMatchingLoaders, useSetWarning } from './state/hooks.js';
4
+ import { useQuery, useQueries } from '@tanstack/react-query';
5
+ import {
6
+ getMatchingLoader,
7
+ useMatchingLoader,
8
+ useSetWarning,
9
+ } from './state/hooks.js';
5
10
  import {
6
11
  AbstractLoaderError,
7
12
  LoaderNotFoundError,
@@ -49,6 +54,31 @@ export function initCoordinationSpace(values, setters, initialValues) {
49
54
  }
50
55
 
51
56
 
57
+ async function dataQueryFn(ctx) {
58
+ const { placeholderObject, loaders } = ctx.meta;
59
+ // This ordering of the queryKey must match.
60
+ const [dataset, dataType, matchOn, isRequired] = ctx.queryKey;
61
+ const loader = getMatchingLoader(loaders, dataset, dataType, matchOn);
62
+ if (loader) {
63
+ // TODO: can cacheing logic be removed from all loaders?
64
+ const payload = await loader.load();
65
+ if (!payload) return placeholderObject; // TODO: throw error instead?
66
+ const { data, url, coordinationValues } = payload;
67
+ // Status: success
68
+ // Array of objects like { url, name }.
69
+ const urls = Array.isArray(url) ? url : [{ url, name: dataType }];
70
+ return { data, coordinationValues, urls };
71
+ }
72
+ // No loader was found.
73
+ if (isRequired) {
74
+ // Status: error
75
+ throw new LoaderNotFoundError(loaders, dataset, dataType, matchOn);
76
+ } else {
77
+ // Status: success
78
+ return { data: placeholderObject };
79
+ }
80
+ }
81
+
52
82
  /**
53
83
  * Get data from a cells data type loader,
54
84
  * updating "ready" and URL state appropriately.
@@ -58,8 +88,6 @@ export function initCoordinationSpace(values, setters, initialValues) {
58
88
  * datasets and data types to loader instances.
59
89
  * @param {string} dataset The key for a dataset,
60
90
  * used to identify which loader to use.
61
- * @param {function} addUrl A function to call to update
62
- * the URL list.
63
91
  * @param {boolean} isRequired Should a warning be thrown if
64
92
  * loading is unsuccessful?
65
93
  * @param {object} coordinationSetters Object where
@@ -68,54 +96,50 @@ export function initCoordinationSpace(values, setters, initialValues) {
68
96
  * @param {object} initialCoordinationValues Object where
69
97
  * keys are coordination type names with the prefix 'initialize',
70
98
  * values are initialization preferences as boolean values.
71
- * @returns {array} [cells, cellsCount] where
99
+ * @returns {array} [data, status, urls] where
72
100
  * cells is an object and cellsCount is the
73
101
  * number of items in the cells object.
74
102
  */
75
103
  export function useDataType(
76
- dataType, loaders, dataset, addUrl, isRequired,
104
+ dataType, loaders, dataset, isRequired,
77
105
  coordinationSetters, initialCoordinationValues, matchOn,
78
106
  ) {
79
- const [data, setData] = useState({});
80
- const [status, setStatus] = useState(STATUS.LOADING);
81
-
82
107
  const setWarning = useSetWarning();
83
- const loader = useMatchingLoader(loaders, dataset, dataType, matchOn);
108
+ const placeholderObject = useMemo(() => ({}), []);
109
+ const dataQuery = useQuery({
110
+ // TODO: only enable when loaders has been initialized?
111
+ structuralSharing: false,
112
+ placeholderData: placeholderObject,
113
+ // Include the hook name in the queryKey to prevent the case in which an identical queryKey
114
+ // in a different hook would cause an accidental cache hit.
115
+ queryKey: [dataset, dataType, matchOn, isRequired, 'useDataType'],
116
+ // Query function should return an object
117
+ // { data, dataKey } where dataKey is the loaded gene selection.
118
+ queryFn: dataQueryFn,
119
+ meta: { loaders, placeholderObject },
120
+ });
121
+ const { data, status, isFetching, error } = dataQuery;
122
+ const loadedData = data?.data || placeholderObject;
123
+
124
+ const coordinationValues = data?.coordinationValues;
125
+ const urls = data?.urls;
84
126
 
85
127
  useEffect(() => {
86
- if (loader) {
87
- setStatus(STATUS.LOADING);
88
- loader.load().catch(e => warn(e, setWarning)).then((payload) => {
89
- if (!payload) return;
90
- const { data: payloadData, url, coordinationValues } = payload;
91
- setData(payloadData);
92
- if (Array.isArray(url)) {
93
- url.forEach(([val, name]) => {
94
- addUrl(val, name);
95
- });
96
- } else if (url) {
97
- addUrl(url, dataType);
98
- }
99
- initCoordinationSpace(
100
- coordinationValues,
101
- coordinationSetters,
102
- initialCoordinationValues,
103
- );
104
- setStatus(STATUS.SUCCESS);
105
- });
106
- } else {
107
- setData({});
108
- if (isRequired) {
109
- warn(new LoaderNotFoundError(loaders, dataset, dataType, matchOn), setWarning);
110
- setStatus(STATUS.ERROR);
111
- } else {
112
- setStatus(STATUS.SUCCESS);
113
- }
128
+ initCoordinationSpace(
129
+ coordinationValues,
130
+ coordinationSetters,
131
+ initialCoordinationValues,
132
+ );
133
+ }, [coordinationValues]);
134
+
135
+ useEffect(() => {
136
+ if (error) {
137
+ warn(error, setWarning);
114
138
  }
115
- // eslint-disable-next-line react-hooks/exhaustive-deps
116
- }, [loader]);
139
+ }, [error, setWarning]);
117
140
 
118
- return [data, status];
141
+ const dataStatus = isFetching ? STATUS.LOADING : status;
142
+ return [loadedData, dataStatus, urls];
119
143
  }
120
144
 
121
145
  /**
@@ -127,8 +151,6 @@ export function useDataType(
127
151
  * datasets and data types to loader instances.
128
152
  * @param {string} dataset The key for a dataset,
129
153
  * used to identify which loader to use.
130
- * @param {function} addUrl A function to call to update
131
- * the URL list.
132
154
  * @param {boolean} isRequired Should a warning be thrown if
133
155
  * loading is unsuccessful?
134
156
  * @param {object} coordinationSetters Object where
@@ -142,51 +164,64 @@ export function useDataType(
142
164
  * number of items in the cells object.
143
165
  */
144
166
  export function useDataTypeMulti(
145
- dataType, loaders, dataset, addUrl, isRequired,
167
+ dataType, loaders, dataset, isRequired,
146
168
  coordinationSetters, initialCoordinationValues, matchOnObj,
147
169
  ) {
148
- const [data, setData] = useState({});
149
- const [status, setStatus] = useState(STATUS.LOADING);
150
-
170
+ const placeholderObject = useMemo(() => ({}), []);
151
171
  const setWarning = useSetWarning();
152
- const matchingLoaders = useMatchingLoaders(loaders, dataset, dataType, matchOnObj);
172
+
173
+ const matchOnEntries = matchOnObj ? Object.entries(matchOnObj) : [];
174
+ const dataQueries = useQueries({
175
+ // eslint-disable-next-line no-unused-vars
176
+ queries: matchOnEntries.map(([scopeKey, matchOn]) => ({
177
+ structuralSharing: false,
178
+ placeholderData: placeholderObject,
179
+ // Query key should match useDataType for shared cacheing
180
+ // and correctness of dataQueryFn.
181
+ queryKey: [dataset, dataType, matchOn, isRequired, 'useDataType'],
182
+ // Query function should return an object
183
+ // { data, dataKey } where dataKey is the loaded gene selection.
184
+ queryFn: dataQueryFn,
185
+ meta: { loaders, placeholderObject },
186
+ })),
187
+ });
188
+
189
+ const anyLoading = dataQueries.some(q => q.isFetching);
190
+ const anyError = dataQueries.some(q => q.isError);
191
+ // eslint-disable-next-line no-nested-ternary
192
+ const dataStatus = anyLoading ? STATUS.LOADING : (anyError ? STATUS.ERROR : STATUS.SUCCESS);
193
+ const isSuccess = dataStatus === STATUS.SUCCESS;
153
194
 
154
195
  useEffect(() => {
155
- if (matchingLoaders) {
156
- setStatus(STATUS.LOADING);
157
- Object.entries(matchingLoaders).forEach(([scopeKey, loader]) => {
158
- if (loader) {
159
- loader.load().catch(e => warn(e, setWarning)).then((payload) => {
160
- if (!payload) return;
161
- const { data: payloadData, url, coordinationValues } = payload;
162
- setData((prev) => {
163
- // eslint-disable-next-line no-param-reassign
164
- prev[scopeKey] = payloadData;
165
- return prev;
166
- });
167
- addUrl(url, dataType);
168
- initCoordinationSpace(
169
- coordinationValues,
170
- coordinationSetters,
171
- initialCoordinationValues,
172
- );
173
- setStatus(STATUS.SUCCESS);
174
- });
175
- }
196
+ dataQueries
197
+ .map(q => q.data?.coordinationValues)
198
+ .filter(v => Boolean(v))
199
+ .forEach((coordinationValues) => {
200
+ initCoordinationSpace(
201
+ coordinationValues,
202
+ coordinationSetters,
203
+ initialCoordinationValues,
204
+ );
176
205
  });
177
- } else {
178
- setData({});
179
- if (isRequired) {
180
- warn(new LoaderNotFoundError(loaders, dataset, dataType, matchOnObj), setWarning);
181
- setStatus(STATUS.ERROR);
182
- } else {
183
- setStatus(STATUS.SUCCESS);
184
- }
185
- }
206
+ // Deliberate dependency omissions: use indirect dependencies for efficiency.
186
207
  // eslint-disable-next-line react-hooks/exhaustive-deps
187
- }, [matchingLoaders]);
208
+ }, [isSuccess]);
188
209
 
189
- return [data, status];
210
+ useEffect(() => {
211
+ dataQueries.map(q => q.error).filter(e => Boolean(e)).forEach((error) => {
212
+ warn(error, setWarning);
213
+ });
214
+ // Deliberate dependency omissions: use indirect dependencies for efficiency.
215
+ // eslint-disable-next-line react-hooks/exhaustive-deps
216
+ }, [anyError, setWarning]);
217
+
218
+ // Convert data to object keyed by scopeKey.
219
+ const data = useMemo(() => fromEntries(
220
+ matchOnEntries.map(([scopeKey], i) => ([scopeKey, dataQueries[i].data?.data])),
221
+ // Deliberate dependency omissions: matchOnEntries, since dataQueries depends on it.
222
+ // eslint-disable-next-line react-hooks/exhaustive-deps
223
+ ), [dataQueries]);
224
+ return [data, dataStatus];
190
225
  }
191
226
 
192
227
  export function useHasLoader(loaders, dataset, dataType, matchOn) {