@haniffalab/cherita-react 1.2.0 → 1.3.0-dev.2025-05-29.ee7e9b72

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 (91) hide show
  1. package/dist/cjs/components/controls/Controls.js +60 -0
  2. package/dist/cjs/components/dotplot/Dotplot.js +47 -38
  3. package/dist/cjs/components/dotplot/DotplotControls.js +77 -114
  4. package/dist/cjs/components/full-page/FullPage.js +29 -33
  5. package/dist/cjs/components/full-page/FullPagePseudospatial.js +30 -33
  6. package/dist/cjs/components/heatmap/Heatmap.js +33 -22
  7. package/dist/cjs/components/heatmap/HeatmapControls.js +2 -19
  8. package/dist/cjs/components/matrixplot/Matrixplot.js +35 -24
  9. package/dist/cjs/components/matrixplot/MatrixplotControls.js +4 -34
  10. package/dist/cjs/components/obs-list/ObsItem.js +63 -51
  11. package/dist/cjs/components/obs-list/ObsList.js +53 -48
  12. package/dist/cjs/components/obsm-list/ObsmList.js +17 -12
  13. package/dist/cjs/components/offcanvas/index.js +14 -11
  14. package/dist/cjs/components/pseudospatial/Pseudospatial.js +78 -68
  15. package/dist/cjs/components/pseudospatial/PseudospatialToolbar.js +27 -21
  16. package/dist/cjs/components/scatterplot/Scatterplot.js +82 -76
  17. package/dist/cjs/components/scatterplot/ScatterplotControls.js +18 -31
  18. package/dist/cjs/components/scatterplot/SpatialControls.js +53 -23
  19. package/dist/cjs/components/scatterplot/Toolbox.js +1 -18
  20. package/dist/cjs/components/search-bar/SearchBar.js +156 -59
  21. package/dist/cjs/components/search-bar/SearchInfo.js +182 -0
  22. package/dist/cjs/components/search-bar/SearchResults.js +90 -60
  23. package/dist/cjs/components/var-list/VarItem.js +52 -75
  24. package/dist/cjs/components/var-list/VarList.js +47 -172
  25. package/dist/cjs/components/var-list/VarListToolbar.js +7 -8
  26. package/dist/cjs/components/var-list/VarSet.js +66 -57
  27. package/dist/cjs/components/violin/Violin.js +54 -43
  28. package/dist/cjs/components/violin/ViolinControls.js +4 -20
  29. package/dist/cjs/context/DatasetContext.js +26 -513
  30. package/dist/cjs/context/FilterContext.js +9 -8
  31. package/dist/cjs/context/SettingsContext.js +539 -0
  32. package/dist/cjs/context/ZarrDataContext.js +1 -2
  33. package/dist/cjs/helpers/color-helper.js +8 -8
  34. package/dist/cjs/helpers/zarr-helper.js +19 -16
  35. package/dist/cjs/utils/Filter.js +25 -21
  36. package/dist/cjs/utils/Histogram.js +4 -3
  37. package/dist/cjs/utils/ImageViewer.js +1 -2
  38. package/dist/cjs/utils/Legend.js +18 -12
  39. package/dist/cjs/utils/LoadingIndicators.js +1 -1
  40. package/dist/cjs/utils/VirtualizedList.js +16 -13
  41. package/dist/cjs/utils/errors.js +20 -22
  42. package/dist/cjs/utils/requests.js +13 -10
  43. package/dist/cjs/utils/zarrData.js +31 -50
  44. package/dist/css/cherita.css +84 -24
  45. package/dist/css/cherita.css.map +1 -1
  46. package/dist/esm/components/controls/Controls.js +51 -0
  47. package/dist/esm/components/dotplot/Dotplot.js +47 -37
  48. package/dist/esm/components/dotplot/DotplotControls.js +77 -112
  49. package/dist/esm/components/full-page/FullPage.js +29 -32
  50. package/dist/esm/components/full-page/FullPagePseudospatial.js +30 -32
  51. package/dist/esm/components/heatmap/Heatmap.js +32 -20
  52. package/dist/esm/components/heatmap/HeatmapControls.js +3 -20
  53. package/dist/esm/components/matrixplot/Matrixplot.js +34 -22
  54. package/dist/esm/components/matrixplot/MatrixplotControls.js +5 -35
  55. package/dist/esm/components/obs-list/ObsItem.js +63 -49
  56. package/dist/esm/components/obs-list/ObsList.js +53 -47
  57. package/dist/esm/components/obsm-list/ObsmList.js +17 -11
  58. package/dist/esm/components/offcanvas/index.js +14 -11
  59. package/dist/esm/components/pseudospatial/Pseudospatial.js +77 -66
  60. package/dist/esm/components/pseudospatial/PseudospatialToolbar.js +27 -20
  61. package/dist/esm/components/scatterplot/Scatterplot.js +81 -74
  62. package/dist/esm/components/scatterplot/ScatterplotControls.js +18 -29
  63. package/dist/esm/components/scatterplot/SpatialControls.js +54 -23
  64. package/dist/esm/components/scatterplot/Toolbox.js +1 -18
  65. package/dist/esm/components/search-bar/SearchBar.js +156 -59
  66. package/dist/esm/components/search-bar/SearchInfo.js +173 -0
  67. package/dist/esm/components/search-bar/SearchResults.js +91 -60
  68. package/dist/esm/components/var-list/VarItem.js +53 -76
  69. package/dist/esm/components/var-list/VarList.js +47 -171
  70. package/dist/esm/components/var-list/VarListToolbar.js +6 -6
  71. package/dist/esm/components/var-list/VarSet.js +67 -57
  72. package/dist/esm/components/violin/Violin.js +53 -41
  73. package/dist/esm/components/violin/ViolinControls.js +5 -21
  74. package/dist/esm/context/DatasetContext.js +25 -510
  75. package/dist/esm/context/FilterContext.js +8 -6
  76. package/dist/esm/context/SettingsContext.js +528 -0
  77. package/dist/esm/helpers/color-helper.js +8 -8
  78. package/dist/esm/helpers/zarr-helper.js +19 -16
  79. package/dist/esm/utils/Filter.js +25 -21
  80. package/dist/esm/utils/Histogram.js +4 -3
  81. package/dist/esm/utils/Legend.js +17 -10
  82. package/dist/esm/utils/LoadingIndicators.js +1 -1
  83. package/dist/esm/utils/VirtualizedList.js +15 -11
  84. package/dist/esm/utils/errors.js +20 -22
  85. package/dist/esm/utils/requests.js +13 -10
  86. package/dist/esm/utils/zarrData.js +33 -51
  87. package/package.json +6 -3
  88. package/scss/cherita.scss +50 -9
  89. package/scss/components/layouts.scss +24 -13
  90. package/scss/components/lists.scss +10 -0
  91. package/scss/components/plots.scss +3 -5
@@ -2,8 +2,8 @@ import { useEffect, useCallback, useMemo } from "react";
2
2
  import { booleanPointInPolygon, point } from "@turf/turf";
3
3
  import _ from "lodash";
4
4
  import { COLOR_ENCODINGS, OBS_TYPES } from "../constants/constants";
5
- import { useDataset } from "../context/DatasetContext";
6
5
  import { useFilteredDataDispatch } from "../context/FilterContext";
6
+ import { useSettings } from "../context/SettingsContext";
7
7
  const EPSILON = 1e-6;
8
8
  const isInBins = (v, binEdges, indices) => {
9
9
  const lastEdge = _.last(binEdges);
@@ -15,7 +15,7 @@ const isInBins = (v, binEdges, indices) => {
15
15
  return _.some(ranges, range => _.inRange(v, ...range));
16
16
  };
17
17
  const isInPolygons = (polygons, positions, index) => {
18
- if (!polygons?.length || !positions?.length) {
18
+ if (!(polygons !== null && polygons !== void 0 && polygons.length) || !(positions !== null && positions !== void 0 && positions.length)) {
19
19
  return false;
20
20
  }
21
21
  return _.some(polygons, (_f, i) => {
@@ -23,13 +23,14 @@ const isInPolygons = (polygons, positions, index) => {
23
23
  });
24
24
  };
25
25
  const isInValues = (omit, value) => {
26
- if (!omit?.length) {
26
+ if (!(omit !== null && omit !== void 0 && omit.length)) {
27
27
  return true;
28
28
  }
29
29
  return !_.includes(omit, value);
30
30
  };
31
31
  export const useFilter = data => {
32
- const dataset = useDataset();
32
+ var _settings$selectedObs, _settings$selectedObs2, _settings$selectedObs3, _settings$selectedObs4, _settings$selectedObs7, _settings$selectedObs8;
33
+ const settings = useSettings();
33
34
  const filterDataDispatch = useFilteredDataDispatch();
34
35
  const {
35
36
  obsmData,
@@ -38,31 +39,33 @@ export const useFilter = data => {
38
39
  isPending,
39
40
  serverError
40
41
  } = data;
41
- const isCategorical = dataset.selectedObs?.type === OBS_TYPES.CATEGORICAL || dataset.selectedObs?.type === OBS_TYPES.BOOLEAN;
42
- const isContinuous = dataset.selectedObs?.type === OBS_TYPES.CONTINUOUS;
43
- const sliceByObs = dataset.colorEncoding === COLOR_ENCODINGS.OBS && !!dataset.selectedObs?.omit.length || dataset.sliceBy.obs;
42
+ const isCategorical = ((_settings$selectedObs = settings.selectedObs) === null || _settings$selectedObs === void 0 ? void 0 : _settings$selectedObs.type) === OBS_TYPES.CATEGORICAL || ((_settings$selectedObs2 = settings.selectedObs) === null || _settings$selectedObs2 === void 0 ? void 0 : _settings$selectedObs2.type) === OBS_TYPES.BOOLEAN;
43
+ const isContinuous = ((_settings$selectedObs3 = settings.selectedObs) === null || _settings$selectedObs3 === void 0 ? void 0 : _settings$selectedObs3.type) === OBS_TYPES.CONTINUOUS;
44
+ const sliceByObs = settings.colorEncoding === COLOR_ENCODINGS.OBS && !!((_settings$selectedObs4 = settings.selectedObs) !== null && _settings$selectedObs4 !== void 0 && _settings$selectedObs4.omit.length) || settings.sliceBy.obs;
44
45
  const isInObsSlice = useCallback((index, values) => {
45
46
  let inSlice = true;
46
47
  if (values && sliceByObs) {
47
48
  if (isCategorical) {
48
- inSlice &= isInValues(dataset.selectedObs?.omit, values[index]);
49
+ var _settings$selectedObs5;
50
+ inSlice &= isInValues((_settings$selectedObs5 = settings.selectedObs) === null || _settings$selectedObs5 === void 0 ? void 0 : _settings$selectedObs5.omit, values[index]);
49
51
  } else if (isContinuous) {
50
52
  if (isNaN(values[index])) {
51
- inSlice &= isInValues(dataset.selectedObs?.omit, -1);
53
+ var _settings$selectedObs6;
54
+ inSlice &= isInValues((_settings$selectedObs6 = settings.selectedObs) === null || _settings$selectedObs6 === void 0 ? void 0 : _settings$selectedObs6.omit, -1);
52
55
  } else {
53
- inSlice &= isInBins(values[index], dataset.selectedObs.bins.binEdges, _.without(dataset.selectedObs.omit, -1));
56
+ inSlice &= isInBins(values[index], settings.selectedObs.bins.binEdges, _.without(settings.selectedObs.omit, -1));
54
57
  }
55
58
  }
56
59
  }
57
60
  return inSlice;
58
- }, [dataset.selectedObs?.bins?.binEdges, dataset.selectedObs?.omit, isCategorical, isContinuous, sliceByObs]);
61
+ }, [(_settings$selectedObs7 = settings.selectedObs) === null || _settings$selectedObs7 === void 0 || (_settings$selectedObs7 = _settings$selectedObs7.bins) === null || _settings$selectedObs7 === void 0 ? void 0 : _settings$selectedObs7.binEdges, (_settings$selectedObs8 = settings.selectedObs) === null || _settings$selectedObs8 === void 0 ? void 0 : _settings$selectedObs8.omit, isCategorical, isContinuous, sliceByObs]);
59
62
  const isInPolygonsSlice = useCallback((index, positions) => {
60
63
  let inSlice = true;
61
- if (dataset.sliceBy.polygons && positions) {
62
- inSlice &= isInPolygons(dataset.polygons[dataset.selectedObsm], positions, index);
64
+ if (settings.sliceBy.polygons && positions) {
65
+ inSlice &= isInPolygons(settings.polygons[settings.selectedObsm], positions, index);
63
66
  }
64
67
  return inSlice;
65
- }, [dataset.polygons, dataset.selectedObsm, dataset.sliceBy.polygons]);
68
+ }, [settings.polygons, settings.selectedObsm, settings.sliceBy.polygons]);
66
69
  const isInSlice = useCallback((index, values, positions) => {
67
70
  return isInObsSlice(index, values) && isInPolygonsSlice(index, positions);
68
71
  }, [isInObsSlice, isInPolygonsSlice]);
@@ -80,7 +83,7 @@ export const useFilter = data => {
80
83
  slicedLength: null
81
84
  };
82
85
  }
83
- if (dataset.colorEncoding === COLOR_ENCODINGS.VAR) {
86
+ if (settings.colorEncoding === COLOR_ENCODINGS.VAR) {
84
87
  const {
85
88
  filtered,
86
89
  filteredIndices
@@ -100,7 +103,7 @@ export const useFilter = data => {
100
103
  valueMax: _.max(filtered),
101
104
  slicedLength: filtered.length
102
105
  };
103
- } else if (dataset.colorEncoding === COLOR_ENCODINGS.OBS) {
106
+ } else if (settings.colorEncoding === COLOR_ENCODINGS.OBS) {
104
107
  const {
105
108
  filtered,
106
109
  filteredIndices
@@ -121,16 +124,17 @@ export const useFilter = data => {
121
124
  slicedLength: filtered.length
122
125
  };
123
126
  } else {
127
+ var _obsData$data;
124
128
  return {
125
129
  filteredIndices: null,
126
130
  valueMin: _.min(obsData.data),
127
131
  valueMax: _.max(obsData.data),
128
- slicedLength: obsData.data?.length
132
+ slicedLength: (_obsData$data = obsData.data) === null || _obsData$data === void 0 ? void 0 : _obsData$data.length
129
133
  };
130
134
  }
131
- }, [dataset.colorEncoding, isContinuous, isInSlice, isPending, obsData.data, obsmData.data, serverError, xData.data]);
132
- const isSliced = sliceByObs || dataset.sliceBy.polygons;
133
- // const isSliced = dataset.sliceBy.obs || dataset.sliceBy.polygons;
135
+ }, [settings.colorEncoding, isContinuous, isInSlice, isPending, obsData.data, obsmData.data, serverError, xData.data]);
136
+ const isSliced = sliceByObs || settings.sliceBy.polygons;
137
+ // const isSliced = settings.sliceBy.obs || settings.sliceBy.polygons;
134
138
 
135
139
  useEffect(() => {
136
140
  if (!isPending && !serverError) {
@@ -143,5 +147,5 @@ export const useFilter = data => {
143
147
  isSliced: isSliced
144
148
  });
145
149
  }
146
- }, [dataset.sliceBy.obs, dataset.sliceBy.polygons, filterDataDispatch, filteredIndices, isPending, isSliced, serverError, slicedLength, valueMax, valueMin]);
150
+ }, [settings.sliceBy.obs, settings.sliceBy.polygons, filterDataDispatch, filteredIndices, isPending, isSliced, serverError, slicedLength, valueMax, valueMin]);
147
151
  };
@@ -4,6 +4,7 @@ import _ from "lodash";
4
4
  import { LoadingLinear } from "./LoadingIndicators";
5
5
  import { formatNumerical, FORMATS } from "./string";
6
6
  export function Histogram(_ref) {
7
+ var _data$bin_edges;
7
8
  let {
8
9
  data,
9
10
  isPending,
@@ -29,11 +30,11 @@ export function Histogram(_ref) {
29
30
  let {
30
31
  dataIndex
31
32
  } = _ref2;
32
- return `${formatNumerical(data.hist[dataIndex])}`;
33
+ return "".concat(formatNumerical(data.hist[dataIndex]));
33
34
  },
34
35
  xAxis: {
35
- data: _.range(data.bin_edges?.length) || null,
36
- valueFormatter: v => `Bin [${formatNumerical(data.bin_edges[v][0], FORMATS.EXPONENTIAL)}, ${formatNumerical(data.bin_edges[v][1], FORMATS.EXPONENTIAL)}${v === data.bin_edges.length - 1 ? "]" : ")"}`
36
+ data: _.range((_data$bin_edges = data.bin_edges) === null || _data$bin_edges === void 0 ? void 0 : _data$bin_edges.length) || null,
37
+ valueFormatter: v => "Bin [".concat(formatNumerical(data.bin_edges[v][0], FORMATS.EXPONENTIAL), ", ").concat(formatNumerical(data.bin_edges[v][1], FORMATS.EXPONENTIAL)).concat(v === data.bin_edges.length - 1 ? "]" : ")")
37
38
  },
38
39
  slotProps: {
39
40
  popper: {
@@ -1,8 +1,10 @@
1
1
  import React, { useMemo } from "react";
2
+ import { faDroplet } from "@fortawesome/free-solid-svg-icons";
3
+ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
2
4
  import _ from "lodash";
3
5
  import { formatNumerical, FORMATS } from "./string";
4
6
  import { COLOR_ENCODINGS } from "../constants/constants";
5
- import { useDataset } from "../context/DatasetContext";
7
+ import { useSettings } from "../context/SettingsContext";
6
8
  import { rgbToHex, useColor } from "../helpers/color-helper";
7
9
  export function Legend(_ref) {
8
10
  let {
@@ -12,7 +14,7 @@ export function Legend(_ref) {
12
14
  colorscale = null,
13
15
  addText = ""
14
16
  } = _ref;
15
- const dataset = useDataset();
17
+ const settings = useSettings();
16
18
  const {
17
19
  getColor
18
20
  } = useColor();
@@ -32,27 +34,32 @@ export function Legend(_ref) {
32
34
  });
33
35
  });
34
36
  }, [colorscale, getColor, isCategorical]);
35
- if (dataset.colorEncoding && !isCategorical) {
37
+ if (settings.colorEncoding && !isCategorical) {
38
+ var _settings$selectedVar, _settings$selectedObs;
36
39
  return /*#__PURE__*/React.createElement("div", {
37
40
  className: "cherita-legend"
38
41
  }, /*#__PURE__*/React.createElement("div", {
39
42
  className: "gradient"
40
43
  }, /*#__PURE__*/React.createElement("p", {
41
44
  className: "small m-0 p-0"
42
- }, (dataset.colorEncoding === COLOR_ENCODINGS.VAR ? dataset.selectedVar?.name : dataset.selectedObs?.name) + addText), spanList, /*#__PURE__*/React.createElement("span", {
45
+ }, /*#__PURE__*/React.createElement(FontAwesomeIcon, {
46
+ icon: faDroplet,
47
+ className: "me-1"
48
+ }), (settings.colorEncoding === COLOR_ENCODINGS.VAR ? (_settings$selectedVar = settings.selectedVar) === null || _settings$selectedVar === void 0 ? void 0 : _settings$selectedVar.name : (_settings$selectedObs = settings.selectedObs) === null || _settings$selectedObs === void 0 ? void 0 : _settings$selectedObs.name) + addText), spanList, /*#__PURE__*/React.createElement("span", {
43
49
  className: "domain-min"
44
50
  }, formatNumerical(min, FORMATS.EXPONENTIAL)), /*#__PURE__*/React.createElement("span", {
45
51
  className: "domain-med"
46
52
  }, formatNumerical((min + max) * 0.5, FORMATS.EXPONENTIAL)), /*#__PURE__*/React.createElement("span", {
47
53
  className: "domain-max"
48
54
  }, formatNumerical(max, FORMATS.EXPONENTIAL))));
49
- } else {
55
+ } else if (settings.colorEncoding === COLOR_ENCODINGS.OBS && settings.selectedObs) {
50
56
  return /*#__PURE__*/React.createElement("div", {
51
- className: "cherita-legend"
52
- }, /*#__PURE__*/React.createElement("div", {
53
- className: "gradient"
57
+ className: "cherita-legend categorical"
54
58
  }, /*#__PURE__*/React.createElement("p", {
55
- className: "small m-0 p-0"
56
- }, dataset.colorEncoding === COLOR_ENCODINGS.OBS && dataset.selectedObs ? dataset.selectedObs.name : "")));
59
+ className: "legend-text text-end m-0 p-0"
60
+ }, /*#__PURE__*/React.createElement(FontAwesomeIcon, {
61
+ icon: faDroplet,
62
+ className: "me-2"
63
+ }), settings.selectedObs.name));
57
64
  }
58
65
  }
@@ -9,7 +9,7 @@ export const LoadingSpinner = _ref => {
9
9
  className: "loading-spinner"
10
10
  }, /*#__PURE__*/React.createElement(CircularProgress, {
11
11
  disableShrink: disableShrink
12
- }), text?.length && /*#__PURE__*/React.createElement("span", {
12
+ }), (text === null || text === void 0 ? void 0 : text.length) && /*#__PURE__*/React.createElement("span", {
13
13
  className: "visually-hidden"
14
14
  }, text));
15
15
  };
@@ -1,16 +1,20 @@
1
+ const _excluded = ["getDataAtIndex", "count", "ItemComponent", "estimateSize", "overscan", "maxHeight"];
1
2
  function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); }
3
+ function _objectWithoutProperties(e, t) { if (null == e) return {}; var o, r, i = _objectWithoutPropertiesLoose(e, t); if (Object.getOwnPropertySymbols) { var n = Object.getOwnPropertySymbols(e); for (r = 0; r < n.length; r++) o = n[r], -1 === t.indexOf(o) && {}.propertyIsEnumerable.call(e, o) && (i[o] = e[o]); } return i; }
4
+ function _objectWithoutPropertiesLoose(r, e) { if (null == r) return {}; var t = {}; for (var n in r) if ({}.hasOwnProperty.call(r, n)) { if (-1 !== e.indexOf(n)) continue; t[n] = r[n]; } return t; }
2
5
  import React, { useCallback, useEffect, useState } from "react";
3
6
  import { useVirtualizer } from "@tanstack/react-virtual";
4
7
  export function VirtualizedList(_ref) {
8
+ var _virtualItems$0$start, _virtualItems$;
5
9
  let {
6
- getDataAtIndex,
7
- count,
8
- ItemComponent,
9
- estimateSize = 45,
10
- overscan = 25,
11
- maxHeight = "65vh",
12
- ...props
13
- } = _ref;
10
+ getDataAtIndex,
11
+ count,
12
+ ItemComponent,
13
+ estimateSize = 45,
14
+ overscan = 25,
15
+ maxHeight = "65vh"
16
+ } = _ref,
17
+ props = _objectWithoutProperties(_ref, _excluded);
14
18
  const [parentNode, setParentNode] = useState(null);
15
19
  const itemVirtualizer = useVirtualizer({
16
20
  count: count,
@@ -24,7 +28,7 @@ export function VirtualizedList(_ref) {
24
28
  const virtualItems = itemVirtualizer.getVirtualItems();
25
29
  useEffect(() => {
26
30
  itemVirtualizer.measure();
27
- }, [itemVirtualizer, parentNode?.clientHeight, getDataAtIndex]);
31
+ }, [itemVirtualizer, parentNode === null || parentNode === void 0 ? void 0 : parentNode.clientHeight, getDataAtIndex]);
28
32
  return /*#__PURE__*/React.createElement("div", {
29
33
  ref: refCallback,
30
34
  style: {
@@ -34,7 +38,7 @@ export function VirtualizedList(_ref) {
34
38
  className: "modern-scrollbars"
35
39
  }, /*#__PURE__*/React.createElement("div", {
36
40
  style: {
37
- height: `${itemVirtualizer.getTotalSize()}px`,
41
+ height: "".concat(itemVirtualizer.getTotalSize(), "px"),
38
42
  width: "100%",
39
43
  position: "relative",
40
44
  willChange: "transform"
@@ -45,7 +49,7 @@ export function VirtualizedList(_ref) {
45
49
  top: 0,
46
50
  left: 0,
47
51
  width: "100%",
48
- transform: `translateY(${virtualItems[0]?.start ?? 0}px)`
52
+ transform: "translateY(".concat((_virtualItems$0$start = (_virtualItems$ = virtualItems[0]) === null || _virtualItems$ === void 0 ? void 0 : _virtualItems$.start) !== null && _virtualItems$0$start !== void 0 ? _virtualItems$0$start : 0, "px)")
49
53
  }
50
54
  }, virtualItems.map(virtualItem => /*#__PURE__*/React.createElement("div", {
51
55
  key: virtualItem.key,
@@ -1,3 +1,8 @@
1
+ function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
2
+ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
3
+ function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
4
+ function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
5
+ function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
1
6
  export const parseError = err => {
2
7
  if (err === DOMException.TIMEOUT_ERR) {
3
8
  return {
@@ -5,42 +10,35 @@ export const parseError = err => {
5
10
  name: err
6
11
  };
7
12
  }
8
- switch (err?.name) {
13
+ switch (err === null || err === void 0 ? void 0 : err.name) {
9
14
  case "TypeError":
10
- return {
11
- ...err,
15
+ return _objectSpread(_objectSpread({}, err), {}, {
12
16
  message: "Failed to fetch data from server"
13
- };
17
+ });
14
18
  case "ReadZarrError":
15
- return {
16
- ...err,
19
+ return _objectSpread(_objectSpread({}, err), {}, {
17
20
  message: "Failed to read AnnData-Zarr"
18
- };
21
+ });
19
22
  case "InvalidObs":
20
- return {
21
- ...err,
23
+ return _objectSpread(_objectSpread({}, err), {}, {
22
24
  message: "Observation not found in dataset"
23
- };
25
+ });
24
26
  case "InvalidVar":
25
- return {
26
- ...err,
27
+ return _objectSpread(_objectSpread({}, err), {}, {
27
28
  message: "Feature not found in dataset"
28
- };
29
+ });
29
30
  case "InvalidKey":
30
- return {
31
- ...err,
31
+ return _objectSpread(_objectSpread({}, err), {}, {
32
32
  message: "Key not found in datset"
33
- };
33
+ });
34
34
  case "BadRequest":
35
- return {
36
- ...err,
35
+ return _objectSpread(_objectSpread({}, err), {}, {
37
36
  message: "Invalid request to server"
38
- };
37
+ });
39
38
  case "InternalServerError":
40
- return {
41
- ...err,
39
+ return _objectSpread(_objectSpread({}, err), {}, {
42
40
  message: "Server error"
43
- };
41
+ });
44
42
  default:
45
43
  return err;
46
44
  }
@@ -1,3 +1,8 @@
1
+ function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
2
+ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
3
+ function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
4
+ function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
5
+ function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
1
6
  import { useQuery } from "@tanstack/react-query";
2
7
  import { useDebounce } from "@uidotdev/usehooks";
3
8
  import { parseError } from "./errors";
@@ -42,7 +47,7 @@ export const useFetch = function (endpoint, params) {
42
47
  data: fetchedData = null,
43
48
  isLoading: isPending = false,
44
49
  error: serverError = null
45
- } = useQuery({
50
+ } = useQuery(_objectSpread({
46
51
  queryKey: [endpoint, params],
47
52
  queryFn: _ref => {
48
53
  let {
@@ -54,11 +59,10 @@ export const useFetch = function (endpoint, params) {
54
59
  let {
55
60
  error
56
61
  } = _ref2;
57
- if ([400, 401, 403, 404, 422].includes(error?.status)) return false;
62
+ if ([400, 401, 403, 404, 422].includes(error === null || error === void 0 ? void 0 : error.status)) return false;
58
63
  return failureCount < 3;
59
- },
60
- ...opts
61
- });
64
+ }
65
+ }, opts));
62
66
  return {
63
67
  fetchedData,
64
68
  isPending,
@@ -77,7 +81,7 @@ export const useDebouncedFetch = function (endpoint, params) {
77
81
  data: fetchedData = null,
78
82
  isLoading: isPending = false,
79
83
  error: serverError = null
80
- } = useQuery({
84
+ } = useQuery(_objectSpread({
81
85
  queryKey: [endpoint, debouncedParams],
82
86
  queryFn: _ref3 => {
83
87
  let {
@@ -89,11 +93,10 @@ export const useDebouncedFetch = function (endpoint, params) {
89
93
  let {
90
94
  error
91
95
  } = _ref4;
92
- if ([400, 401, 403, 404, 422].includes(error?.status)) return false;
96
+ if ([400, 401, 403, 404, 422].includes(error === null || error === void 0 ? void 0 : error.status)) return false;
93
97
  return failureCount < 3;
94
- },
95
- ...opts
96
- });
98
+ }
99
+ }, opts));
97
100
  return {
98
101
  fetchedData,
99
102
  isPending,
@@ -1,23 +1,23 @@
1
- import { useEffect, useState } from "react";
1
+ import { useMemo } from "react";
2
2
  import _ from "lodash";
3
+ import { slice } from "zarr";
3
4
  import { OBS_TYPES } from "../constants/constants";
4
5
  import { useDataset } from "../context/DatasetContext";
6
+ import { useSettings } from "../context/SettingsContext";
5
7
  import { GET_OPTIONS, useZarr, useMultipleZarr } from "../helpers/zarr-helper";
8
+
9
+ // @TODO: support specifying slice to load from context
6
10
  export const useObsmData = function () {
7
11
  let obsm = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;
8
12
  const dataset = useDataset();
9
- obsm = obsm || dataset.selectedObsm;
10
- const [obsmParams, setObsmParams] = useState({
13
+ const settings = useSettings();
14
+ obsm = obsm || settings.selectedObsm;
15
+ const obsmParams = useMemo(() => ({
11
16
  url: dataset.url,
12
- path: "obsm/" + obsm
13
- });
14
- useEffect(() => {
15
- setObsmParams({
16
- url: dataset.url,
17
- path: "obsm/" + obsm
18
- });
19
- }, [dataset.url, obsm]);
20
- return useZarr(obsmParams, null, GET_OPTIONS, {
17
+ path: "obsm/" + obsm,
18
+ s: [null, slice(null, 2)] // load only [:, :2]
19
+ }), [dataset.url, obsm]);
20
+ return useZarr(obsmParams, GET_OPTIONS, {
21
21
  enabled: !!obsm
22
22
  });
23
23
  };
@@ -32,70 +32,52 @@ const meanData = (_i, data) => {
32
32
  export const useXData = function () {
33
33
  let agg = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : meanData;
34
34
  const dataset = useDataset();
35
- const [xParams, setXParams] = useState(!dataset.selectedVar ? [] : !dataset.selectedVar?.isSet ? [{
36
- url: dataset.url,
37
- path: "X",
38
- s: [null, dataset.selectedVar?.matrix_index]
39
- }] : _.map(dataset.selectedVar?.vars, v => {
40
- return {
35
+ const settings = useSettings();
36
+ const xParams = useMemo(() => {
37
+ var _settings$selectedVar, _settings$selectedVar2, _settings$selectedVar3;
38
+ return !settings.selectedVar ? [] : !((_settings$selectedVar = settings.selectedVar) !== null && _settings$selectedVar !== void 0 && _settings$selectedVar.isSet) ? [{
41
39
  url: dataset.url,
42
40
  path: "X",
43
- s: [null, v.matrix_index]
44
- };
45
- }));
46
- useEffect(() => {
47
- setXParams(!dataset.selectedVar ? [] : !dataset.selectedVar?.isSet ? [{
48
- url: dataset.url,
49
- path: "X",
50
- s: [null, dataset.selectedVar?.matrix_index]
51
- }] : _.map(dataset.selectedVar?.vars, v => {
41
+ s: [null, (_settings$selectedVar2 = settings.selectedVar) === null || _settings$selectedVar2 === void 0 ? void 0 : _settings$selectedVar2.matrix_index]
42
+ }] : _.map((_settings$selectedVar3 = settings.selectedVar) === null || _settings$selectedVar3 === void 0 ? void 0 : _settings$selectedVar3.vars, v => {
52
43
  return {
53
44
  url: dataset.url,
54
45
  path: "X",
55
46
  s: [null, v.matrix_index]
56
47
  };
57
- }));
58
- }, [dataset.url, dataset.selectedVar]);
48
+ });
49
+ }, [dataset.url, settings.selectedVar]);
59
50
  return useMultipleZarr(xParams, GET_OPTIONS, {
60
51
  enabled: !!xParams.length
61
52
  }, agg);
62
53
  };
63
54
  export const useObsData = function () {
55
+ var _obs3, _obs4;
64
56
  let obs = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;
65
57
  const dataset = useDataset();
66
- obs = obs || dataset.selectedObs;
67
- const [obsParams, setObsParams] = useState({
68
- url: dataset.url,
69
- path: "obs/" + obs?.name + (obs?.type === OBS_TYPES.CATEGORICAL ? "/codes" : "")
70
- });
71
- useEffect(() => {
72
- setObsParams({
58
+ const settings = useSettings();
59
+ obs = obs || settings.selectedObs;
60
+ const obsParams = useMemo(() => {
61
+ var _obs, _obs2;
62
+ return {
73
63
  url: dataset.url,
74
- path: "obs/" + obs?.name + (obs?.type === OBS_TYPES.CATEGORICAL ? "/codes" : "")
75
- });
76
- }, [dataset.url, obs]);
77
- return useZarr(obsParams, null, GET_OPTIONS, {
64
+ path: "obs/" + ((_obs = obs) === null || _obs === void 0 ? void 0 : _obs.name) + (((_obs2 = obs) === null || _obs2 === void 0 ? void 0 : _obs2.type) === OBS_TYPES.CATEGORICAL ? "/codes" : "")
65
+ };
66
+ }, [dataset.url, (_obs3 = obs) === null || _obs3 === void 0 ? void 0 : _obs3.name, (_obs4 = obs) === null || _obs4 === void 0 ? void 0 : _obs4.type]);
67
+ return useZarr(obsParams, GET_OPTIONS, {
78
68
  enabled: !!obs
79
69
  });
80
70
  };
81
71
  export const useLabelObsData = () => {
82
72
  const dataset = useDataset();
83
- const [labelObsParams, setLabelObsParams] = useState(_.map(dataset.labelObs, obs => {
73
+ const settings = useSettings();
74
+ const labelObsParams = useMemo(() => _.map(settings.labelObs, obs => {
84
75
  return {
85
76
  url: dataset.url,
86
77
  path: "obs/" + obs.name + (obs.type === OBS_TYPES.CATEGORICAL ? "/codes" : ""),
87
78
  key: obs.name
88
79
  };
89
- }));
90
- useEffect(() => {
91
- setLabelObsParams(_.map(dataset.labelObs, obs => {
92
- return {
93
- url: dataset.url,
94
- path: "obs/" + obs.name + (obs.type === OBS_TYPES.CATEGORICAL ? "/codes" : ""),
95
- key: obs.name
96
- };
97
- }));
98
- }, [dataset.labelObs, dataset.url]);
80
+ }), [dataset.url, settings.labelObs]);
99
81
  return useMultipleZarr(labelObsParams, GET_OPTIONS, {
100
82
  enabled: !!labelObsParams.length
101
83
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@haniffalab/cherita-react",
3
- "version": "1.2.0",
3
+ "version": "1.3.0-dev.2025-05-29.ee7e9b72",
4
4
  "author": "Haniffa Lab",
5
5
  "license": "MIT",
6
6
  "keywords": [
@@ -77,6 +77,8 @@
77
77
  "jest": "^29.7.0",
78
78
  "jest-environment-jsdom": "^29.7.0",
79
79
  "prettier": "^3.5.3",
80
+ "react": "^18.2.0",
81
+ "react-dom": "^18.2.0",
80
82
  "sass": "1.77.6",
81
83
  "stylelint": "^16.10.0",
82
84
  "stylelint-config-prettier": "^9.0.5",
@@ -123,5 +125,6 @@
123
125
  "bugs": {
124
126
  "url": "https://github.com/haniffalab/cherita-react/issues"
125
127
  },
126
- "homepage": "https://github.com/haniffalab/cherita-react#readme"
127
- }
128
+ "homepage": "https://github.com/haniffalab/cherita-react#readme",
129
+ "prereleaseSha": "ee7e9b72ff17d43a4febc5169837417e48926f4a"
130
+ }
package/scss/cherita.scss CHANGED
@@ -14,7 +14,7 @@ $prefix: "bs-" !default;
14
14
  height: 100%;
15
15
  width: 100%;
16
16
  display: flex;
17
- z-index: 1;
17
+ z-index: 2;
18
18
  opacity: 0.75;
19
19
  position: absolute;
20
20
  justify-content: center;
@@ -73,15 +73,13 @@ $prefix: "bs-" !default;
73
73
  .cherita-spatial-footer {
74
74
  position: absolute;
75
75
  z-index: 10;
76
- bottom: 1rem;
76
+ bottom: 0;
77
77
  display: flex;
78
78
  justify-content: space-between;
79
- align-items: flex-end;
79
+ align-items: end;
80
80
  flex-wrap: wrap;
81
81
  width: 100%;
82
- padding-left: 1rem;
83
- padding-right: 1rem;
84
- padding-bottom: 1rem;
82
+ padding: 1rem;
85
83
  pointer-events: none;
86
84
 
87
85
  > * {
@@ -105,6 +103,41 @@ $prefix: "bs-" !default;
105
103
  .cherita-legend {
106
104
  order: 2;
107
105
  width: 12rem;
106
+ color: #333;
107
+ }
108
+
109
+ .cherita-legend.categorical {
110
+ display: flex;
111
+ align-items: center;
112
+ justify-content: flex-end;
113
+ padding-left: 1rem;
114
+ flex: 1;
115
+ overflow: hidden;
116
+ white-space: nowrap;
117
+ text-overflow: ellipsis;
118
+ pointer-events: auto;
119
+ .legend-text {
120
+ overflow: hidden;
121
+ text-overflow: ellipsis;
122
+ white-space: nowrap;
123
+ max-width: 100%;
124
+ display: inline-block;
125
+ }
126
+ }
127
+
128
+ @media (max-width: 600px) {
129
+ .cherita-spatial-footer {
130
+ flex-direction: column;
131
+ align-items: center;
132
+ justify-content: center;
133
+ }
134
+
135
+ .cherita-legend {
136
+ flex-direction: column;
137
+ align-items: center;
138
+ margin-top: 1rem;
139
+ padding: 0;
140
+ }
108
141
  }
109
142
 
110
143
  .cherita-accordion-active .accordion-button {
@@ -248,6 +281,11 @@ $gauge-padding: 0.15rem;
248
281
  font-size: 0.85rem !important;
249
282
  }
250
283
 
284
+ .feature-disease-info-list {
285
+ max-height: 300px;
286
+ overflow-y: auto;
287
+ }
288
+
251
289
  .feature-disease-info {
252
290
  font-weight: 300;
253
291
  padding-left: 0 !important;
@@ -352,9 +390,6 @@ $gauge-padding: 0.15rem;
352
390
  }
353
391
 
354
392
  .search-results {
355
- max-height: 25vh;
356
- overflow-y: scroll;
357
-
358
393
  .dropdown-item {
359
394
  white-space: normal;
360
395
  overflow-wrap: break-word;
@@ -365,3 +400,9 @@ $gauge-padding: 0.15rem;
365
400
  margin-right: 0.4rem;
366
401
  width: 1rem;
367
402
  }
403
+
404
+ .ellipsis-text {
405
+ white-space: nowrap;
406
+ overflow: hidden;
407
+ text-overflow: ellipsis;
408
+ }