@haniffalab/cherita-react 1.4.1-dev.2025-10-20.528e0f75 → 1.4.1-dev.2025-10-22.61540191

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 (50) hide show
  1. package/dist/cjs/components/dotplot/Dotplot.js +23 -15
  2. package/dist/cjs/components/full-page/FullPage.js +32 -30
  3. package/dist/cjs/components/full-page/PlotAlert.js +36 -0
  4. package/dist/cjs/components/full-page/PlotTypeSelector.js +77 -39
  5. package/dist/cjs/components/heatmap/Heatmap.js +23 -15
  6. package/dist/cjs/components/icons/DotPlotIcon.js +58 -0
  7. package/dist/cjs/components/icons/HeatmapIcon.js +39 -0
  8. package/dist/cjs/components/icons/MatrixPlotIcon.1.js +51 -0
  9. package/dist/cjs/components/icons/MatrixPlotIcon.js +53 -0
  10. package/dist/cjs/components/icons/ScatterplotIcon.1.js +158 -0
  11. package/dist/cjs/components/icons/ScatterplotIcon.js +138 -0
  12. package/dist/cjs/components/icons/ViolinPlotIcon.js +34 -0
  13. package/dist/cjs/components/matrixplot/Matrixplot.js +25 -17
  14. package/dist/cjs/components/pseudospatial/Pseudospatial.js +64 -25
  15. package/dist/cjs/components/pseudospatial/PseudospatialToolbar.js +63 -15
  16. package/dist/cjs/components/violin/Violin.js +41 -30
  17. package/dist/cjs/context/DatasetContext.js +1 -1
  18. package/dist/cjs/context/SettingsContext.js +27 -3
  19. package/dist/cjs/utils/StyledTooltip.js +39 -0
  20. package/dist/css/cherita.css +37 -22
  21. package/dist/css/cherita.css.map +1 -1
  22. package/dist/esm/components/dotplot/Dotplot.js +22 -13
  23. package/dist/esm/components/full-page/FullPage.js +6 -3
  24. package/dist/esm/components/full-page/PlotAlert.js +30 -0
  25. package/dist/esm/components/full-page/PlotTypeSelector.js +76 -38
  26. package/dist/esm/components/heatmap/Heatmap.js +22 -13
  27. package/dist/esm/components/icons/DotPlotIcon.js +52 -0
  28. package/dist/esm/components/icons/HeatmapIcon.js +33 -0
  29. package/dist/esm/components/icons/MatrixPlotIcon.1.js +45 -0
  30. package/dist/esm/components/icons/MatrixPlotIcon.js +47 -0
  31. package/dist/esm/components/icons/ScatterplotIcon.1.js +152 -0
  32. package/dist/esm/components/icons/ScatterplotIcon.js +132 -0
  33. package/dist/esm/components/icons/ViolinPlotIcon.js +28 -0
  34. package/dist/esm/components/matrixplot/Matrixplot.js +24 -15
  35. package/dist/esm/components/pseudospatial/Pseudospatial.js +58 -18
  36. package/dist/esm/components/pseudospatial/PseudospatialToolbar.js +52 -4
  37. package/dist/esm/components/violin/Violin.js +36 -24
  38. package/dist/esm/context/DatasetContext.js +1 -1
  39. package/dist/esm/context/SettingsContext.js +27 -3
  40. package/dist/esm/utils/StyledTooltip.js +33 -0
  41. package/package.json +2 -2
  42. package/scss/cherita.scss +19 -1
  43. package/scss/components/layouts.scss +2 -21
  44. package/scss/components/plotly.scss +37 -26
  45. package/scss/components/plots.scss +14 -1
  46. package/dist/assets/images/plots/dotplot.svg +0 -152
  47. package/dist/assets/images/plots/heatmap.svg +0 -193
  48. package/dist/assets/images/plots/matrixplot.svg +0 -275
  49. package/dist/assets/images/plots/scatterplot.svg +0 -198
  50. package/dist/assets/images/plots/violin.svg +0 -50
@@ -3,9 +3,8 @@ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t =
3
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
4
  function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
5
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); }
6
- import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
7
- import { library } from "@fortawesome/fontawesome-svg-core";
8
- import { faSliders } from "@fortawesome/free-solid-svg-icons";
6
+ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
7
+ import { faEye, faSliders } from "@fortawesome/free-solid-svg-icons";
9
8
  import _ from "lodash";
10
9
  import { Alert } from "react-bootstrap";
11
10
  import Plot from "react-plotly.js";
@@ -19,7 +18,6 @@ import { Legend } from "../../utils/Legend";
19
18
  import { LoadingSpinner } from "../../utils/LoadingIndicators";
20
19
  import { useDebouncedFetch } from "../../utils/requests";
21
20
  import { useSelectedObs, useSelectedVar } from "../../utils/Resolver";
22
- library.add(faSliders);
23
21
  function usePseudospatialData(plotType) {
24
22
  const ENDPOINT = "pseudospatial";
25
23
  const dataset = useDataset();
@@ -73,7 +71,7 @@ function usePseudospatialData(plotType) {
73
71
  });
74
72
  }
75
73
  export function Pseudospatial(_ref) {
76
- var _layout$coloraxis3, _layout$coloraxis4;
74
+ var _layout$coloraxis3, _layout$coloraxis4, _settings$pseudospati2;
77
75
  let {
78
76
  showLegend = true,
79
77
  sharedScaleRange = false,
@@ -82,6 +80,9 @@ export function Pseudospatial(_ref) {
82
80
  plotType,
83
81
  setPlotType
84
82
  } = _ref;
83
+ const {
84
+ imageUrl
85
+ } = useDataset();
85
86
  const settings = useSettings();
86
87
  const dispatch = useSettingsDispatch();
87
88
  const [data, setData] = useState([]);
@@ -188,7 +189,48 @@ export function Pseudospatial(_ref) {
188
189
  }
189
190
  }, [settings.controls.range, settings.controls.valueMax, settings.controls.valueMin, getColor, sharedScaleRange, valueMax, valueMin]);
190
191
  const hasSelections = !!plotType && !!settings.pseudospatial.maskSet;
191
- const faSlidersPath = faSliders.icon[4];
192
+ const images = useMemo(() => {
193
+ if (imageUrl) {
194
+ return [_objectSpread({
195
+ source: imageUrl,
196
+ xref: "paper",
197
+ yref: "paper",
198
+ x: 0.5,
199
+ y: 0.5,
200
+ sizex: 1,
201
+ sizey: 1,
202
+ sizing: "contain",
203
+ layer: "above",
204
+ xanchor: "center",
205
+ yanchor: "middle",
206
+ name: "Reference Image"
207
+ }, settings.pseudospatial.refImg)];
208
+ }
209
+ return [];
210
+ }, [imageUrl, settings.pseudospatial.refImg]);
211
+ const modeBarButtons = useMemo(() => {
212
+ var _settings$pseudospati;
213
+ const isRefImgVisible = (_settings$pseudospati = settings.pseudospatial) === null || _settings$pseudospati === void 0 || (_settings$pseudospati = _settings$pseudospati.refImg) === null || _settings$pseudospati === void 0 ? void 0 : _settings$pseudospati.visible;
214
+ return [{
215
+ name: "Open plot controls",
216
+ icon: {
217
+ width: 512,
218
+ height: 512,
219
+ path: faSliders.icon[4]
220
+ },
221
+ click: () => setShowControls(prev => !prev)
222
+ }, ...(imageUrl ? [{
223
+ name: isRefImgVisible ? "Hide reference image" : "Show reference image",
224
+ icon: {
225
+ width: 600,
226
+ height: 512,
227
+ path: faEye.icon[4]
228
+ },
229
+ click: () => dispatch({
230
+ type: "toggle.pseudospatial.refImg.visible"
231
+ })
232
+ }] : [])];
233
+ }, [dispatch, imageUrl, setShowControls, (_settings$pseudospati2 = settings.pseudospatial) === null || _settings$pseudospati2 === void 0 || (_settings$pseudospati2 = _settings$pseudospati2.refImg) === null || _settings$pseudospati2 === void 0 ? void 0 : _settings$pseudospati2.visible]);
192
234
  if (!_.keys(settings.data.pseudospatial).length) {
193
235
  return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Alert, {
194
236
  variant: "warning"
@@ -201,24 +243,22 @@ export function Pseudospatial(_ref) {
201
243
  data: data,
202
244
  layout: _objectSpread(_objectSpread({}, layout), {}, {
203
245
  autosize: true,
204
- height: height
246
+ height: height,
247
+ margin: {
248
+ l: 0,
249
+ r: 0,
250
+ t: 0,
251
+ b: 0,
252
+ pad: 0
253
+ },
254
+ images: images
205
255
  }),
206
256
  useResizeHandler: true,
207
257
  className: "cherita-pseudospatial-plot",
208
258
  config: {
209
259
  displaylogo: false,
210
260
  displayModeBar: true,
211
- modeBarButtonsToAdd: [{
212
- name: "Open plot controls",
213
- icon: {
214
- width: 512,
215
- height: 512,
216
- path: faSlidersPath,
217
- ascent: 512,
218
- descent: 0
219
- },
220
- click: () => setShowControls(prev => !prev)
221
- }]
261
+ modeBarButtonsToAdd: modeBarButtons
222
262
  }
223
263
  }), hasSelections && showLegend && /*#__PURE__*/React.createElement(Legend, {
224
264
  min: layout === null || layout === void 0 || (_layout$coloraxis5 = layout.coloraxis) === null || _layout$coloraxis5 === void 0 ? void 0 : _layout$coloraxis5.cmin,
@@ -1,7 +1,9 @@
1
- import React from "react";
1
+ import { useState } from "react";
2
+ import { Box, Slider, Typography } from "@mui/material";
2
3
  import _ from "lodash";
3
4
  import { ButtonGroup, Dropdown, Form } from "react-bootstrap";
4
5
  import { PSEUDOSPATIAL_CATEGORICAL_MODES as MODES, PSEUDOSPATIAL_PLOT_TYPES as PLOT_TYPES } from "../../constants/constants";
6
+ import { useDataset } from "../../context/DatasetContext";
5
7
  import { useSettings, useSettingsDispatch } from "../../context/SettingsContext";
6
8
  function CategoricalMode() {
7
9
  const settings = useSettings();
@@ -89,13 +91,59 @@ function MaskSet() {
89
91
  onChange: toggleMasks
90
92
  })), masksList)));
91
93
  }
94
+ function OpacitySlider(_ref) {
95
+ let {
96
+ opacity,
97
+ setOpacity
98
+ } = _ref;
99
+ const [sliderValue, setSliderValue] = useState(opacity);
100
+ return /*#__PURE__*/React.createElement(Box, {
101
+ className: "w-100"
102
+ }, /*#__PURE__*/React.createElement(Typography, {
103
+ id: "opacity-range",
104
+ gutterBottom: true
105
+ }, "Reference image opacity"), /*#__PURE__*/React.createElement("div", {
106
+ className: "px-4"
107
+ }, /*#__PURE__*/React.createElement(Slider, {
108
+ "aria-labelledby": "opacity-range",
109
+ min: 0,
110
+ max: 1,
111
+ step: 0.05,
112
+ value: sliderValue,
113
+ onChange: (_e, value) => setSliderValue(value),
114
+ onChangeCommitted: (_e, value) => setOpacity(value),
115
+ valueLabelDisplay: "auto",
116
+ getAriaValueText: value => "".concat(value * 100, "%"),
117
+ valueLabelFormat: value => "".concat((value * 100).toFixed(0), "%"),
118
+ marks: [{
119
+ value: 0,
120
+ label: "0%"
121
+ }, {
122
+ value: 1,
123
+ label: "100%"
124
+ }]
125
+ })));
126
+ }
92
127
 
93
128
  // @TODO: add colormap, colorbar slider
94
- export function PseudospatialToolbar(_ref) {
129
+ export function PseudospatialToolbar(_ref2) {
95
130
  let {
96
131
  plotType
97
- } = _ref;
132
+ } = _ref2;
133
+ const {
134
+ imageUrl
135
+ } = useDataset();
136
+ const settings = useSettings();
137
+ const dispatch = useSettingsDispatch();
98
138
  return /*#__PURE__*/React.createElement("div", {
99
139
  className: "cherita-pseudospatial-toolbar"
100
- }, /*#__PURE__*/React.createElement(ButtonGroup, null, /*#__PURE__*/React.createElement(MaskSet, null)), /*#__PURE__*/React.createElement(ButtonGroup, null, plotType === PLOT_TYPES.CATEGORICAL && /*#__PURE__*/React.createElement(CategoricalMode, null)));
140
+ }, /*#__PURE__*/React.createElement(ButtonGroup, null, /*#__PURE__*/React.createElement(MaskSet, null)), /*#__PURE__*/React.createElement(ButtonGroup, null, plotType === PLOT_TYPES.CATEGORICAL && /*#__PURE__*/React.createElement(CategoricalMode, null)), imageUrl && /*#__PURE__*/React.createElement(OpacitySlider, {
141
+ opacity: settings.pseudospatial.refImg.opacity,
142
+ setOpacity: opacity => {
143
+ dispatch({
144
+ type: "set.pseudospatial.refImg.opacity",
145
+ opacity: opacity
146
+ });
147
+ }
148
+ }));
101
149
  }
@@ -3,11 +3,11 @@ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t =
3
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
4
  function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
5
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); }
6
- import React, { useEffect, useState } from "react";
6
+ import { useEffect, useState } from "react";
7
7
  import { faCircleInfo } from "@fortawesome/free-solid-svg-icons";
8
8
  import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
9
9
  import _ from "lodash";
10
- import { Alert, Button, OverlayTrigger, Tooltip } from "react-bootstrap";
10
+ import { Badge, Button } from "react-bootstrap";
11
11
  import Plot from "react-plotly.js";
12
12
  import { PLOTLY_MODEBAR_BUTTONS, VIOLIN_MODES } from "../../constants/constants";
13
13
  import { useDataset } from "../../context/DatasetContext";
@@ -16,6 +16,8 @@ import { useSettings } from "../../context/SettingsContext";
16
16
  import { LoadingSpinner } from "../../utils/LoadingIndicators";
17
17
  import { useDebouncedFetch } from "../../utils/requests";
18
18
  import { useSelectedMultiVar, useSelectedObs, useSelectedVar } from "../../utils/Resolver";
19
+ import { StyledTooltip } from "../../utils/StyledTooltip";
20
+ import { PlotAlert } from "../full-page/PlotAlert";
19
21
  import { ControlsPlotlyToolbar, ObsPlotlyToolbar, VarPlotlyToolbar } from "../toolbar/Toolbar";
20
22
  export function Violin(_ref) {
21
23
  let {
@@ -25,7 +27,9 @@ export function Violin(_ref) {
25
27
  showCtrlsBtn = false,
26
28
  setShowObs,
27
29
  setShowVars,
28
- setShowControls
30
+ setShowControls,
31
+ plotType,
32
+ setPlotType
29
33
  } = _ref;
30
34
  const ENDPOINT = "violin";
31
35
  const dataset = useDataset();
@@ -152,39 +156,47 @@ export function Violin(_ref) {
152
156
  modeBarButtons: modeBarButtons
153
157
  }
154
158
  })), (fetchedData === null || fetchedData === void 0 ? void 0 : fetchedData.resampled) && /*#__PURE__*/React.createElement("div", {
155
- className: "flex-shrink-0"
156
- }, /*#__PURE__*/React.createElement(Alert, {
157
- variant: "warning",
158
- className: "mb-1"
159
- }, /*#__PURE__*/React.createElement("b", null, "Warning:"), " For performance reasons this plot was generated with resampled data. It will not be exactly the same as one produced with the entire dataset. \xA0", /*#__PURE__*/React.createElement(OverlayTrigger, {
160
- placement: "top",
161
- overlay: /*#__PURE__*/React.createElement(Tooltip, null, "Resampled to 100K values following a Monte Carlo style approach to help ensure resampled data is a good representation of the original dataset's distribution.")
159
+ className: "resampled-tooltip-container"
160
+ }, /*#__PURE__*/React.createElement(StyledTooltip, {
161
+ title: /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("strong", null, "Note:"), " This plot uses resampled data to improve performance, so values may differ slightly from the full dataset. The data were resampled to exactly 100,000 values using a Monte Carlo\u2013style approach to provide a representative view of the full distribution while reducing processing time."),
162
+ placement: "bottom"
163
+ }, /*#__PURE__*/React.createElement(Badge, {
164
+ bg: "info"
162
165
  }, /*#__PURE__*/React.createElement(FontAwesomeIcon, {
166
+ className: "fs-6",
163
167
  icon: faCircleInfo
164
- }))))));
168
+ }), /*#__PURE__*/React.createElement("span", {
169
+ className: "d-none d-lg-inline ms-2 fs-6 text-uppercase"
170
+ }, "Resampled"))))));
165
171
  }
166
- return /*#__PURE__*/React.createElement("div", {
167
- className: "cherita-violin"
168
- }, mode === VIOLIN_MODES.MULTIKEY && /*#__PURE__*/React.createElement(Alert, {
169
- variant: "light"
170
- }, "Select", " ", showVarsBtn ? /*#__PURE__*/React.createElement(Button, {
172
+ return /*#__PURE__*/React.createElement(PlotAlert, {
173
+ variant: "info",
174
+ heading: "Violin plot",
175
+ plotType: plotType,
176
+ setPlotType: setPlotType
177
+ }, mode === VIOLIN_MODES.MULTIKEY && /*#__PURE__*/React.createElement("p", {
178
+ className: "p-0 m-0"
179
+ }, "To begin, select one or more", " ", showVarsBtn ? /*#__PURE__*/React.createElement(Button, {
171
180
  variant: "link",
172
181
  className: "border-0 p-0 align-baseline",
173
182
  onClick: setShowVars
174
- }, "features") : "features"), mode === VIOLIN_MODES.GROUPBY && /*#__PURE__*/React.createElement(Alert, {
175
- variant: "light"
176
- }, "Select", " ", showObsBtn ? /*#__PURE__*/React.createElement(Button, {
183
+ }, "features") : "features", " ", "to display their expression distributions across all observations."), mode === VIOLIN_MODES.GROUPBY && /*#__PURE__*/React.createElement("p", {
184
+ className: "p-0 m-0"
185
+ }, "To begin, select a", " ", showObsBtn ? /*#__PURE__*/React.createElement(Button, {
177
186
  variant: "link",
178
187
  className: "border-0 p-0 align-baseline",
179
188
  onClick: setShowObs
180
- }, "categories") : "categories", " ", "and a", " ", showVarsBtn ? /*#__PURE__*/React.createElement(Button, {
189
+ }, "category") : "category", " ", "to group observations, and choose a", " ", showVarsBtn ? /*#__PURE__*/React.createElement(Button, {
181
190
  variant: "link",
182
191
  className: "border-0 p-0 align-baseline",
183
192
  onClick: setShowVars
184
- }, "feature") : "feature"));
193
+ }, "feature") : "feature", " ", "to view its distribution within each group."));
185
194
  } else {
186
- return /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement(Alert, {
187
- variant: "danger"
188
- }, serverError.message));
195
+ return /*#__PURE__*/React.createElement(PlotAlert, {
196
+ variant: "danger",
197
+ heading: "Violin plot",
198
+ plotType: plotType,
199
+ setPlotType: setPlotType
200
+ }, serverError.message || "An unexpected error occurred while generating the plot.");
189
201
  }
190
202
  }
@@ -50,7 +50,7 @@ const persistOptions = {
50
50
  return false;
51
51
  }
52
52
  },
53
- buster: "1.4.1-dev.2025-10-20.528e0f75" || "0.0.0"
53
+ buster: "1.4.1-dev.2025-10-22.61540191" || "0.0.0"
54
54
  // @TODO: add maxAge and api version numbers as buster
55
55
  };
56
56
  const initialDataset = {
@@ -62,7 +62,11 @@ const initialSettings = {
62
62
  pseudospatial: {
63
63
  maskSet: null,
64
64
  maskValues: null,
65
- categoricalMode: PSEUDOSPATIAL_CATEGORICAL_MODES.ACROSS.value
65
+ categoricalMode: PSEUDOSPATIAL_CATEGORICAL_MODES.ACROSS.value,
66
+ refImg: {
67
+ visible: false,
68
+ opacity: 1
69
+ }
66
70
  },
67
71
  // dataset resolved values
68
72
  data: {
@@ -158,7 +162,7 @@ export function SettingsProvider(_ref2) {
158
162
 
159
163
  // If the buster is not set or does not match the current package version,
160
164
  // reset localSettings to avoid stale data
161
- if (!buster || buster !== "1.4.1-dev.2025-10-20.528e0f75") {
165
+ if (!buster || buster !== "1.4.1-dev.2025-10-22.61540191") {
162
166
  localSettings = {};
163
167
  }
164
168
  const initSettings = useRef(initializer({
@@ -183,7 +187,7 @@ export function SettingsProvider(_ref2) {
183
187
  if (canOverrideSettings && settings) {
184
188
  try {
185
189
  localStorage.setItem(DATASET_STORAGE_KEY, JSON.stringify(_objectSpread({
186
- buster: "1.4.1-dev.2025-10-20.528e0f75" || "0.0.0",
190
+ buster: "1.4.1-dev.2025-10-22.61540191" || "0.0.0",
187
191
  timestamp: Date.now()
188
192
  }, _.omit(settings, "data"))));
189
193
  } catch (err) {
@@ -708,6 +712,26 @@ function settingsReducer(settings, action) {
708
712
  })
709
713
  });
710
714
  }
715
+ case "toggle.pseudospatial.refImg.visible":
716
+ {
717
+ return _objectSpread(_objectSpread({}, settings), {}, {
718
+ pseudospatial: _objectSpread(_objectSpread({}, settings.pseudospatial), {}, {
719
+ refImg: _objectSpread(_objectSpread({}, settings.pseudospatial.refImg), {}, {
720
+ visible: !settings.pseudospatial.refImg.visible
721
+ })
722
+ })
723
+ });
724
+ }
725
+ case "set.pseudospatial.refImg.opacity":
726
+ {
727
+ return _objectSpread(_objectSpread({}, settings), {}, {
728
+ pseudospatial: _objectSpread(_objectSpread({}, settings.pseudospatial), {}, {
729
+ refImg: _objectSpread(_objectSpread({}, settings.pseudospatial.refImg), {}, {
730
+ opacity: action.opacity
731
+ })
732
+ })
733
+ });
734
+ }
711
735
  default:
712
736
  {
713
737
  throw Error("Unknown action: " + action.type);
@@ -0,0 +1,33 @@
1
+ const _excluded = ["className"];
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; }
5
+ import { styled, Tooltip, tooltipClasses } from "@mui/material";
6
+ export const StyledTooltip = styled(_ref => {
7
+ let {
8
+ className
9
+ } = _ref,
10
+ props = _objectWithoutProperties(_ref, _excluded);
11
+ return /*#__PURE__*/React.createElement(Tooltip, _extends({}, props, {
12
+ classes: {
13
+ popper: className
14
+ }
15
+ }));
16
+ })(_ref2 => {
17
+ let {
18
+ theme
19
+ } = _ref2;
20
+ return {
21
+ ["& .".concat(tooltipClasses.tooltip)]: {
22
+ backgroundColor: "#000",
23
+ color: "#fff",
24
+ maxWidth: 220,
25
+ fontSize: 13,
26
+ borderRadius: 4,
27
+ padding: theme.spacing(0.5, 1),
28
+ whiteSpace: "pre-line",
29
+ textAlign: "center",
30
+ boxShadow: "0 0.25rem 0.75rem rgba(0, 0, 0, 0.1)"
31
+ }
32
+ };
33
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@haniffalab/cherita-react",
3
- "version": "1.4.1-dev.2025-10-20.528e0f75",
3
+ "version": "1.4.1-dev.2025-10-22.61540191",
4
4
  "author": "Haniffa Lab",
5
5
  "license": "MIT",
6
6
  "keywords": [
@@ -128,5 +128,5 @@
128
128
  "url": "https://github.com/haniffalab/cherita-react/issues"
129
129
  },
130
130
  "homepage": "https://github.com/haniffalab/cherita-react#readme",
131
- "prereleaseSha": "528e0f754fac6fae140c651384c2979c1b1fcbaf"
131
+ "prereleaseSha": "615401914075590f2159adb28a6db12e0d02b720"
132
132
  }
package/scss/cherita.scss CHANGED
@@ -428,4 +428,22 @@ $gauge-padding: 0.15rem;
428
428
 
429
429
  input[type="checkbox"] {
430
430
  cursor: pointer;
431
- }
431
+ }
432
+
433
+ .ps-xs-1 {
434
+ padding-left: 0.15rem !important;
435
+ }
436
+
437
+ .resampled-tooltip-container {
438
+ position: absolute;
439
+ bottom: 16px;
440
+ right: 16px;
441
+ z-index: 10;
442
+ width: max-content;
443
+
444
+ @media (min-width: 991px) {
445
+ top: 16px;
446
+ left: 16px;
447
+ bottom: auto;
448
+ }
449
+ }
@@ -97,21 +97,6 @@
97
97
  }
98
98
  }
99
99
 
100
- .sidebar-plotselector {
101
- padding: 6px;
102
- border-bottom: 1px solid #dee2e6;
103
-
104
- .plotselector-icon {
105
- padding-bottom: 2px;
106
- cursor: pointer;
107
-
108
- .active {
109
- opacity: 1;
110
- border-bottom: 2px solid #007bff;
111
- }
112
- }
113
- }
114
-
115
100
  .sidebar-pseudospatial {
116
101
  padding: 10px;
117
102
  border-bottom: 1px solid #dee2e6;
@@ -182,9 +167,9 @@
182
167
  }
183
168
 
184
169
  .cherita-app-canvas {
185
- @extend .p-1;
170
+ @extend .p-0;
186
171
  flex: 1 1 auto;
187
- min-width: 400px;
172
+ min-width: 375px;
188
173
  position: relative;
189
174
  max-height: 100%;
190
175
  display: flex;
@@ -211,7 +196,3 @@
211
196
  }
212
197
  }
213
198
  }
214
-
215
- .ps-xs-1 {
216
- padding-left: 0.15rem !important;
217
- }
@@ -1,35 +1,46 @@
1
- .js-plotly-plot .plotly .modebar {
1
+ // Plotly modebar customisation
2
+ .js-plotly-plot {
3
+ .plotly {
4
+ .modebar {
5
+ top: 16px;
6
+ right: 0;
2
7
 
3
- .modebar-group {
4
- @extend .bg-primary;
5
- @extend .mx-1;
6
- border-radius: 5px;
7
- padding: 4px !important;
8
+ // Each button group in the modebar
9
+ .modebar-group {
10
+ @extend .bg-primary;
11
+ @extend .mx-1;
12
+ border-radius: 5px;
13
+ padding: 4px !important;
8
14
 
9
- .modebar-btn {
10
- display: inline-flex;
11
- justify-content: center;
12
- position: relative;
13
- font-size: 1.25rem;
14
- padding: 0.25rem;
15
- height: auto;
16
- cursor: pointer;
17
- line-height: normal;
15
+ // Individual buttons
16
+ .modebar-btn {
17
+ display: inline-flex;
18
+ justify-content: center;
19
+ align-items: center;
20
+ position: relative;
21
+ font-size: 1.25rem;
22
+ padding: 0.25rem;
23
+ height: auto;
24
+ cursor: pointer;
25
+ line-height: normal;
18
26
 
19
- svg {
20
- position: relative;
21
- top: 1px;
27
+ svg {
28
+ position: relative;
29
+ top: 1px;
22
30
 
23
- path {
24
- fill: white !important;
25
- color: white !important;
31
+ path {
32
+ fill: white !important;
33
+ color: white !important;
34
+ }
35
+ }
26
36
  }
27
37
  }
38
+
39
+ // Hover effect for modebar buttons except watermark
40
+ &--hover > :not(.watermark) {
41
+ opacity: 1;
42
+ transition: opacity 0.3s;
43
+ }
28
44
  }
29
45
  }
30
46
  }
31
-
32
- .js-plotly-plot .plotly .modebar--hover > :not(.watermark) {
33
- opacity: 0.5;
34
- transition: opacity 0.3s;
35
- }
@@ -1,11 +1,24 @@
1
1
  .cherita-spatial-controls {
2
2
  position: absolute;
3
- z-index: 10;
4
3
  top: 1rem;
5
4
  left: 1rem;
5
+ z-index: 10;
6
6
  width: 3rem;
7
+
8
+ // Target buttons inside the controls
7
9
  .btn {
8
10
  padding-left: 0;
9
11
  padding-right: 0;
10
12
  }
11
13
  }
14
+
15
+ .plotselector {
16
+ padding: 0 10px;
17
+ border-bottom: 1px solid #dee2e6;
18
+
19
+ // Individual plot selector icons
20
+ .plotselector-icon {
21
+ padding: 6px;
22
+ cursor: pointer;
23
+ }
24
+ }