@haniffalab/cherita-react 1.0.0-dev.2025-03-13.bda9e1a6 → 1.0.0-dev.2025-03-13.274a553c

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 (95) hide show
  1. package/dist/{components → cjs/components}/full-page/FullPage.js +6 -5
  2. package/dist/{components → cjs/components}/full-page/FullPagePseudospatial.js +6 -5
  3. package/dist/{components → cjs/components}/obs-list/ObsItem.js +50 -46
  4. package/dist/{components → cjs/components}/obs-list/ObsList.js +9 -7
  5. package/dist/{components → cjs/components}/obs-list/ObsToolbar.js +12 -11
  6. package/dist/{components → cjs/components}/offcanvas/index.js +24 -20
  7. package/dist/{components → cjs/components}/pseudospatial/Pseudospatial.js +9 -8
  8. package/dist/{components → cjs/components}/pseudospatial/PseudospatialToolbar.js +4 -3
  9. package/dist/{components → cjs/components}/scatterplot/Scatterplot.js +31 -22
  10. package/dist/{components → cjs/components}/scatterplot/SpatialControls.js +11 -10
  11. package/dist/{components → cjs/components}/scatterplot/Toolbox.js +6 -5
  12. package/dist/{components → cjs/components}/search-bar/SearchBar.js +6 -5
  13. package/dist/{components → cjs/components}/search-bar/SearchResults.js +11 -9
  14. package/dist/{components → cjs/components}/var-list/VarItem.js +32 -27
  15. package/dist/{components → cjs/components}/var-list/VarList.js +14 -10
  16. package/dist/{components → cjs/components}/var-list/VarListToolbar.js +4 -3
  17. package/dist/{components → cjs/components}/var-list/VarSet.js +21 -18
  18. package/dist/{components → cjs/components}/violin/Violin.js +4 -3
  19. package/dist/{context → cjs/context}/DatasetContext.js +11 -9
  20. package/dist/{context → cjs/context}/FilterContext.js +4 -3
  21. package/dist/{context → cjs/context}/ZarrDataContext.js +4 -3
  22. package/dist/{helpers → cjs/helpers}/color-helper.js +12 -11
  23. package/dist/{helpers → cjs/helpers}/map-helper.js +8 -7
  24. package/dist/{helpers → cjs/helpers}/zarr-helper.js +25 -15
  25. package/dist/{utils → cjs/utils}/Histogram.js +12 -8
  26. package/dist/{utils → cjs/utils}/ImageViewer.js +6 -5
  27. package/dist/{utils → cjs/utils}/Legend.js +8 -7
  28. package/dist/{utils → cjs/utils}/LoadingIndicators.js +5 -4
  29. package/dist/{utils → cjs/utils}/VirtualizedList.js +10 -9
  30. package/dist/{utils → cjs/utils}/requests.js +33 -21
  31. package/dist/{utils → cjs/utils}/string.js +9 -4
  32. package/dist/{utils → cjs/utils}/zarrData.js +12 -4
  33. package/dist/esm/components/dotplot/Dotplot.js +135 -0
  34. package/dist/esm/components/dotplot/DotplotControls.js +148 -0
  35. package/dist/esm/components/full-page/FullPage.js +134 -0
  36. package/dist/esm/components/full-page/FullPagePseudospatial.js +151 -0
  37. package/dist/esm/components/heatmap/Heatmap.js +105 -0
  38. package/dist/esm/components/heatmap/HeatmapControls.js +23 -0
  39. package/dist/esm/components/matrixplot/Matrixplot.js +107 -0
  40. package/dist/esm/components/matrixplot/MatrixplotControls.js +38 -0
  41. package/dist/esm/components/obs-list/ObsItem.js +477 -0
  42. package/dist/esm/components/obs-list/ObsList.js +256 -0
  43. package/dist/esm/components/obs-list/ObsToolbar.js +58 -0
  44. package/dist/esm/components/obsm-list/ObsmList.js +72 -0
  45. package/dist/esm/components/offcanvas/index.js +67 -0
  46. package/dist/esm/components/pseudospatial/Pseudospatial.js +228 -0
  47. package/dist/esm/components/pseudospatial/PseudospatialToolbar.js +123 -0
  48. package/dist/esm/components/scatterplot/Scatterplot.js +394 -0
  49. package/dist/esm/components/scatterplot/ScatterplotControls.js +71 -0
  50. package/dist/esm/components/scatterplot/SpatialControls.js +140 -0
  51. package/dist/esm/components/scatterplot/Toolbox.js +25 -0
  52. package/dist/esm/components/search-bar/SearchBar.js +74 -0
  53. package/dist/esm/components/search-bar/SearchResults.js +139 -0
  54. package/dist/esm/components/var-list/VarItem.js +250 -0
  55. package/dist/esm/components/var-list/VarList.js +267 -0
  56. package/dist/esm/components/var-list/VarListToolbar.js +84 -0
  57. package/dist/esm/components/var-list/VarSet.js +193 -0
  58. package/dist/esm/components/violin/Violin.js +141 -0
  59. package/dist/esm/components/violin/ViolinControls.js +24 -0
  60. package/dist/esm/constants/colorscales.js +22 -0
  61. package/dist/esm/constants/constants.js +84 -0
  62. package/dist/esm/context/DatasetContext.js +572 -0
  63. package/dist/esm/context/FilterContext.js +48 -0
  64. package/dist/esm/context/ZarrDataContext.js +26 -0
  65. package/dist/esm/helpers/color-helper.js +66 -0
  66. package/dist/esm/helpers/map-helper.js +53 -0
  67. package/dist/esm/helpers/zarr-helper.js +129 -0
  68. package/dist/esm/index.js +22 -0
  69. package/dist/esm/utils/Filter.js +147 -0
  70. package/dist/esm/utils/Histogram.js +44 -0
  71. package/dist/esm/utils/ImageViewer.js +27 -0
  72. package/dist/esm/utils/Legend.js +58 -0
  73. package/dist/esm/utils/LoadingIndicators.js +22 -0
  74. package/dist/esm/utils/VirtualizedList.js +55 -0
  75. package/dist/esm/utils/errors.js +47 -0
  76. package/dist/esm/utils/requests.js +116 -0
  77. package/dist/esm/utils/search.js +39 -0
  78. package/dist/esm/utils/string.js +59 -0
  79. package/dist/esm/utils/zarrData.js +102 -0
  80. package/package.json +16 -5
  81. /package/dist/{components → cjs/components}/dotplot/Dotplot.js +0 -0
  82. /package/dist/{components → cjs/components}/dotplot/DotplotControls.js +0 -0
  83. /package/dist/{components → cjs/components}/heatmap/Heatmap.js +0 -0
  84. /package/dist/{components → cjs/components}/heatmap/HeatmapControls.js +0 -0
  85. /package/dist/{components → cjs/components}/matrixplot/Matrixplot.js +0 -0
  86. /package/dist/{components → cjs/components}/matrixplot/MatrixplotControls.js +0 -0
  87. /package/dist/{components → cjs/components}/obsm-list/ObsmList.js +0 -0
  88. /package/dist/{components → cjs/components}/scatterplot/ScatterplotControls.js +0 -0
  89. /package/dist/{components → cjs/components}/violin/ViolinControls.js +0 -0
  90. /package/dist/{constants → cjs/constants}/colorscales.js +0 -0
  91. /package/dist/{constants → cjs/constants}/constants.js +0 -0
  92. /package/dist/{index.js → cjs/index.js} +0 -0
  93. /package/dist/{utils → cjs/utils}/Filter.js +0 -0
  94. /package/dist/{utils → cjs/utils}/errors.js +0 -0
  95. /package/dist/{utils → cjs/utils}/search.js +0 -0
@@ -0,0 +1,24 @@
1
+ import React from "react";
2
+ import _ from "lodash";
3
+ import { Dropdown, ButtonGroup, ButtonToolbar, InputGroup } from "react-bootstrap";
4
+ import { VIOLINPLOT_SCALES } from "../../constants/constants";
5
+ import { useDataset, useDatasetDispatch } from "../../context/DatasetContext";
6
+ export function ViolinControls() {
7
+ const dataset = useDataset();
8
+ const dispatch = useDatasetDispatch();
9
+ const scaleList = _.values(VIOLINPLOT_SCALES).map(scale => /*#__PURE__*/React.createElement(Dropdown.Item, {
10
+ key: scale.value,
11
+ active: dataset.controls.scale.violinplot === scale,
12
+ onClick: () => {
13
+ dispatch({
14
+ type: "set.controls.scale",
15
+ plot: "violinplot",
16
+ scale: scale
17
+ });
18
+ }
19
+ }, scale.name));
20
+ return /*#__PURE__*/React.createElement(ButtonToolbar, null, /*#__PURE__*/React.createElement(ButtonGroup, null, /*#__PURE__*/React.createElement(InputGroup, null, /*#__PURE__*/React.createElement(InputGroup.Text, null, "Scale"), /*#__PURE__*/React.createElement(Dropdown, null, /*#__PURE__*/React.createElement(Dropdown.Toggle, {
21
+ id: "dropdownStandardScale",
22
+ variant: "light"
23
+ }, dataset.controls.scale.violinplot.name), /*#__PURE__*/React.createElement(Dropdown.Menu, null, scaleList)))));
24
+ }
@@ -0,0 +1,22 @@
1
+ // From plotly https://github.com/plotly/plotly.js/blob/5bc25b490702e5ed61265207833dbd58e8ab27f1/src/components/colorscale/scales.js
2
+ export const COLORSCALES = {
3
+ Greys: ["#000000", "#ffffff"],
4
+ YlGnBu: ["#081d58", "#253494", "#225ea8", "#1d91c0", "#41b6c4", "#7fcdbb", "#c7e9b4", "#edf8d9", "#ffffd9"],
5
+ Greens: ["#00441b", "#006d2c", "#238b45", "#41ab5d", "#74c476", "#a1d9a5", "#c7e9c0", "#e5f5e0", "#f7fcf5"],
6
+ YlOrRd: ["#800026", "#bd0026", "#e31a1c", "#fc4e2a", "#fd8d3c", "#feb24c", "#fed976", "#ffed9f", "#ffffcc"],
7
+ Bluered: ["#0000ff", "#ff0000"],
8
+ RdBu: ["#050aac", "#6a89f7", "#bebebe", "#dcaa84", "#e6915a", "#b20a1c"],
9
+ Reds: ["#dcdcdc", "#f5c39d", "#f5a069", "#b20a1c"],
10
+ Blues: ["#050aac", "#283cba", "#4664f5", "#5a78f5", "#6a89f7", "#dcdcdc"],
11
+ Picnic: ["#0000ff", "#3399ff", "#66ccff", "#99ccff", "#ccccff", "#ffffff", "#ffccff", "#ff99ff", "#ff66cc", "#ff6666", "#ff0000"],
12
+ Rainbow: ["#96005a", "#0000c8", "#0019ff", "#0098ff", "#2cff96", "#97ff00", "#ffe600", "#ff6f00", "#ff0000"],
13
+ Portland: ["#0c3383", "#0a88ba", "#f2d338", "#f28f38", "#d91e1e"],
14
+ Jet: ["#000083", "#003caa", "#05ffff", "#ffff00", "#fa0000", "#800000"],
15
+ Hot: ["#000000", "#e60000", "#ffd200", "#ffffff"],
16
+ Blackbody: ["#000000", "#e60000", "#e6d200", "#ffffff", "#a0c8ff"],
17
+ Earth: ["#000082", "#00b4b4", "#28d228", "#e6e632", "#784614", "#ffffff"],
18
+ Electric: ["#000000", "#1e0064", "#780064", "#a05a00", "#e6c800", "#fffadc"],
19
+ Viridis: ["#440154", "#48186a", "#472d7b", "#424086", "#3b528b", "#33638d", "#2c728e", "#26828e", "#21918c", "#1fa088", "#28ae80", "#3fbc73", "#5ec962", "#84d44b", "#addc30", "#d8e219", "#fde725"],
20
+ Cividis: ["#00204c", "#002a66", "#00346e", "#273f6c", "#3c4a6c", "#4c556b", "#5b5f6d", "#686a70", "#757575", "#838178", "#929c78", "#a19676", "#b0a572", "#c0af6d", "#d1ba65", "#e1c75c", "#f3db4f", "#ffe945"],
21
+ Accent: ["#7fc97f", "#beaed4", "#fdc086", "#ffff99", "#386cb0", "#f0027f", "#bf5b17", "#666666"]
22
+ };
@@ -0,0 +1,84 @@
1
+ export const LOCAL_STORAGE_KEY = "CHERITA";
2
+ export const COLOR_ENCODINGS = {
3
+ VAR: "var",
4
+ OBS: "obs"
5
+ };
6
+ export const OBS_TYPES = {
7
+ CATEGORICAL: "categorical",
8
+ DISCRETE: "discrete",
9
+ CONTINUOUS: "continuous",
10
+ BOOLEAN: "boolean"
11
+ };
12
+ export const VAR_SORT = {
13
+ NONE: null,
14
+ NAME: "name",
15
+ MATRIX: "matrix"
16
+ };
17
+ export const VAR_SORT_ORDER = {
18
+ ASC: "asc",
19
+ DESC: "desc"
20
+ };
21
+ export const SELECTED_POLYGON_FILLCOLOR = [107, 170, 209, 255 / 2];
22
+ export const UNSELECTED_POLYGON_FILLCOLOR = [167, 191, 211, 255 / 3];
23
+ export const SELECTION_MODES = {
24
+ SINGLE: "single",
25
+ MULTIPLE: "multiple"
26
+ };
27
+ export const VIOLIN_MODES = {
28
+ MULTIKEY: "multikey",
29
+ GROUPBY: "groupby"
30
+ };
31
+ export const MATRIXPLOT_SCALES = {
32
+ NONE: {
33
+ value: null,
34
+ name: "None"
35
+ },
36
+ GROUP: {
37
+ value: "group",
38
+ name: "Group"
39
+ },
40
+ VAR: {
41
+ value: "var",
42
+ name: "Var"
43
+ }
44
+ };
45
+ export const DOTPLOT_SCALES = {
46
+ NONE: {
47
+ value: null,
48
+ name: "None"
49
+ },
50
+ GROUP: {
51
+ value: "group",
52
+ name: "Group"
53
+ },
54
+ VAR: {
55
+ value: "var",
56
+ name: "Var"
57
+ }
58
+ };
59
+ export const VIOLINPLOT_SCALES = {
60
+ WIDTH: {
61
+ value: "width",
62
+ name: "Width"
63
+ },
64
+ COUNT: {
65
+ value: "count",
66
+ name: "Count"
67
+ }
68
+ };
69
+ export const PSEUDOSPATIAL_PLOT_TYPES = {
70
+ GENE: "gene",
71
+ CATEGORICAL: "categorical",
72
+ CONTINUOUS: "continuous",
73
+ MASKS: "masks"
74
+ };
75
+ export const PSEUDOSPATIAL_CATEGORICAL_MODES = {
76
+ ACROSS: {
77
+ value: "across",
78
+ name: "% across sections"
79
+ },
80
+ WITHIN: {
81
+ value: "within",
82
+ name: "% within section"
83
+ }
84
+ };
@@ -0,0 +1,572 @@
1
+ import React, { useEffect, createContext, useContext, useReducer } from "react";
2
+ import { createSyncStoragePersister } from "@tanstack/query-sync-storage-persister";
3
+ import { QueryClient, QueryCache } from "@tanstack/react-query";
4
+ import { PersistQueryClientProvider } from "@tanstack/react-query-persist-client";
5
+ import _ from "lodash";
6
+ import { FilterProvider } from "./FilterContext";
7
+ import { ZarrDataProvider } from "./ZarrDataContext";
8
+ import { COLOR_ENCODINGS, DOTPLOT_SCALES, LOCAL_STORAGE_KEY, MATRIXPLOT_SCALES, OBS_TYPES, PSEUDOSPATIAL_CATEGORICAL_MODES, VAR_SORT, VAR_SORT_ORDER, VIOLINPLOT_SCALES } from "../constants/constants";
9
+ export const DatasetContext = /*#__PURE__*/createContext(null);
10
+ export const DatasetDispatchContext = /*#__PURE__*/createContext(null);
11
+ const queryClient = new QueryClient({
12
+ defaultOptions: {
13
+ queries: {
14
+ gcTime: 1000 * 60 * 15,
15
+ // garbage collect after 15min of inactive
16
+ staleTime: 1000 * 60 * 60,
17
+ refetchOnMount: false,
18
+ refetchOnReconnect: false,
19
+ refetchOnWindowFocus: false
20
+ }
21
+ },
22
+ queryCache: new QueryCache({
23
+ onError: error => {
24
+ console.error(error);
25
+ }
26
+ })
27
+ });
28
+ // Type of queries to store responses
29
+ const persistKeys = ["obs/cols", "var/names", "obsm/keys", "obs/bins", "obs/distribution"];
30
+ const persistOptions = {
31
+ persister: createSyncStoragePersister({
32
+ storage: window.localStorage
33
+ }),
34
+ dehydrateOptions: {
35
+ shouldDehydrateQuery: _ref => {
36
+ let {
37
+ queryKey,
38
+ state
39
+ } = _ref;
40
+ if (state.status === "success") {
41
+ return persistKeys.includes(queryKey?.[0]);
42
+ }
43
+ return false;
44
+ }
45
+ }
46
+ // @TODO: add maxAge and buster (app and api version numbers as busters)
47
+ };
48
+ const initialDataset = {
49
+ varNamesCol: null,
50
+ selectedObs: null,
51
+ selectedObsm: null,
52
+ selectedVar: null,
53
+ selectedMultiObs: [],
54
+ selectedMultiVar: [],
55
+ colorEncoding: null,
56
+ labelObs: [],
57
+ varSets: [],
58
+ sliceBy: {
59
+ obs: false,
60
+ polygons: false
61
+ },
62
+ controls: {
63
+ colorScale: "Viridis",
64
+ valueRange: [0, 1],
65
+ range: [0, 1],
66
+ colorAxis: {
67
+ dmin: 0,
68
+ dmax: 1,
69
+ cmin: 0,
70
+ cmax: 1
71
+ },
72
+ scale: {
73
+ dotplot: DOTPLOT_SCALES.NONE,
74
+ matrixplot: MATRIXPLOT_SCALES.NONE,
75
+ violinplot: VIOLINPLOT_SCALES.WIDTH
76
+ },
77
+ meanOnlyExpressed: false,
78
+ expressionCutoff: 0.0
79
+ },
80
+ diseaseDatasets: [],
81
+ selectedDisease: null,
82
+ varSort: {
83
+ var: {
84
+ sort: VAR_SORT.NONE,
85
+ sortOrder: VAR_SORT_ORDER.ASC
86
+ },
87
+ disease: {
88
+ sort: VAR_SORT.NONE,
89
+ sortOrder: VAR_SORT_ORDER.ASC
90
+ }
91
+ },
92
+ obsCols: null,
93
+ // @TODO: implement specifying groups/categories for dropdowns
94
+ imageUrl: null,
95
+ pseudospatial: {
96
+ maskSet: null,
97
+ maskValues: null,
98
+ categoricalMode: PSEUDOSPATIAL_CATEGORICAL_MODES.ACROSS.value
99
+ },
100
+ polygons: {}
101
+ };
102
+ const initializer = initialState => {
103
+ const localObj = (JSON.parse(localStorage.getItem(LOCAL_STORAGE_KEY)) || {})[initialState.url] || {};
104
+ const keys = _.keys(initialState);
105
+ const localValues = _.pick(localObj, keys);
106
+ return _.assign(initialState, localValues);
107
+ };
108
+ export function DatasetProvider(_ref2) {
109
+ let {
110
+ dataset_url,
111
+ children,
112
+ ...dataset_params
113
+ } = _ref2;
114
+ const [dataset, dispatch] = useReducer(datasetReducer, _.assign(initializer({
115
+ url: dataset_url,
116
+ ...initialDataset
117
+ }), dataset_params));
118
+ useEffect(() => {
119
+ try {
120
+ const localObj = JSON.parse(localStorage.getItem(LOCAL_STORAGE_KEY)) || {};
121
+ localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify({
122
+ ...localObj,
123
+ [dataset.url]: dataset
124
+ }));
125
+ } catch (err) {
126
+ if (err.code === 22 || err.code === 1014 || err.name === "QuotaExceededError" || err.name === "NS_ERROR_DOM_QUOTA_REACHED") {
127
+ console.err("Browser storage quota exceeded");
128
+ } else {
129
+ console.err(err);
130
+ }
131
+ }
132
+ }, [dataset]);
133
+ return /*#__PURE__*/React.createElement(DatasetContext.Provider, {
134
+ value: dataset
135
+ }, /*#__PURE__*/React.createElement(DatasetDispatchContext.Provider, {
136
+ value: dispatch
137
+ }, /*#__PURE__*/React.createElement(PersistQueryClientProvider, {
138
+ client: queryClient,
139
+ persistOptions: persistOptions
140
+ }, /*#__PURE__*/React.createElement(FilterProvider, null, /*#__PURE__*/React.createElement(ZarrDataProvider, null, children)))));
141
+ }
142
+ export function useDataset() {
143
+ return useContext(DatasetContext);
144
+ }
145
+ export function useDatasetDispatch() {
146
+ return useContext(DatasetDispatchContext);
147
+ }
148
+ function datasetReducer(dataset, action) {
149
+ switch (action.type) {
150
+ case "select.obs":
151
+ {
152
+ return {
153
+ ...dataset,
154
+ selectedObs: action.obs,
155
+ controls: {
156
+ ...dataset.controls,
157
+ range: action.obs?.type === OBS_TYPES.CATEGORICAL ? [0, 1] : dataset.controls.range
158
+ },
159
+ colorEncoding: dataset.colorEncoding === COLOR_ENCODINGS.OBS && !action.obs ? null : dataset.colorEncoding,
160
+ sliceBy: {
161
+ ...dataset.sliceBy,
162
+ obs: action.obs ? dataset.sliceBy.obs : false
163
+ }
164
+ };
165
+ }
166
+ case "select.obsm":
167
+ {
168
+ return {
169
+ ...dataset,
170
+ selectedObsm: action.obsm
171
+ };
172
+ }
173
+ case "select.var":
174
+ {
175
+ return {
176
+ ...dataset,
177
+ selectedVar: action.var
178
+ };
179
+ }
180
+ case "select.multivar":
181
+ {
182
+ if (dataset.selectedMultiVar.find(i => action.var.isSet ? i.name === action.var.name : i.matrix_index === action.var.matrix_index)) {
183
+ return dataset;
184
+ } else {
185
+ return {
186
+ ...dataset,
187
+ selectedMultiVar: [...dataset.selectedMultiVar, action.var]
188
+ };
189
+ }
190
+ }
191
+ case "deselect.multivar":
192
+ {
193
+ return {
194
+ ...dataset,
195
+ selectedMultiVar: dataset.selectedMultiVar.filter(a => action.var.isSet ? a.name !== action.var.name : a.matrix_index !== action.var.matrix_index)
196
+ };
197
+ }
198
+ case "update.multivar":
199
+ {
200
+ return {
201
+ ...dataset,
202
+ selelectedMultiVar: dataset.selectedMultiVar.map(i => {
203
+ if (i.isSet) {
204
+ return action.vars.find(s => s.name === i.name);
205
+ }
206
+ return i;
207
+ })
208
+ };
209
+ }
210
+ case "set.colorEncoding":
211
+ {
212
+ return {
213
+ ...dataset,
214
+ colorEncoding: action.value
215
+ };
216
+ }
217
+ case "reset.multiVar":
218
+ {
219
+ return {
220
+ ...dataset,
221
+ selectedMultiVar: [],
222
+ colorEncoding: dataset.colorEncoding === COLOR_ENCODINGS.VAR ? null : dataset.colorEncoding
223
+ };
224
+ }
225
+ case "reset.var":
226
+ {
227
+ return {
228
+ ...dataset,
229
+ selectedVar: null,
230
+ colorEncoding: dataset.colorEncoding === COLOR_ENCODINGS.VAR ? null : dataset.colorEncoding
231
+ };
232
+ }
233
+ case "add.varSet":
234
+ {
235
+ return {
236
+ ...dataset,
237
+ varSets: [...dataset.varSets, action.varSet]
238
+ };
239
+ }
240
+ case "remove.varSet":
241
+ {
242
+ return {
243
+ ...dataset,
244
+ varSets: dataset.varSets.filter(a => a.name !== action.varSet.name)
245
+ };
246
+ }
247
+ case "reset.varSets":
248
+ {
249
+ return {
250
+ ...dataset,
251
+ varSets: []
252
+ };
253
+ }
254
+ case "add.varSet.var":
255
+ {
256
+ const varSet = dataset.varSets.find(s => s.name === action.varSet.name);
257
+ if (varSet.vars.find(v => _.isEqual(v, action.var))) {
258
+ return dataset;
259
+ } else {
260
+ return {
261
+ ...dataset,
262
+ varSets: dataset.varSets.map(s => {
263
+ if (s.name === varSet.name) {
264
+ return {
265
+ ...s,
266
+ vars: [...s.vars, action.var]
267
+ };
268
+ } else {
269
+ return s;
270
+ }
271
+ })
272
+ };
273
+ }
274
+ }
275
+ case "remove.varSet.var":
276
+ {
277
+ const varSet = dataset.varSets.find(s => s.name === action.varSet.name);
278
+ return {
279
+ ...dataset,
280
+ varSets: dataset.varSets.map(s => {
281
+ if (s.name === varSet.name) {
282
+ return {
283
+ ...s,
284
+ vars: s.vars.filter(v => v.name !== action.var.name)
285
+ };
286
+ } else {
287
+ return s;
288
+ }
289
+ })
290
+ };
291
+ }
292
+ case "select.disease":
293
+ {
294
+ return {
295
+ ...dataset,
296
+ selectedDisease: {
297
+ id: action.id,
298
+ name: action.name
299
+ }
300
+ };
301
+ }
302
+ case "reset.disease":
303
+ {
304
+ return {
305
+ ...dataset,
306
+ selectedDisease: null
307
+ };
308
+ }
309
+ case "set.controls.colorScale":
310
+ {
311
+ return {
312
+ ...dataset,
313
+ controls: {
314
+ ...dataset.controls,
315
+ colorScale: action.colorScale
316
+ }
317
+ };
318
+ }
319
+ case "set.controls.valueRange":
320
+ {
321
+ return {
322
+ ...dataset,
323
+ controls: {
324
+ ...dataset.controls,
325
+ valueRange: action.valueRange
326
+ }
327
+ };
328
+ }
329
+ case "set.controls.range":
330
+ {
331
+ return {
332
+ ...dataset,
333
+ controls: {
334
+ ...dataset.controls,
335
+ range: action.range
336
+ }
337
+ };
338
+ }
339
+ case "set.controls.colorAxis":
340
+ {
341
+ return {
342
+ ...dataset,
343
+ controls: {
344
+ ...dataset.controls,
345
+ colorAxis: action.colorAxis
346
+ }
347
+ };
348
+ }
349
+ case "set.controls.colorAxis.crange":
350
+ {
351
+ return {
352
+ ...dataset,
353
+ controls: {
354
+ ...dataset.controls,
355
+ colorAxis: {
356
+ ...dataset.controls.colorAxis,
357
+ cmin: action.cmin,
358
+ cmax: action.cmax
359
+ }
360
+ }
361
+ };
362
+ }
363
+ case "set.controls.colorAxis.cmin":
364
+ {
365
+ return {
366
+ ...dataset,
367
+ controls: {
368
+ ...dataset.controls,
369
+ colorAxis: {
370
+ ...dataset.controls.colorAxis,
371
+ cmin: action.cmin
372
+ }
373
+ }
374
+ };
375
+ }
376
+ case "set.controls.colorAxis.cmax":
377
+ {
378
+ return {
379
+ ...dataset,
380
+ controls: {
381
+ ...dataset.controls,
382
+ colorAxis: {
383
+ ...dataset.controls.colorAxis,
384
+ cmax: action.cmax
385
+ }
386
+ }
387
+ };
388
+ }
389
+ case "set.controls.scale":
390
+ {
391
+ return {
392
+ ...dataset,
393
+ controls: {
394
+ ...dataset.controls,
395
+ scale: {
396
+ ...dataset.controls.scale,
397
+ [action.plot]: action.scale
398
+ }
399
+ }
400
+ };
401
+ }
402
+ case "set.controls.meanOnlyExpressed":
403
+ {
404
+ return {
405
+ ...dataset,
406
+ controls: {
407
+ ...dataset.controls,
408
+ meanOnlyExpressed: action.meanOnlyExpressed
409
+ }
410
+ };
411
+ }
412
+ case "set.controls.expressionCutoff":
413
+ {
414
+ return {
415
+ ...dataset,
416
+ controls: {
417
+ ...dataset.controls,
418
+ expressionCutoff: action.expressionCutoff
419
+ }
420
+ };
421
+ }
422
+ case "toggle.slice.obs":
423
+ {
424
+ if (_.isEqual(dataset.selectedObs, action.obs)) {
425
+ return {
426
+ ...dataset,
427
+ sliceBy: {
428
+ ...dataset.sliceBy,
429
+ obs: !dataset.sliceBy.obs
430
+ }
431
+ };
432
+ } else {
433
+ return {
434
+ ...dataset,
435
+ selectedObs: action.obs,
436
+ sliceBy: {
437
+ ...dataset.sliceBy,
438
+ obs: true
439
+ }
440
+ };
441
+ }
442
+ }
443
+ case "toggle.slice.polygons":
444
+ {
445
+ return {
446
+ ...dataset,
447
+ sliceBy: {
448
+ ...dataset.sliceBy,
449
+ polygons: !dataset.sliceBy.polygons
450
+ }
451
+ };
452
+ }
453
+ case "disable.slice.polygons":
454
+ {
455
+ return {
456
+ ...dataset,
457
+ sliceBy: {
458
+ ...dataset.sliceBy,
459
+ polygons: false
460
+ }
461
+ };
462
+ }
463
+ case "add.label.obs":
464
+ {
465
+ if (dataset.labelObs.find(i => _.isEqual(i, action.obs))) {
466
+ return dataset;
467
+ } else {
468
+ return {
469
+ ...dataset,
470
+ labelObs: [...dataset.labelObs, action.obs]
471
+ };
472
+ }
473
+ }
474
+ case "remove.label.obs":
475
+ {
476
+ return {
477
+ ...dataset,
478
+ labelObs: dataset.labelObs.filter(a => a.name !== action.obsName)
479
+ };
480
+ }
481
+ case "reset.label.obs":
482
+ {
483
+ return {
484
+ ...dataset,
485
+ labelObs: []
486
+ };
487
+ }
488
+ case "set.varSort":
489
+ {
490
+ return {
491
+ ...dataset,
492
+ varSort: {
493
+ ...dataset.varSort,
494
+ [action.var]: {
495
+ sort: action.sort,
496
+ sortOrder: action.sortOrder
497
+ }
498
+ }
499
+ };
500
+ }
501
+ case "set.varSort.sort":
502
+ {
503
+ return {
504
+ ...dataset,
505
+ varSort: {
506
+ ...dataset.varSort,
507
+ [action.var]: {
508
+ ...dataset.varSort[action.var],
509
+ sort: action.sort
510
+ }
511
+ }
512
+ };
513
+ }
514
+ case "set.varSort.sortOrder":
515
+ {
516
+ return {
517
+ ...dataset,
518
+ varSort: {
519
+ ...dataset.varSort,
520
+ [action.var]: {
521
+ ...dataset.varSort[action.var],
522
+ sortOrder: action.sortOrder
523
+ }
524
+ }
525
+ };
526
+ }
527
+ case "set.polygons":
528
+ {
529
+ return {
530
+ ...dataset,
531
+ polygons: {
532
+ ...dataset.polygons,
533
+ [action.obsm]: action.polygons
534
+ }
535
+ };
536
+ }
537
+ case "set.pseudospatial.maskSet":
538
+ {
539
+ return {
540
+ ...dataset,
541
+ pseudospatial: {
542
+ ...dataset.pseudospatial,
543
+ maskSet: action.maskSet
544
+ }
545
+ };
546
+ }
547
+ case "set.pseudospatial.maskValues":
548
+ {
549
+ return {
550
+ ...dataset,
551
+ pseudospatial: {
552
+ ...dataset.pseudospatial,
553
+ maskValues: action.maskValues
554
+ }
555
+ };
556
+ }
557
+ case "set.pseudospatial.categoricalMode":
558
+ {
559
+ return {
560
+ ...dataset,
561
+ pseudospatial: {
562
+ ...dataset.pseudospatial,
563
+ categoricalMode: action.categoricalMode
564
+ }
565
+ };
566
+ }
567
+ default:
568
+ {
569
+ throw Error("Unknown action: " + action.type);
570
+ }
571
+ }
572
+ }