@haniffalab/cherita-react 0.2.0-dev.2024-12-16.67617f27 → 0.2.0-dev.2025-01-17.f9a0f419

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.
@@ -19,8 +19,10 @@ function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e;
19
19
  function Dotplot() {
20
20
  const ENDPOINT = "dotplot";
21
21
  const dataset = (0, _DatasetContext.useDataset)();
22
- const filteredData = (0, _FilterContext.useFilteredData)();
23
- const isSliced = dataset.sliceBy.obs || dataset.sliceBy.polygons;
22
+ const {
23
+ obsIndices,
24
+ isSliced
25
+ } = (0, _FilterContext.useFilteredData)();
24
26
  const dispatch = (0, _DatasetContext.useDatasetDispatch)();
25
27
  const colorscale = (0, _react.useRef)(dataset.controls.colorScale);
26
28
  const [data, setData] = (0, _react.useState)([]);
@@ -34,7 +36,7 @@ function Dotplot() {
34
36
  name: i.name,
35
37
  indices: i.vars.map(v => v.index)
36
38
  } : i.index),
37
- obsIndices: isSliced ? [...(filteredData.obsIndices || [])] : null,
39
+ obsIndices: isSliced ? [...(obsIndices || [])] : null,
38
40
  standardScale: dataset.controls.standardScale,
39
41
  meanOnlyExpressed: dataset.controls.meanOnlyExpressed,
40
42
  expressionCutoff: dataset.controls.expressionCutoff,
@@ -58,14 +60,14 @@ function Dotplot() {
58
60
  name: i.name,
59
61
  indices: i.vars.map(v => v.index)
60
62
  } : i.index),
61
- obsIndices: isSliced ? [...(filteredData.obsIndices || [])] : null,
63
+ obsIndices: isSliced ? [...(obsIndices || [])] : null,
62
64
  standardScale: dataset.controls.standardScale,
63
65
  meanOnlyExpressed: dataset.controls.meanOnlyExpressed,
64
66
  expressionCutoff: dataset.controls.expressionCutoff,
65
67
  varNamesCol: dataset.varNamesCol
66
68
  };
67
69
  });
68
- }, [dataset.url, dataset.selectedObs, dataset.selectedMultiVar, dataset.controls.standardScale, dataset.controls.meanOnlyExpressed, dataset.controls.expressionCutoff, dataset.varNamesCol, isSliced, filteredData.obsIndices]);
70
+ }, [dataset.url, dataset.selectedObs, dataset.selectedMultiVar, dataset.controls.standardScale, dataset.controls.meanOnlyExpressed, dataset.controls.expressionCutoff, dataset.varNamesCol, isSliced, obsIndices]);
69
71
  const updateColorscale = (0, _react.useCallback)(colorscale => {
70
72
  setLayout(l => {
71
73
  return {
@@ -43,14 +43,27 @@ function FullPage(_ref) {
43
43
  (0, _react.useLayoutEffect)(() => {
44
44
  function updateDimensions() {
45
45
  if (targetRef.current) {
46
+ // Log the full viewport height
47
+ console.log("Full viewport height:", window.innerHeight);
48
+
49
+ // Get the distance from the top of the page to the target element
50
+ const rect = targetRef.current.getBoundingClientRect();
51
+ const distanceFromTop = rect.top + window.scrollY;
52
+ console.log("Distance from top of the page:", distanceFromTop);
53
+
54
+ // Calculate the available height for the Cherita app
55
+ const availableHeight = window.innerHeight - distanceFromTop;
56
+ console.log("Available height for Cherita app:", availableHeight);
57
+
58
+ // Update the dimensions to fit the viewport minus the navbar height
46
59
  setDimensions({
47
60
  width: targetRef.current.offsetWidth,
48
- height: window.innerHeight - targetRef.current.offsetTop
61
+ height: availableHeight
49
62
  });
50
63
  }
51
64
  }
52
65
  window.addEventListener("resize", updateDimensions);
53
- updateDimensions();
66
+ updateDimensions(); // Initial update
54
67
  return () => window.removeEventListener("resize", updateDimensions);
55
68
  }, []);
56
69
  return /*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
@@ -19,8 +19,10 @@ function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e;
19
19
  function Heatmap() {
20
20
  const ENDPOINT = "heatmap";
21
21
  const dataset = (0, _DatasetContext.useDataset)();
22
- const filteredData = (0, _FilterContext.useFilteredData)();
23
- const isSliced = dataset.sliceBy.obs || dataset.sliceBy.polygons;
22
+ const {
23
+ obsIndices,
24
+ isSliced
25
+ } = (0, _FilterContext.useFilteredData)();
24
26
  const colorscale = (0, _react.useRef)(dataset.controls.colorScale);
25
27
  const [data, setData] = (0, _react.useState)([]);
26
28
  const [layout, setLayout] = (0, _react.useState)({});
@@ -33,7 +35,7 @@ function Heatmap() {
33
35
  name: i.name,
34
36
  indices: i.vars.map(v => v.index)
35
37
  } : i.index),
36
- obsIndices: isSliced ? [...(filteredData.obsIndices || [])] : null,
38
+ obsIndices: isSliced ? [...(obsIndices || [])] : null,
37
39
  varNamesCol: dataset.varNamesCol
38
40
  });
39
41
  (0, _react.useEffect)(() => {
@@ -52,11 +54,11 @@ function Heatmap() {
52
54
  name: i.name,
53
55
  indices: i.vars.map(v => v.index)
54
56
  } : i.index),
55
- obsIndices: isSliced ? [...(filteredData.obsIndices || [])] : null,
57
+ obsIndices: isSliced ? [...(obsIndices || [])] : null,
56
58
  varNamesCol: dataset.varNamesCol
57
59
  };
58
60
  });
59
- }, [dataset.selectedMultiVar, dataset.selectedObs, dataset.url, dataset.varNamesCol, filteredData.obsIndices, isSliced]);
61
+ }, [dataset.selectedMultiVar, dataset.selectedObs, dataset.url, dataset.varNamesCol, obsIndices, isSliced]);
60
62
  const updateColorscale = (0, _react.useCallback)(colorscale => {
61
63
  setLayout(l => {
62
64
  return {
@@ -19,8 +19,10 @@ function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e;
19
19
  function Matrixplot() {
20
20
  const ENDPOINT = "matrixplot";
21
21
  const dataset = (0, _DatasetContext.useDataset)();
22
- const filteredData = (0, _FilterContext.useFilteredData)();
23
- const isSliced = dataset.sliceBy.obs || dataset.sliceBy.polygons;
22
+ const {
23
+ obsIndices,
24
+ isSliced
25
+ } = (0, _FilterContext.useFilteredData)();
24
26
  const colorscale = (0, _react.useRef)(dataset.controls.colorScale);
25
27
  const [data, setData] = (0, _react.useState)([]);
26
28
  const [layout, setLayout] = (0, _react.useState)({});
@@ -33,7 +35,7 @@ function Matrixplot() {
33
35
  name: i.name,
34
36
  indices: i.vars.map(v => v.index)
35
37
  } : i.index),
36
- obsIndices: isSliced ? [...(filteredData.obsIndices || [])] : null,
38
+ obsIndices: isSliced ? [...(obsIndices || [])] : null,
37
39
  standardScale: dataset.controls.standardScale,
38
40
  varNamesCol: dataset.varNamesCol
39
41
  });
@@ -53,12 +55,12 @@ function Matrixplot() {
53
55
  name: i.name,
54
56
  indices: i.vars.map(v => v.index)
55
57
  } : i.index),
56
- obsIndices: isSliced ? [...(filteredData.obsIndices || [])] : null,
58
+ obsIndices: isSliced ? [...(obsIndices || [])] : null,
57
59
  standardScale: dataset.controls.standardScale,
58
60
  varNamesCol: dataset.varNamesCol
59
61
  };
60
62
  });
61
- }, [dataset.controls.standardScale, dataset.selectedMultiVar, dataset.selectedObs, dataset.url, dataset.varNamesCol, filteredData.obsIndices, isSliced]);
63
+ }, [dataset.controls.standardScale, dataset.selectedMultiVar, dataset.selectedObs, dataset.url, dataset.varNamesCol, obsIndices, isSliced]);
62
64
  const updateColorscale = (0, _react.useCallback)(colorscale => {
63
65
  setLayout(l => {
64
66
  return {
@@ -48,8 +48,10 @@ function getContinuousLabel(code, binEdges) {
48
48
  const useObsHistogram = obs => {
49
49
  const ENDPOINT = "obs/histograms";
50
50
  const dataset = (0, _DatasetContext.useDataset)();
51
- const filteredData = (0, _FilterContext.useFilteredData)();
52
- const isSliced = dataset.sliceBy.obs || dataset.sliceBy.polygons;
51
+ const {
52
+ obsIndices,
53
+ isSliced
54
+ } = (0, _FilterContext.useFilteredData)();
53
55
  const [params, setParams] = (0, _react.useState)({
54
56
  url: dataset.url,
55
57
  obsCol: _lodash.default.omit(obs, "omit"),
@@ -58,7 +60,7 @@ const useObsHistogram = obs => {
58
60
  name: dataset.selectedVar?.name,
59
61
  indices: dataset.selectedVar?.vars.map(v => v.index)
60
62
  } : dataset.selectedVar?.index,
61
- obsIndices: isSliced ? [...(filteredData.obsIndices || [])] : null
63
+ obsIndices: isSliced ? [...(obsIndices || [])] : null
62
64
  });
63
65
  (0, _react.useEffect)(() => {
64
66
  setParams(p => {
@@ -69,10 +71,10 @@ const useObsHistogram = obs => {
69
71
  name: dataset.selectedVar?.name,
70
72
  indices: dataset.selectedVar?.vars.map(v => v.index)
71
73
  } : dataset.selectedVar?.index,
72
- obsIndices: isSliced ? [...(filteredData.obsIndices || [])] : null
74
+ obsIndices: isSliced ? [...(obsIndices || [])] : null
73
75
  };
74
76
  });
75
- }, [dataset.selectedVar?.index, dataset.selectedVar?.isSet, dataset.selectedVar?.name, dataset.selectedVar?.vars, filteredData.obsIndices, isSliced, obs]);
77
+ }, [dataset.selectedVar?.index, dataset.selectedVar?.isSet, dataset.selectedVar?.name, dataset.selectedVar?.vars, obsIndices, isSliced, obs]);
76
78
  return (0, _requests.useFetch)(ENDPOINT, params, {
77
79
  enabled: !!dataset.selectedVar && dataset.colorEncoding === _constants.COLOR_ENCODINGS.VAR,
78
80
  refetchOnMount: false
@@ -189,11 +191,13 @@ function CategoricalObs(_ref2) {
189
191
  showColor = true
190
192
  } = _ref2;
191
193
  const dataset = (0, _DatasetContext.useDataset)();
194
+ const {
195
+ isSliced
196
+ } = (0, _FilterContext.useFilteredData)();
192
197
  const dispatch = (0, _DatasetContext.useDatasetDispatch)();
193
198
  const totalCounts = _lodash.default.sum(_lodash.default.values(obs.value_counts));
194
199
  const min = _lodash.default.min(_lodash.default.values(obs.codes));
195
200
  const max = _lodash.default.max(_lodash.default.values(obs.codes));
196
- const isSliced = dataset.sliceBy.obs || dataset.sliceBy.polygons;
197
201
  const obsHistograms = useObsHistogram(obs);
198
202
  (0, _react.useEffect)(() => {
199
203
  if (dataset.selectedObs?.name === obs.name) {
@@ -25,19 +25,21 @@ function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e;
25
25
  function usePseudospatialData(plotType) {
26
26
  const ENDPOINT = "pseudospatial";
27
27
  const dataset = (0, _DatasetContext.useDataset)();
28
- const filteredData = (0, _FilterContext.useFilteredData)();
29
- const isSliced = dataset.sliceBy.obs || dataset.sliceBy.polygons;
28
+ const {
29
+ obsIndices,
30
+ isSliced
31
+ } = (0, _FilterContext.useFilteredData)();
30
32
  const baseParams = (0, _react.useMemo)(() => {
31
33
  return {
32
34
  url: dataset.url,
33
35
  maskSet: dataset.pseudospatial.maskSet,
34
36
  maskValues: dataset.pseudospatial.maskValues,
35
- obsIndices: isSliced ? [...(filteredData.obsIndices || [])] : null,
37
+ obsIndices: isSliced ? [...(obsIndices || [])] : null,
36
38
  varNamesCol: dataset.varNamesCol,
37
39
  showColorbar: false,
38
40
  format: "json"
39
41
  };
40
- }, [dataset.url, dataset.pseudospatial.maskSet, dataset.pseudospatial.maskValues, dataset.varNamesCol, isSliced, filteredData.obsIndices]);
42
+ }, [dataset.url, dataset.pseudospatial.maskSet, dataset.pseudospatial.maskValues, dataset.varNamesCol, isSliced, obsIndices]);
41
43
  const getPlotParams = (0, _react.useCallback)(() => {
42
44
  if (plotType === _constants.PSEUDOSPATIAL_PLOT_TYPES.GENE) {
43
45
  return {
@@ -191,7 +193,8 @@ function Pseudospatial(_ref) {
191
193
  }
192
194
  }), hasSelections && showLegend && /*#__PURE__*/(0, _jsxRuntime.jsx)(_Legend.Legend, {
193
195
  min: layout?.coloraxis?.cmin,
194
- max: layout?.coloraxis?.cmax
196
+ max: layout?.coloraxis?.cmax,
197
+ addText: plotType === _constants.PSEUDOSPATIAL_PLOT_TYPES.GENE ? " - Mean expression" : plotType === _constants.PSEUDOSPATIAL_PLOT_TYPES.CATEGORICAL ? " - %" : plotType === _constants.PSEUDOSPATIAL_PLOT_TYPES.CONTINUOUS ? " - Mean value" : ""
195
198
  })]
196
199
  })]
197
200
  });
@@ -17,9 +17,10 @@ var _SpatialControls = require("./SpatialControls");
17
17
  var _Toolbox = require("./Toolbox");
18
18
  var _constants = require("../../constants/constants");
19
19
  var _DatasetContext = require("../../context/DatasetContext");
20
+ var _FilterContext = require("../../context/FilterContext");
21
+ var _ZarrDataContext = require("../../context/ZarrDataContext");
20
22
  var _colorHelper = require("../../helpers/color-helper");
21
23
  var _mapHelper = require("../../helpers/map-helper");
22
- var _Filter = require("../../utils/Filter");
23
24
  var _Legend = require("../../utils/Legend");
24
25
  var _LoadingIndicators = require("../../utils/LoadingIndicators");
25
26
  var _string = require("../../utils/string");
@@ -42,6 +43,12 @@ function Scatterplot(_ref) {
42
43
  radius = 30
43
44
  } = _ref;
44
45
  const dataset = (0, _DatasetContext.useDataset)();
46
+ const {
47
+ obsIndices,
48
+ valueMin,
49
+ valueMax,
50
+ slicedLength
51
+ } = (0, _FilterContext.useFilteredData)();
45
52
  const dispatch = (0, _DatasetContext.useDatasetDispatch)();
46
53
  const {
47
54
  getColor
@@ -60,21 +67,17 @@ function Scatterplot(_ref) {
60
67
  const [mode, setMode] = (0, _react.useState)(() => _editModes.ViewMode);
61
68
  const [features, setFeatures] = (0, _react.useState)({
62
69
  type: "FeatureCollection",
63
- features: []
70
+ features: dataset.polygons[dataset.selectedObsm] || []
64
71
  });
65
72
  const [selectedFeatureIndexes, setSelectedFeatureIndexes] = (0, _react.useState)([]);
66
- const obsmData = (0, _zarrData.useObsmData)();
67
- const xData = (0, _zarrData.useXData)();
68
- const obsData = (0, _zarrData.useObsData)();
73
+ const {
74
+ obsmData,
75
+ xData,
76
+ obsData
77
+ } = (0, _ZarrDataContext.useZarrData)();
69
78
  const labelObsData = (0, _zarrData.useLabelObsData)();
70
79
  // @TODO: assert length of obsmData, xData, obsData is equal
71
80
 
72
- const {
73
- filteredIndices,
74
- valueMin,
75
- valueMax,
76
- slicedLength
77
- } = (0, _Filter.useFilter)(data, features);
78
81
  (0, _react.useEffect)(() => {
79
82
  if (!obsmData.isPending && !obsmData.serverError) {
80
83
  setIsRendering(true);
@@ -207,22 +210,22 @@ function Scatterplot(_ref) {
207
210
  let {
208
211
  index
209
212
  } = _ref2;
210
- const grayOut = filteredIndices && !filteredIndices.has(index);
213
+ const grayOut = obsIndices && !obsIndices.has(index);
211
214
  return getColor({
212
215
  value: (data.values[index] - min) / (max - min),
213
216
  categorical: isCategorical,
214
217
  grayOut: grayOut
215
218
  });
216
- }, [data.values, filteredIndices, getColor, isCategorical, max, min]);
219
+ }, [data.values, obsIndices, getColor, isCategorical, max, min]);
217
220
 
218
221
  // @TODO: add support for pseudospatial hover to reflect in radius
219
222
  const getRadius = (0, _react.useCallback)((_d, _ref3) => {
220
223
  let {
221
224
  index
222
225
  } = _ref3;
223
- const grayOut = filteredIndices && !filteredIndices.has(index);
226
+ const grayOut = obsIndices && !obsIndices.has(index);
224
227
  return grayOut ? 1 : 3;
225
- }, [filteredIndices]);
228
+ }, [obsIndices]);
226
229
  const memoizedLayers = (0, _react.useMemo)(() => {
227
230
  return [new _layers.ScatterplotLayer({
228
231
  id: "cherita-layer-scatterplot",
@@ -281,6 +284,13 @@ function Scatterplot(_ref) {
281
284
  });
282
285
  }
283
286
  }, [dispatch, features?.features?.length]);
287
+ (0, _react.useEffect)(() => {
288
+ dispatch({
289
+ type: "set.polygons",
290
+ obsm: dataset.selectedObsm,
291
+ polygons: features?.features || []
292
+ });
293
+ }, [dataset.selectedObsm, dispatch, features.features]);
284
294
  function onLayerClick(info) {
285
295
  if (mode !== _editModes.ViewMode) {
286
296
  // don't change selection while editing
@@ -301,7 +311,7 @@ function Scatterplot(_ref) {
301
311
  object,
302
312
  index
303
313
  } = _ref5;
304
- if (!object) return;
314
+ if (!object || object?.type === "Feature") return;
305
315
  const text = [];
306
316
  if (dataset.colorEncoding === _constants.COLOR_ENCODINGS.OBS && dataset.selectedObs && !_lodash.default.some(dataset.labelObs, {
307
317
  name: dataset.selectedObs.name
@@ -318,7 +328,7 @@ function Scatterplot(_ref) {
318
328
  }));
319
329
  }
320
330
  if (!text.length) return;
321
- const grayOut = filteredIndices && !filteredIndices.has(index);
331
+ const grayOut = obsIndices && !obsIndices.has(index);
322
332
  return {
323
333
  text: text.length ? _lodash.default.compact(text).join("\n") : null,
324
334
  className: grayOut ? "tooltip-grayout" : "deck-tooltip",
@@ -27,21 +27,25 @@ function VarHistogram(_ref) {
27
27
  } = _ref;
28
28
  const ENDPOINT = "var/histograms";
29
29
  const dataset = (0, _DatasetContext.useDataset)();
30
- const filteredData = (0, _FilterContext.useFilteredData)();
30
+ const {
31
+ obsIndices
32
+ } = (0, _FilterContext.useFilteredData)();
33
+ // @TODO: consider using Filter's isSliced; would trigger more re-renders/requests
34
+ // const { obsIndices, isSliced } = useFilteredData();
31
35
  const isSliced = dataset.sliceBy.obs || dataset.sliceBy.polygons;
32
36
  const [params, setParams] = (0, _react.useState)({
33
37
  url: dataset.url,
34
38
  varKey: item.matrix_index,
35
- obsIndices: isSliced ? [...(filteredData.obsIndices || [])] : null
39
+ obsIndices: isSliced ? [...(obsIndices || [])] : null
36
40
  });
37
41
  (0, _react.useEffect)(() => {
38
42
  setParams(p => {
39
43
  return {
40
44
  ...p,
41
- obsIndices: isSliced ? [...(filteredData.obsIndices || [])] : null
45
+ obsIndices: isSliced ? [...(obsIndices || [])] : null
42
46
  };
43
47
  });
44
- }, [filteredData.obsIndices, isSliced]);
48
+ }, [obsIndices, isSliced]);
45
49
  const {
46
50
  fetchedData,
47
51
  isPending,
@@ -30,6 +30,7 @@ const useVarMean = function (varKeys) {
30
30
  name: v.name,
31
31
  indices: v.vars.map(v => v.index)
32
32
  } : v.index),
33
+ // obsIndices:
33
34
  varNamesCol: dataset.varNamesCol
34
35
  });
35
36
  (0, _react.useEffect)(() => {
@@ -25,8 +25,10 @@ function Violin(_ref) {
25
25
  } = _ref;
26
26
  const ENDPOINT = "violin";
27
27
  const dataset = (0, _DatasetContext.useDataset)();
28
- const filteredData = (0, _FilterContext.useFilteredData)();
29
- const isSliced = dataset.sliceBy.obs || dataset.sliceBy.polygons;
28
+ const {
29
+ obsIndices,
30
+ isSliced
31
+ } = (0, _FilterContext.useFilteredData)();
30
32
  const [data, setData] = (0, _react.useState)([]);
31
33
  const [layout, setLayout] = (0, _react.useState)({});
32
34
  const [hasSelections, setHasSelections] = (0, _react.useState)(false);
@@ -50,7 +52,7 @@ function Violin(_ref) {
50
52
  } : dataset.selectedVar?.index,
51
53
  obsCol: dataset.selectedObs,
52
54
  obsValues: !dataset.selectedObs?.omit.length ? null : _lodash.default.difference(_lodash.default.values(dataset.selectedObs?.codes), dataset.selectedObs?.omit).map(c => dataset.selectedObs?.codesMap[c]),
53
- obsIndices: isSliced ? [...(filteredData.obsIndices || [])] : null
55
+ obsIndices: isSliced ? [...(obsIndices || [])] : null
54
56
  }
55
57
  }[mode]
56
58
  });
@@ -93,13 +95,13 @@ function Violin(_ref) {
93
95
  } : dataset.selectedVar?.index,
94
96
  obsCol: dataset.selectedObs,
95
97
  obsValues: !dataset.selectedObs?.omit.length ? null : _lodash.default.difference(_lodash.default.values(dataset.selectedObs?.codes), dataset.selectedObs?.omit).map(c => dataset.selectedObs?.codesMap[c]),
96
- obsIndices: isSliced ? [...(filteredData.obsIndices || [])] : null,
98
+ obsIndices: isSliced ? [...(obsIndices || [])] : null,
97
99
  scale: dataset.controls.scale.violinplot.value,
98
100
  varNamesCol: dataset.varNamesCol
99
101
  };
100
102
  });
101
103
  }
102
- }, [dataset.controls.scale.violinplot.value, dataset.selectedMultiVar, dataset.selectedObs, dataset.selectedVar, dataset.url, dataset.varNamesCol, filteredData.obsIndices, isSliced, mode]);
104
+ }, [dataset.controls.scale.violinplot.value, dataset.selectedMultiVar, dataset.selectedObs, dataset.selectedVar, dataset.url, dataset.varNamesCol, obsIndices, isSliced, mode]);
103
105
  const {
104
106
  fetchedData,
105
107
  isPending,
@@ -13,6 +13,7 @@ var _reactQuery = require("@tanstack/react-query");
13
13
  var _reactQueryPersistClient = require("@tanstack/react-query-persist-client");
14
14
  var _lodash = _interopRequireDefault(require("lodash"));
15
15
  var _FilterContext = require("./FilterContext");
16
+ var _ZarrDataContext = require("./ZarrDataContext");
16
17
  var _constants = require("../constants/constants");
17
18
  var _jsxRuntime = require("react/jsx-runtime");
18
19
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
@@ -103,7 +104,8 @@ const initialDataset = {
103
104
  maskSet: null,
104
105
  maskValues: null,
105
106
  categoricalMode: _constants.PSEUDOSPATIAL_CATEGORICAL_MODES.ACROSS.value
106
- }
107
+ },
108
+ polygons: {}
107
109
  };
108
110
  const initializer = initialState => {
109
111
  const localObj = (JSON.parse(localStorage.getItem(_constants.LOCAL_STORAGE_KEY)) || {})[initialState.url] || {};
@@ -144,7 +146,9 @@ function DatasetProvider(_ref2) {
144
146
  client: queryClient,
145
147
  persistOptions: persistOptions,
146
148
  children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_FilterContext.FilterProvider, {
147
- children: children
149
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_ZarrDataContext.ZarrDataProvider, {
150
+ children: children
151
+ })
148
152
  })
149
153
  })
150
154
  })
@@ -535,6 +539,16 @@ function datasetReducer(dataset, action) {
535
539
  }
536
540
  };
537
541
  }
542
+ case "set.polygons":
543
+ {
544
+ return {
545
+ ...dataset,
546
+ polygons: {
547
+ ...dataset.polygons,
548
+ [action.obsm]: action.polygons
549
+ }
550
+ };
551
+ }
538
552
  case "set.pseudospatial.maskSet":
539
553
  {
540
554
  return {
@@ -14,9 +14,11 @@ function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e;
14
14
  const FilteredDataContext = exports.FilteredDataContext = /*#__PURE__*/(0, _react.createContext)(null);
15
15
  const FilteredDataDispatchContext = exports.FilteredDataDispatchContext = /*#__PURE__*/(0, _react.createContext)(null);
16
16
  const initialFilterData = {
17
- obsIndices: [],
18
- // @TODO: split to byObs and byPolygons ?
19
- varIndices: []
17
+ obsIndices: null,
18
+ valueMin: null,
19
+ valueMax: null,
20
+ slicedLength: null,
21
+ isSliced: false
20
22
  };
21
23
  function FilterProvider(_ref) {
22
24
  let {
@@ -45,28 +47,11 @@ function filterReducer(filteredData, action) {
45
47
  {
46
48
  return {
47
49
  ...filteredData,
48
- obsIndices: action.indices
49
- };
50
- }
51
- case "reset.obs.indices":
52
- {
53
- return {
54
- ...filteredData,
55
- obsIndices: []
56
- };
57
- }
58
- case "set.var.indices":
59
- {
60
- return {
61
- ...filteredData,
62
- varIndices: action.indices
63
- };
64
- }
65
- case "reset.var.indices":
66
- {
67
- return {
68
- ...filteredData,
69
- varIndices: []
50
+ obsIndices: action.indices,
51
+ valueMin: action.valueMin,
52
+ valueMax: action.valueMax,
53
+ slicedLength: action.slicedLength,
54
+ isSliced: action.isSliced
70
55
  };
71
56
  }
72
57
  default:
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.ZarrDataProvider = ZarrDataProvider;
7
+ exports.useZarrData = useZarrData;
8
+ var _react = _interopRequireWildcard(require("react"));
9
+ var _Filter = require("../utils/Filter");
10
+ var _zarrData = require("../utils/zarrData");
11
+ var _jsxRuntime = require("react/jsx-runtime");
12
+ function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
13
+ function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
14
+ const ZarrDataContext = /*#__PURE__*/(0, _react.createContext)(null);
15
+ function ZarrDataProvider(_ref) {
16
+ let {
17
+ children
18
+ } = _ref;
19
+ const obsmData = (0, _zarrData.useObsmData)();
20
+ const obsData = (0, _zarrData.useObsData)();
21
+ const xData = (0, _zarrData.useXData)();
22
+ const data = {
23
+ obsmData: obsmData,
24
+ obsData: obsData,
25
+ xData: xData,
26
+ isPending: obsmData.isPending || obsData.isPending || xData.isPending,
27
+ serverError: obsmData.serverError || obsData.serverError || xData.serverError
28
+ };
29
+ (0, _Filter.useFilter)(data);
30
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(ZarrDataContext.Provider, {
31
+ value: data,
32
+ children: children
33
+ });
34
+ }
35
+ function useZarrData() {
36
+ return (0, _react.useContext)(ZarrDataContext);
37
+ }
@@ -11907,12 +11907,12 @@ textarea.form-control-lg {
11907
11907
  margin-top: 76px;
11908
11908
  padding: 20px;
11909
11909
  position: relative;
11910
- min-height: 500px;
11910
+ min-height: 400px;
11911
11911
  }
11912
11912
 
11913
11913
  .cherita-container-scatterplot {
11914
11914
  position: relative;
11915
- min-height: 500px;
11915
+ min-height: 400px;
11916
11916
  height: 100%;
11917
11917
  }
11918
11918
 
@@ -45,20 +45,29 @@ const useZarr = function (_ref2) {
45
45
  } = _ref2;
46
46
  let s = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
47
47
  let opts = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
48
+ const {
49
+ enabled = true
50
+ } = opts;
48
51
  const [data, setData] = (0, _react.useState)(null);
49
52
  const [isPending, setIsPending] = (0, _react.useState)(true);
50
53
  const [serverError, setServerError] = (0, _react.useState)(null);
51
54
  (0, _react.useEffect)(() => {
52
- setIsPending(true);
53
- setServerError(null);
54
- fetchDataFromZarr(url, path, s, opts).then(data => {
55
- setData(data);
56
- }).catch(error => {
57
- setServerError(error.message);
58
- }).finally(() => {
55
+ if (enabled) {
56
+ setIsPending(true);
57
+ setServerError(null);
58
+ fetchDataFromZarr(url, path, s, opts).then(data => {
59
+ setData(data);
60
+ }).catch(error => {
61
+ setServerError(error.message);
62
+ }).finally(() => {
63
+ setIsPending(false);
64
+ });
65
+ } else {
59
66
  setIsPending(false);
60
- });
61
- }, [opts, path, s, url]);
67
+ setData(null);
68
+ setServerError(null);
69
+ }
70
+ }, [enabled, opts, path, s, url]);
62
71
  return {
63
72
  data,
64
73
  isPending,
@@ -87,20 +96,29 @@ const aggregateData = (inputs, data) => {
87
96
  const useMultipleZarr = function (inputs) {
88
97
  let opts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
89
98
  let agg = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : aggregateData;
99
+ const {
100
+ enabled = true
101
+ } = opts;
90
102
  const [data, setData] = (0, _react.useState)(null);
91
103
  const [isPending, setIsPending] = (0, _react.useState)(true);
92
104
  const [serverError, setServerError] = (0, _react.useState)(null);
93
105
  (0, _react.useEffect)(() => {
94
- setIsPending(true);
95
- setServerError(null);
96
- fetchDataFromZarrs(inputs, opts).then(data => {
97
- setData(agg(inputs, data));
98
- }).catch(error => {
99
- setServerError(error.message);
100
- }).finally(() => {
106
+ if (enabled) {
107
+ setIsPending(true);
108
+ setServerError(null);
109
+ fetchDataFromZarrs(inputs, opts).then(data => {
110
+ setData(agg(inputs, data));
111
+ }).catch(error => {
112
+ setServerError(error.message);
113
+ }).finally(() => {
114
+ setIsPending(false);
115
+ });
116
+ } else {
117
+ setData(null);
101
118
  setIsPending(false);
102
- });
103
- }, [agg, inputs, opts]);
119
+ setServerError(null);
120
+ }
121
+ }, [agg, enabled, inputs, opts]);
104
122
  return {
105
123
  data,
106
124
  isPending,
@@ -12,61 +12,87 @@ var _DatasetContext = require("../context/DatasetContext");
12
12
  var _FilterContext = require("../context/FilterContext");
13
13
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
14
14
  const EPSILON = 1e-6;
15
-
16
- // @TODO: polish hook
17
- const useFilter = (data, features) => {
15
+ const isInBins = (v, binEdges, indices) => {
16
+ const lastEdge = _lodash.default.last(binEdges);
17
+ const allButLastEdges = _lodash.default.initial(binEdges);
18
+ // add epsilon to last edge to include the last value
19
+ const modifiedBinEdges = [...allButLastEdges, [lastEdge[0], lastEdge[1] + EPSILON]];
20
+ const binIndices = _lodash.default.difference(_lodash.default.range(binEdges.length), indices);
21
+ const ranges = _lodash.default.at(modifiedBinEdges, binIndices);
22
+ return _lodash.default.some(ranges, range => _lodash.default.inRange(v, ...range));
23
+ };
24
+ const isInPolygons = (polygons, positions, index) => {
25
+ if (!polygons?.length || !positions?.length) {
26
+ return false;
27
+ }
28
+ return _lodash.default.some(polygons, (_f, i) => {
29
+ return (0, _turf.booleanPointInPolygon)((0, _turf.point)([positions[index][0], positions[index][1]]), polygons[i]);
30
+ });
31
+ };
32
+ const isInValues = (omit, value) => {
33
+ if (!omit?.length) {
34
+ return true;
35
+ }
36
+ return !_lodash.default.includes(omit, value);
37
+ };
38
+ const useFilter = data => {
18
39
  const dataset = (0, _DatasetContext.useDataset)();
19
40
  const filterDataDispatch = (0, _FilterContext.useFilteredDataDispatch)();
20
- const isInBins = (v, binEdges, indices) => {
21
- const lastEdge = _lodash.default.last(binEdges);
22
- const allButLastEdges = _lodash.default.initial(binEdges);
23
- // add epsilon to last edge to include the last value
24
- const modifiedBinEdges = [...allButLastEdges, [lastEdge[0], lastEdge[1] + EPSILON]];
25
- const binIndices = _lodash.default.difference(_lodash.default.range(binEdges.length), indices);
26
- const ranges = _lodash.default.at(modifiedBinEdges, binIndices);
27
- return _lodash.default.some(ranges, range => _lodash.default.inRange(v, ...range));
28
- };
29
- const isCategorical = (0, _react.useMemo)(() => {
30
- if (dataset.colorEncoding === _constants.COLOR_ENCODINGS.OBS) {
31
- return dataset.selectedObs?.type === _constants.OBS_TYPES.CATEGORICAL || dataset.selectedObs?.type === _constants.OBS_TYPES.BOOLEAN;
32
- } else {
33
- return false;
34
- }
35
- }, [dataset.colorEncoding, dataset.selectedObs?.type]);
36
- const isInSlice = (0, _react.useCallback)((index, values, positions) => {
41
+ const {
42
+ obsmData,
43
+ xData,
44
+ obsData,
45
+ isPending,
46
+ serverError
47
+ } = data;
48
+ const isCategorical = dataset.selectedObs?.type === _constants.OBS_TYPES.CATEGORICAL || dataset.selectedObs?.type === _constants.OBS_TYPES.BOOLEAN;
49
+ const isContinuous = dataset.selectedObs?.type === _constants.OBS_TYPES.CONTINUOUS;
50
+ const sliceByObs = dataset.colorEncoding === _constants.COLOR_ENCODINGS.OBS && !!dataset.selectedObs?.omit.length || dataset.sliceBy.obs;
51
+ const isInObsSlice = (0, _react.useCallback)((index, values) => {
37
52
  let inSlice = true;
38
- if (isCategorical && values) {
39
- inSlice &= !_lodash.default.includes(dataset.selectedObs?.omit, values[index]);
40
- } else if ((dataset.sliceBy.obs || dataset.colorEncoding === _constants.COLOR_ENCODINGS.OBS && dataset.selectedObs?.type === _constants.OBS_TYPES.CONTINUOUS) && !!dataset.selectedObs?.omit.length && values) {
41
- if (dataset.selectedObs.type === _constants.OBS_TYPES.CATEGORICAL) {
42
- inSlice &= !_lodash.default.includes(dataset.selectedObs.omit, values[index]);
43
- } else if (dataset.selectedObs.type === _constants.OBS_TYPES.CONTINUOUS) {
53
+ if (values && sliceByObs) {
54
+ if (isCategorical) {
55
+ inSlice &= isInValues(dataset.selectedObs?.omit, values[index]);
56
+ } else if (isContinuous) {
44
57
  if (isNaN(values[index])) {
45
- inSlice &= !_lodash.default.includes(dataset.selectedObs.omit, -1);
58
+ inSlice &= isInValues(dataset.selectedObs?.omit, -1);
46
59
  } else {
47
60
  inSlice &= isInBins(values[index], dataset.selectedObs.bins.binEdges, _lodash.default.without(dataset.selectedObs.omit, -1));
48
61
  }
49
62
  }
50
63
  }
64
+ return inSlice;
65
+ }, [dataset.selectedObs?.bins?.binEdges, dataset.selectedObs?.omit, isCategorical, isContinuous, sliceByObs]);
66
+ const isInPolygonsSlice = (0, _react.useCallback)((index, positions) => {
67
+ let inSlice = true;
51
68
  if (dataset.sliceBy.polygons && positions) {
52
- inSlice &= _lodash.default.some(features?.features, (_f, i) => {
53
- return (0, _turf.booleanPointInPolygon)((0, _turf.point)([positions[index][0], positions[index][1]]), features.features[i]);
54
- });
69
+ inSlice &= isInPolygons(dataset.polygons[dataset.selectedObsm], positions, index);
55
70
  }
56
71
  return inSlice;
57
- }, [dataset.colorEncoding, dataset.selectedObs?.bins?.binEdges, dataset.selectedObs?.omit, dataset.selectedObs?.type, dataset.sliceBy.obs, dataset.sliceBy.polygons, features?.features, isCategorical]);
72
+ }, [dataset.polygons, dataset.selectedObsm, dataset.sliceBy.polygons]);
73
+ const isInSlice = (0, _react.useCallback)((index, values, positions) => {
74
+ return isInObsSlice(index, values) && isInPolygonsSlice(index, positions);
75
+ }, [isInObsSlice, isInPolygonsSlice]);
58
76
  const {
59
77
  filteredIndices,
60
78
  valueMin,
61
79
  valueMax,
62
80
  slicedLength
63
81
  } = (0, _react.useMemo)(() => {
82
+ if (isPending || serverError) {
83
+ return {
84
+ filteredIndices: null,
85
+ valueMin: null,
86
+ valueMax: null,
87
+ slicedLength: null
88
+ };
89
+ }
64
90
  if (dataset.colorEncoding === _constants.COLOR_ENCODINGS.VAR) {
65
91
  const {
66
92
  filtered,
67
93
  filteredIndices
68
- } = _lodash.default.reduce(data.values, (acc, v, i) => {
69
- if (isInSlice(i, data.sliceValues, data.positions)) {
94
+ } = _lodash.default.reduce(xData.data, (acc, v, i) => {
95
+ if (isInSlice(i, obsData.data, obsmData.data)) {
70
96
  acc.filtered.push(v);
71
97
  acc.filteredIndices.add(i);
72
98
  }
@@ -82,12 +108,11 @@ const useFilter = (data, features) => {
82
108
  slicedLength: filtered.length
83
109
  };
84
110
  } else if (dataset.colorEncoding === _constants.COLOR_ENCODINGS.OBS) {
85
- const isContinuous = dataset.selectedObs?.type === _constants.OBS_TYPES.CONTINUOUS;
86
111
  const {
87
112
  filtered,
88
113
  filteredIndices
89
- } = _lodash.default.reduce(data.values, (acc, v, i) => {
90
- if (isInSlice(i, data.values, data.positions)) {
114
+ } = _lodash.default.reduce(obsData.data, (acc, v, i) => {
115
+ if (isInSlice(i, obsData.data, obsmData.data)) {
91
116
  acc.filtered.push(v);
92
117
  acc.filteredIndices.add(i);
93
118
  }
@@ -98,32 +123,33 @@ const useFilter = (data, features) => {
98
123
  });
99
124
  return {
100
125
  filteredIndices: filteredIndices,
101
- valueMin: _lodash.default.min(isContinuous ? filtered : data.values),
102
- valueMax: _lodash.default.max(isContinuous ? filtered : data.values),
126
+ valueMin: _lodash.default.min(isContinuous ? filtered : obsData.data),
127
+ valueMax: _lodash.default.max(isContinuous ? filtered : obsData.data),
103
128
  slicedLength: filtered.length
104
129
  };
105
130
  } else {
106
131
  return {
107
132
  filteredIndices: null,
108
- valueMin: _lodash.default.min(data.values),
109
- valueMax: _lodash.default.max(data.values),
110
- slicedLength: data.values.length
133
+ valueMin: _lodash.default.min(obsData.data),
134
+ valueMax: _lodash.default.max(obsData.data),
135
+ slicedLength: obsData.data.length
111
136
  };
112
137
  }
113
- }, [data.positions, data.sliceValues, data.values, dataset.colorEncoding, dataset.selectedObs?.type, isInSlice]);
138
+ }, [dataset.colorEncoding, isContinuous, isInSlice, isPending, obsData.data, obsmData.data, serverError, xData.data]);
139
+ const isSliced = sliceByObs || dataset.sliceBy.polygons;
140
+ // const isSliced = dataset.sliceBy.obs || dataset.sliceBy.polygons;
114
141
 
115
- // @TODO: consider moving dispatch outside of hook, only return values
116
142
  (0, _react.useEffect)(() => {
117
- filterDataDispatch({
118
- type: "set.obs.indices",
119
- indices: dataset.sliceBy.obs || dataset.sliceBy.polygons ? filteredIndices : null
120
- });
121
- }, [dataset.sliceBy.obs, dataset.sliceBy.polygons, filterDataDispatch, filteredIndices]);
122
- return {
123
- filteredIndices,
124
- valueMin,
125
- valueMax,
126
- slicedLength
127
- };
143
+ if (!isPending && !serverError) {
144
+ filterDataDispatch({
145
+ type: "set.obs.indices",
146
+ indices: isSliced ? filteredIndices : null,
147
+ valueMin: valueMin,
148
+ valueMax: valueMax,
149
+ slicedLength: slicedLength,
150
+ isSliced: isSliced
151
+ });
152
+ }
153
+ }, [dataset.sliceBy.obs, dataset.sliceBy.polygons, filterDataDispatch, filteredIndices, isPending, isSliced, serverError, slicedLength, valueMax, valueMin]);
128
154
  };
129
155
  exports.useFilter = useFilter;
@@ -17,7 +17,8 @@ function Legend(_ref) {
17
17
  isCategorical = false,
18
18
  min = 0,
19
19
  max = 1,
20
- colorscale = null
20
+ colorscale = null,
21
+ addText = ""
21
22
  } = _ref;
22
23
  const dataset = (0, _DatasetContext.useDataset)();
23
24
  const {
@@ -45,7 +46,7 @@ function Legend(_ref) {
45
46
  className: "gradient",
46
47
  children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("p", {
47
48
  className: "small m-0 p-0",
48
- children: dataset.colorEncoding === _constants.COLOR_ENCODINGS.VAR ? dataset.selectedVar?.name : dataset.selectedObs?.name
49
+ children: (dataset.colorEncoding === _constants.COLOR_ENCODINGS.VAR ? dataset.selectedVar?.name : dataset.selectedObs?.name) + addText
49
50
  }), spanList, /*#__PURE__*/(0, _jsxRuntime.jsx)("span", {
50
51
  className: "domain-min",
51
52
  children: (0, _string.formatNumerical)(min, _string.FORMATS.EXPONENTIAL)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@haniffalab/cherita-react",
3
- "version": "0.2.0-dev.2024-12-16.67617f27",
3
+ "version": "0.2.0-dev.2025-01-17.f9a0f419",
4
4
  "author": "",
5
5
  "license": "",
6
6
  "main": "dist/index.js",
@@ -101,5 +101,5 @@
101
101
  "url": "https://github.com/haniffalab/cherita-react/issues"
102
102
  },
103
103
  "homepage": "https://github.com/haniffalab/cherita-react#readme",
104
- "prereleaseSha": "67617f278b24035c633eed30a5b4d0136e3f77bb"
104
+ "prereleaseSha": "f9a0f419a990cb5fab684b202bf30c53d20f07f1"
105
105
  }
package/scss/cherita.scss CHANGED
@@ -70,12 +70,12 @@
70
70
  margin-top: 76px;
71
71
  padding: 20px;
72
72
  position: relative;
73
- min-height: 500px;
73
+ min-height: 400px;
74
74
  }
75
75
 
76
76
  .cherita-container-scatterplot {
77
77
  position: relative;
78
- min-height: 500px;
78
+ min-height: 400px;
79
79
  height: 100%;
80
80
  }
81
81