@haniffalab/cherita-react 1.4.1-dev.2025-10-23.3179e534 → 1.4.1-dev.2025-10-23.e95b5a90

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.
@@ -49,7 +49,7 @@ function Dotplot(_ref) {
49
49
  const [hasSelections, setHasSelections] = (0, _react.useState)(false);
50
50
  const selectedObs = (0, _Resolver.useSelectedObs)();
51
51
  const selectedMultiVar = (0, _Resolver.useSelectedMultiVar)();
52
- const [params, setParams] = (0, _react.useState)({
52
+ const params = (0, _react.useMemo)(() => ({
53
53
  url: dataset.url,
54
54
  obsCol: selectedObs,
55
55
  obsValues: !(selectedObs !== null && selectedObs !== void 0 && selectedObs.omit.length) ? null : _lodash.default.difference(selectedObs === null || selectedObs === void 0 ? void 0 : selectedObs.values, selectedObs === null || selectedObs === void 0 ? void 0 : selectedObs.omit),
@@ -62,7 +62,7 @@ function Dotplot(_ref) {
62
62
  meanOnlyExpressed: settings.controls.meanOnlyExpressed,
63
63
  expressionCutoff: settings.controls.expressionCutoff,
64
64
  varNamesCol: dataset.varNamesCol
65
- });
65
+ }), [dataset.url, dataset.varNamesCol, isSliced, obsIndices, selectedMultiVar, selectedObs, settings.controls.expressionCutoff, settings.controls.meanOnlyExpressed, settings.controls.scale.dotplot]);
66
66
  // @TODO: set default scale
67
67
 
68
68
  (0, _react.useEffect)(() => {
@@ -71,22 +71,6 @@ function Dotplot(_ref) {
71
71
  } else {
72
72
  setHasSelections(false);
73
73
  }
74
- setParams(p => {
75
- return _objectSpread(_objectSpread({}, p), {}, {
76
- url: dataset.url,
77
- obsCol: selectedObs,
78
- obsValues: !(selectedObs !== null && selectedObs !== void 0 && selectedObs.omit.length) ? null : _lodash.default.difference(selectedObs === null || selectedObs === void 0 ? void 0 : selectedObs.values, selectedObs === null || selectedObs === void 0 ? void 0 : selectedObs.omit),
79
- varKeys: selectedMultiVar.map(i => i.isSet ? {
80
- name: i.name,
81
- indices: i.vars.map(v => v.index)
82
- } : i.index),
83
- obsIndices: isSliced ? [...(obsIndices || [])] : null,
84
- standardScale: settings.controls.scale.dotplot,
85
- meanOnlyExpressed: settings.controls.meanOnlyExpressed,
86
- expressionCutoff: settings.controls.expressionCutoff,
87
- varNamesCol: dataset.varNamesCol
88
- });
89
- });
90
74
  }, [dataset.url, selectedObs, settings.controls.scale.dotplot, settings.controls.meanOnlyExpressed, settings.controls.expressionCutoff, dataset.varNamesCol, isSliced, obsIndices, selectedMultiVar]);
91
75
  const updateColorscale = (0, _react.useCallback)(colorscale => {
92
76
  setLayout(l => {
@@ -102,10 +86,10 @@ function Dotplot(_ref) {
102
86
  isPending,
103
87
  serverError
104
88
  } = (0, _requests.useDebouncedFetch)(ENDPOINT, params, 500, {
105
- enabled: !!params.obsCol && !!params.varKeys.length
89
+ isEnabled: params => !!params.obsCol && !!params.varKeys.length
106
90
  });
107
91
  (0, _react.useEffect)(() => {
108
- if (hasSelections && !isPending && !serverError) {
92
+ if (hasSelections && !!fetchedData && !isPending && !serverError) {
109
93
  setData(fetchedData.data);
110
94
  setLayout(fetchedData.layout);
111
95
  // @TODO: keep colorAxis range from settings
@@ -119,6 +103,9 @@ function Dotplot(_ref) {
119
103
  }
120
104
  });
121
105
  updateColorscale(colorscale.current);
106
+ } else {
107
+ setData([]);
108
+ setLayout({});
122
109
  }
123
110
  }, [fetchedData, isPending, serverError, hasSelections, dispatch, updateColorscale]);
124
111
  (0, _react.useEffect)(() => {
@@ -48,7 +48,7 @@ function Heatmap(_ref) {
48
48
  const [hasSelections, setHasSelections] = (0, _react.useState)(false);
49
49
  const selectedObs = (0, _Resolver.useSelectedObs)();
50
50
  const selectedMultiVar = (0, _Resolver.useSelectedMultiVar)();
51
- const [params, setParams] = (0, _react.useState)({
51
+ const params = (0, _react.useMemo)(() => ({
52
52
  url: dataset.url,
53
53
  obsCol: selectedObs,
54
54
  obsValues: !(selectedObs !== null && selectedObs !== void 0 && selectedObs.omit.length) ? null : _lodash.default.difference(selectedObs === null || selectedObs === void 0 ? void 0 : selectedObs.values, selectedObs === null || selectedObs === void 0 ? void 0 : selectedObs.omit),
@@ -58,26 +58,13 @@ function Heatmap(_ref) {
58
58
  } : i.index),
59
59
  obsIndices: isSliced ? [...(obsIndices || [])] : null,
60
60
  varNamesCol: dataset.varNamesCol
61
- });
61
+ }), [dataset.url, dataset.varNamesCol, isSliced, obsIndices, selectedMultiVar, selectedObs]);
62
62
  (0, _react.useEffect)(() => {
63
63
  if (selectedObs && selectedMultiVar.length) {
64
64
  setHasSelections(true);
65
65
  } else {
66
66
  setHasSelections(false);
67
67
  }
68
- setParams(p => {
69
- return _objectSpread(_objectSpread({}, p), {}, {
70
- url: dataset.url,
71
- obsCol: selectedObs,
72
- obsValues: !(selectedObs !== null && selectedObs !== void 0 && selectedObs.omit.length) ? null : _lodash.default.difference(selectedObs === null || selectedObs === void 0 ? void 0 : selectedObs.values, selectedObs === null || selectedObs === void 0 ? void 0 : selectedObs.omit),
73
- varKeys: selectedMultiVar.map(i => i.isSet ? {
74
- name: i.name,
75
- indices: i.vars.map(v => v.index)
76
- } : i.index),
77
- obsIndices: isSliced ? [...(obsIndices || [])] : null,
78
- varNamesCol: dataset.varNamesCol
79
- });
80
- });
81
68
  }, [selectedMultiVar, selectedObs, dataset.url, dataset.varNamesCol, obsIndices, isSliced]);
82
69
  const updateColorscale = (0, _react.useCallback)(colorscale => {
83
70
  setLayout(l => {
@@ -93,13 +80,16 @@ function Heatmap(_ref) {
93
80
  isPending,
94
81
  serverError
95
82
  } = (0, _requests.useDebouncedFetch)(ENDPOINT, params, 500, {
96
- enabled: !!params.obsCol && !!params.varKeys.length
83
+ isEnabled: params => !!params.obsCol && !!params.varKeys.length
97
84
  });
98
85
  (0, _react.useEffect)(() => {
99
- if (hasSelections && !isPending && !serverError) {
86
+ if (hasSelections && !!fetchedData && !isPending && !serverError) {
100
87
  setData(fetchedData.data);
101
88
  setLayout(fetchedData.layout);
102
89
  updateColorscale(colorscale.current);
90
+ } else {
91
+ setData([]);
92
+ setLayout({});
103
93
  }
104
94
  }, [fetchedData, hasSelections, isPending, serverError, updateColorscale]);
105
95
  (0, _react.useEffect)(() => {
@@ -48,7 +48,7 @@ function Matrixplot(_ref) {
48
48
  const [hasSelections, setHasSelections] = (0, _react.useState)(false);
49
49
  const selectedObs = (0, _Resolver.useSelectedObs)();
50
50
  const selectedMultiVar = (0, _Resolver.useSelectedMultiVar)();
51
- const [params, setParams] = (0, _react.useState)({
51
+ const params = (0, _react.useMemo)(() => ({
52
52
  url: dataset.url,
53
53
  obsCol: selectedObs,
54
54
  obsValues: !(selectedObs !== null && selectedObs !== void 0 && selectedObs.omit.length) ? null : _lodash.default.difference(selectedObs === null || selectedObs === void 0 ? void 0 : selectedObs.values, selectedObs === null || selectedObs === void 0 ? void 0 : selectedObs.omit),
@@ -59,28 +59,14 @@ function Matrixplot(_ref) {
59
59
  obsIndices: isSliced ? [...(obsIndices || [])] : null,
60
60
  standardScale: settings.controls.scale.matrixplot,
61
61
  varNamesCol: dataset.varNamesCol
62
- });
62
+ }), [dataset.url, dataset.varNamesCol, isSliced, obsIndices, selectedMultiVar, selectedObs, settings.controls.scale.matrixplot]);
63
63
  (0, _react.useEffect)(() => {
64
64
  if (selectedObs && selectedMultiVar.length) {
65
65
  setHasSelections(true);
66
66
  } else {
67
67
  setHasSelections(false);
68
68
  }
69
- setParams(p => {
70
- return _objectSpread(_objectSpread({}, p), {}, {
71
- url: dataset.url,
72
- obsCol: selectedObs,
73
- obsValues: !(selectedObs !== null && selectedObs !== void 0 && selectedObs.omit.length) ? null : _lodash.default.difference(selectedObs === null || selectedObs === void 0 ? void 0 : selectedObs.values, selectedObs === null || selectedObs === void 0 ? void 0 : selectedObs.omit),
74
- varKeys: selectedMultiVar.map(i => i.isSet ? {
75
- name: i.name,
76
- indices: i.vars.map(v => v.index)
77
- } : i.index),
78
- obsIndices: isSliced ? [...(obsIndices || [])] : null,
79
- standardScale: settings.controls.scale.matrixplot,
80
- varNamesCol: dataset.varNamesCol
81
- });
82
- });
83
- }, [settings.controls.scale.matrixplot, selectedMultiVar, selectedObs, dataset.url, dataset.varNamesCol, obsIndices, isSliced]);
69
+ }, [selectedMultiVar.length, selectedObs]);
84
70
  const updateColorscale = (0, _react.useCallback)(colorscale => {
85
71
  setLayout(l => {
86
72
  return _objectSpread(_objectSpread({}, l), {}, {
@@ -95,13 +81,16 @@ function Matrixplot(_ref) {
95
81
  isPending,
96
82
  serverError
97
83
  } = (0, _requests.useDebouncedFetch)(ENDPOINT, params, 500, {
98
- enabled: !!params.obsCol && !!params.varKeys.length
84
+ isEnabled: params => !!params.obsCol && !!params.varKeys.length
99
85
  });
100
86
  (0, _react.useEffect)(() => {
101
- if (hasSelections && !isPending && !serverError) {
87
+ if (hasSelections && !!fetchedData && !isPending && !serverError) {
102
88
  setData(fetchedData.data);
103
89
  setLayout(fetchedData.layout);
104
90
  updateColorscale(colorscale.current);
91
+ } else {
92
+ setData([]);
93
+ setLayout({});
105
94
  }
106
95
  }, [fetchedData, hasSelections, isPending, serverError, updateColorscale]);
107
96
  (0, _react.useEffect)(() => {
@@ -52,7 +52,7 @@ function Violin(_ref) {
52
52
  const selectedMultiVar = (0, _Resolver.useSelectedMultiVar)();
53
53
  const selectedVar = (0, _Resolver.useSelectedVar)();
54
54
  const selectedObs = (0, _Resolver.useSelectedObs)();
55
- const [params, setParams] = (0, _react.useState)(_objectSpread({
55
+ const params = (0, _react.useMemo)(() => _objectSpread({
56
56
  url: dataset.url,
57
57
  mode: mode,
58
58
  scale: settings.controls.scale.violinplot,
@@ -74,7 +74,7 @@ function Violin(_ref) {
74
74
  obsValues: !(selectedObs !== null && selectedObs !== void 0 && selectedObs.omit.length) ? null : _lodash.default.difference(selectedObs === null || selectedObs === void 0 ? void 0 : selectedObs.values, selectedObs === null || selectedObs === void 0 ? void 0 : selectedObs.omit),
75
75
  obsIndices: isSliced ? [...(obsIndices || [])] : null
76
76
  }
77
- }[mode]));
77
+ }[mode]), [dataset.url, dataset.varNamesCol, isSliced, mode, obsIndices, selectedMultiVar, selectedObs, selectedVar === null || selectedVar === void 0 ? void 0 : selectedVar.index, selectedVar === null || selectedVar === void 0 ? void 0 : selectedVar.isSet, selectedVar === null || selectedVar === void 0 ? void 0 : selectedVar.name, selectedVar === null || selectedVar === void 0 ? void 0 : selectedVar.vars, settings.controls.scale.violinplot]);
78
78
  // @TODO: set default scale
79
79
 
80
80
  (0, _react.useEffect)(() => {
@@ -84,39 +84,12 @@ function Violin(_ref) {
84
84
  } else {
85
85
  setHasSelections(false);
86
86
  }
87
- setParams(p => {
88
- return _objectSpread(_objectSpread({}, p), {}, {
89
- url: dataset.url,
90
- mode: mode,
91
- varKeys: selectedMultiVar.map(i => i.isSet ? {
92
- name: i.name,
93
- indices: i.vars.map(v => v.index)
94
- } : i.index),
95
- scale: settings.controls.scale.violinplot,
96
- varNamesCol: dataset.varNamesCol
97
- });
98
- });
99
87
  } else if (mode === _constants.VIOLIN_MODES.GROUPBY) {
100
88
  if (selectedObs && selectedVar) {
101
89
  setHasSelections(true);
102
90
  } else {
103
91
  setHasSelections(false);
104
92
  }
105
- setParams(p => {
106
- return _objectSpread(_objectSpread({}, p), {}, {
107
- url: dataset.url,
108
- mode: mode,
109
- varKey: selectedVar !== null && selectedVar !== void 0 && selectedVar.isSet ? {
110
- name: selectedVar === null || selectedVar === void 0 ? void 0 : selectedVar.name,
111
- indices: selectedVar === null || selectedVar === void 0 ? void 0 : selectedVar.vars.map(v => v.index)
112
- } : selectedVar === null || selectedVar === void 0 ? void 0 : selectedVar.index,
113
- obsCol: selectedObs,
114
- obsValues: !(selectedObs !== null && selectedObs !== void 0 && selectedObs.omit.length) ? null : _lodash.default.difference(selectedObs === null || selectedObs === void 0 ? void 0 : selectedObs.values, selectedObs === null || selectedObs === void 0 ? void 0 : selectedObs.omit),
115
- obsIndices: isSliced ? [...(obsIndices || [])] : null,
116
- scale: settings.controls.scale.violinplot,
117
- varNamesCol: dataset.varNamesCol
118
- });
119
- });
120
93
  }
121
94
  }, [settings.controls.scale.violinplot, selectedMultiVar, selectedObs, selectedVar, dataset.url, dataset.varNamesCol, obsIndices, isSliced, mode]);
122
95
  const {
@@ -124,12 +97,15 @@ function Violin(_ref) {
124
97
  isPending,
125
98
  serverError
126
99
  } = (0, _requests.useDebouncedFetch)(ENDPOINT, params, 500, {
127
- enabled: mode === _constants.VIOLIN_MODES.MULTIKEY && (!!params.varKeys.length || !!params.obsKeys.length) || mode === _constants.VIOLIN_MODES.GROUPBY && !!params.varKey && !!params.obsCol
100
+ isEnabled: mode === _constants.VIOLIN_MODES.MULTIKEY && (params => !!params.varKeys.length || !!params.obsKeys.length) || mode === _constants.VIOLIN_MODES.GROUPBY && (params => !!params.varKey && !!params.obsCol)
128
101
  });
129
102
  (0, _react.useEffect)(() => {
130
- if (hasSelections && !isPending && !serverError) {
103
+ if (hasSelections && !!fetchedData && !isPending && !serverError) {
131
104
  setData(fetchedData.data);
132
105
  setLayout(fetchedData.layout);
106
+ } else {
107
+ setData([]);
108
+ setLayout({});
133
109
  }
134
110
  }, [fetchedData, hasSelections, isPending, serverError]);
135
111
  const customModeBarButtons = _lodash.default.compact([showObsBtn && (0, _Toolbar.ObsPlotlyToolbar)({
@@ -60,7 +60,7 @@ const persistOptions = {
60
60
  return false;
61
61
  }
62
62
  },
63
- buster: "1.4.1-dev.2025-10-23.3179e534" || "0.0.0"
63
+ buster: "1.4.1-dev.2025-10-23.e95b5a90" || "0.0.0"
64
64
  // @TODO: add maxAge and api version numbers as buster
65
65
  };
66
66
  const initialDataset = {
@@ -173,7 +173,7 @@ function SettingsProvider(_ref2) {
173
173
 
174
174
  // If the buster is not set or does not match the current package version,
175
175
  // reset localSettings to avoid stale data
176
- if (!buster || buster !== "1.4.1-dev.2025-10-23.3179e534") {
176
+ if (!buster || buster !== "1.4.1-dev.2025-10-23.e95b5a90") {
177
177
  localSettings = {};
178
178
  }
179
179
  const initSettings = (0, _react.useRef)(initializer({
@@ -198,7 +198,7 @@ function SettingsProvider(_ref2) {
198
198
  if (canOverrideSettings && settings) {
199
199
  try {
200
200
  localStorage.setItem(DATASET_STORAGE_KEY, JSON.stringify(_objectSpread({
201
- buster: "1.4.1-dev.2025-10-23.3179e534" || "0.0.0",
201
+ buster: "1.4.1-dev.2025-10-23.e95b5a90" || "0.0.0",
202
202
  timestamp: Date.now()
203
203
  }, _lodash.default.omit(settings, "data"))));
204
204
  } catch (err) {
@@ -89,15 +89,17 @@ const useDebouncedFetch = function (endpoint, params) {
89
89
  refetchOnWindowFocus: false
90
90
  };
91
91
  let apiUrl = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : null;
92
+ // Optional isEnabled function to determine if enabled based on debouncedParams instead of params
92
93
  const {
93
- retry = null
94
+ enabled = true,
95
+ isEnabled = () => true
94
96
  } = opts;
95
97
  const debouncedParams = (0, _usehooks.useDebounce)(params, delay);
96
98
  const {
97
99
  data: fetchedData = null,
98
100
  isLoading: isPending = false,
99
101
  error: serverError = null
100
- } = (0, _reactQuery.useQuery)(_objectSpread({
102
+ } = (0, _reactQuery.useQuery)(_objectSpread(_objectSpread({
101
103
  queryKey: [endpoint, debouncedParams],
102
104
  queryFn: _ref3 => {
103
105
  let {
@@ -105,14 +107,16 @@ const useDebouncedFetch = function (endpoint, params) {
105
107
  } = _ref3;
106
108
  return fetchData(endpoint, debouncedParams, signal, apiUrl);
107
109
  },
108
- retry: retry || ((failureCount, _ref4) => {
110
+ retry: (failureCount, _ref4) => {
109
111
  let {
110
112
  error
111
113
  } = _ref4;
112
114
  if ([400, 401, 403, 404, 422].includes(error === null || error === void 0 ? void 0 : error.status)) return false;
113
115
  return failureCount < 3;
114
- })
115
- }, opts));
116
+ }
117
+ }, opts), {}, {
118
+ enabled: enabled && isEnabled(debouncedParams)
119
+ }));
116
120
  return {
117
121
  fetchedData,
118
122
  isPending,
@@ -3,7 +3,7 @@ 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 { useCallback, useEffect, useRef, useState } from "react";
6
+ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
7
7
  import _ from "lodash";
8
8
  import { Button } from "react-bootstrap";
9
9
  import Plot from "react-plotly.js";
@@ -42,7 +42,7 @@ export function Dotplot(_ref) {
42
42
  const [hasSelections, setHasSelections] = useState(false);
43
43
  const selectedObs = useSelectedObs();
44
44
  const selectedMultiVar = useSelectedMultiVar();
45
- const [params, setParams] = useState({
45
+ const params = useMemo(() => ({
46
46
  url: dataset.url,
47
47
  obsCol: selectedObs,
48
48
  obsValues: !(selectedObs !== null && selectedObs !== void 0 && selectedObs.omit.length) ? null : _.difference(selectedObs === null || selectedObs === void 0 ? void 0 : selectedObs.values, selectedObs === null || selectedObs === void 0 ? void 0 : selectedObs.omit),
@@ -55,7 +55,7 @@ export function Dotplot(_ref) {
55
55
  meanOnlyExpressed: settings.controls.meanOnlyExpressed,
56
56
  expressionCutoff: settings.controls.expressionCutoff,
57
57
  varNamesCol: dataset.varNamesCol
58
- });
58
+ }), [dataset.url, dataset.varNamesCol, isSliced, obsIndices, selectedMultiVar, selectedObs, settings.controls.expressionCutoff, settings.controls.meanOnlyExpressed, settings.controls.scale.dotplot]);
59
59
  // @TODO: set default scale
60
60
 
61
61
  useEffect(() => {
@@ -64,22 +64,6 @@ export function Dotplot(_ref) {
64
64
  } else {
65
65
  setHasSelections(false);
66
66
  }
67
- setParams(p => {
68
- return _objectSpread(_objectSpread({}, p), {}, {
69
- url: dataset.url,
70
- obsCol: selectedObs,
71
- obsValues: !(selectedObs !== null && selectedObs !== void 0 && selectedObs.omit.length) ? null : _.difference(selectedObs === null || selectedObs === void 0 ? void 0 : selectedObs.values, selectedObs === null || selectedObs === void 0 ? void 0 : selectedObs.omit),
72
- varKeys: selectedMultiVar.map(i => i.isSet ? {
73
- name: i.name,
74
- indices: i.vars.map(v => v.index)
75
- } : i.index),
76
- obsIndices: isSliced ? [...(obsIndices || [])] : null,
77
- standardScale: settings.controls.scale.dotplot,
78
- meanOnlyExpressed: settings.controls.meanOnlyExpressed,
79
- expressionCutoff: settings.controls.expressionCutoff,
80
- varNamesCol: dataset.varNamesCol
81
- });
82
- });
83
67
  }, [dataset.url, selectedObs, settings.controls.scale.dotplot, settings.controls.meanOnlyExpressed, settings.controls.expressionCutoff, dataset.varNamesCol, isSliced, obsIndices, selectedMultiVar]);
84
68
  const updateColorscale = useCallback(colorscale => {
85
69
  setLayout(l => {
@@ -95,10 +79,10 @@ export function Dotplot(_ref) {
95
79
  isPending,
96
80
  serverError
97
81
  } = useDebouncedFetch(ENDPOINT, params, 500, {
98
- enabled: !!params.obsCol && !!params.varKeys.length
82
+ isEnabled: params => !!params.obsCol && !!params.varKeys.length
99
83
  });
100
84
  useEffect(() => {
101
- if (hasSelections && !isPending && !serverError) {
85
+ if (hasSelections && !!fetchedData && !isPending && !serverError) {
102
86
  setData(fetchedData.data);
103
87
  setLayout(fetchedData.layout);
104
88
  // @TODO: keep colorAxis range from settings
@@ -112,6 +96,9 @@ export function Dotplot(_ref) {
112
96
  }
113
97
  });
114
98
  updateColorscale(colorscale.current);
99
+ } else {
100
+ setData([]);
101
+ setLayout({});
115
102
  }
116
103
  }, [fetchedData, isPending, serverError, hasSelections, dispatch, updateColorscale]);
117
104
  useEffect(() => {
@@ -3,7 +3,7 @@ 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 { useCallback, useEffect, useRef, useState } from "react";
6
+ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
7
7
  import _ from "lodash";
8
8
  import { Button } from "react-bootstrap";
9
9
  import Plot from "react-plotly.js";
@@ -41,7 +41,7 @@ export function Heatmap(_ref) {
41
41
  const [hasSelections, setHasSelections] = useState(false);
42
42
  const selectedObs = useSelectedObs();
43
43
  const selectedMultiVar = useSelectedMultiVar();
44
- const [params, setParams] = useState({
44
+ const params = useMemo(() => ({
45
45
  url: dataset.url,
46
46
  obsCol: selectedObs,
47
47
  obsValues: !(selectedObs !== null && selectedObs !== void 0 && selectedObs.omit.length) ? null : _.difference(selectedObs === null || selectedObs === void 0 ? void 0 : selectedObs.values, selectedObs === null || selectedObs === void 0 ? void 0 : selectedObs.omit),
@@ -51,26 +51,13 @@ export function Heatmap(_ref) {
51
51
  } : i.index),
52
52
  obsIndices: isSliced ? [...(obsIndices || [])] : null,
53
53
  varNamesCol: dataset.varNamesCol
54
- });
54
+ }), [dataset.url, dataset.varNamesCol, isSliced, obsIndices, selectedMultiVar, selectedObs]);
55
55
  useEffect(() => {
56
56
  if (selectedObs && selectedMultiVar.length) {
57
57
  setHasSelections(true);
58
58
  } else {
59
59
  setHasSelections(false);
60
60
  }
61
- setParams(p => {
62
- return _objectSpread(_objectSpread({}, p), {}, {
63
- url: dataset.url,
64
- obsCol: selectedObs,
65
- obsValues: !(selectedObs !== null && selectedObs !== void 0 && selectedObs.omit.length) ? null : _.difference(selectedObs === null || selectedObs === void 0 ? void 0 : selectedObs.values, selectedObs === null || selectedObs === void 0 ? void 0 : selectedObs.omit),
66
- varKeys: selectedMultiVar.map(i => i.isSet ? {
67
- name: i.name,
68
- indices: i.vars.map(v => v.index)
69
- } : i.index),
70
- obsIndices: isSliced ? [...(obsIndices || [])] : null,
71
- varNamesCol: dataset.varNamesCol
72
- });
73
- });
74
61
  }, [selectedMultiVar, selectedObs, dataset.url, dataset.varNamesCol, obsIndices, isSliced]);
75
62
  const updateColorscale = useCallback(colorscale => {
76
63
  setLayout(l => {
@@ -86,13 +73,16 @@ export function Heatmap(_ref) {
86
73
  isPending,
87
74
  serverError
88
75
  } = useDebouncedFetch(ENDPOINT, params, 500, {
89
- enabled: !!params.obsCol && !!params.varKeys.length
76
+ isEnabled: params => !!params.obsCol && !!params.varKeys.length
90
77
  });
91
78
  useEffect(() => {
92
- if (hasSelections && !isPending && !serverError) {
79
+ if (hasSelections && !!fetchedData && !isPending && !serverError) {
93
80
  setData(fetchedData.data);
94
81
  setLayout(fetchedData.layout);
95
82
  updateColorscale(colorscale.current);
83
+ } else {
84
+ setData([]);
85
+ setLayout({});
96
86
  }
97
87
  }, [fetchedData, hasSelections, isPending, serverError, updateColorscale]);
98
88
  useEffect(() => {
@@ -3,7 +3,7 @@ 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 { useCallback, useEffect, useRef, useState } from "react";
6
+ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
7
7
  import _ from "lodash";
8
8
  import { Button } from "react-bootstrap";
9
9
  import Plot from "react-plotly.js";
@@ -41,7 +41,7 @@ export function Matrixplot(_ref) {
41
41
  const [hasSelections, setHasSelections] = useState(false);
42
42
  const selectedObs = useSelectedObs();
43
43
  const selectedMultiVar = useSelectedMultiVar();
44
- const [params, setParams] = useState({
44
+ const params = useMemo(() => ({
45
45
  url: dataset.url,
46
46
  obsCol: selectedObs,
47
47
  obsValues: !(selectedObs !== null && selectedObs !== void 0 && selectedObs.omit.length) ? null : _.difference(selectedObs === null || selectedObs === void 0 ? void 0 : selectedObs.values, selectedObs === null || selectedObs === void 0 ? void 0 : selectedObs.omit),
@@ -52,28 +52,14 @@ export function Matrixplot(_ref) {
52
52
  obsIndices: isSliced ? [...(obsIndices || [])] : null,
53
53
  standardScale: settings.controls.scale.matrixplot,
54
54
  varNamesCol: dataset.varNamesCol
55
- });
55
+ }), [dataset.url, dataset.varNamesCol, isSliced, obsIndices, selectedMultiVar, selectedObs, settings.controls.scale.matrixplot]);
56
56
  useEffect(() => {
57
57
  if (selectedObs && selectedMultiVar.length) {
58
58
  setHasSelections(true);
59
59
  } else {
60
60
  setHasSelections(false);
61
61
  }
62
- setParams(p => {
63
- return _objectSpread(_objectSpread({}, p), {}, {
64
- url: dataset.url,
65
- obsCol: selectedObs,
66
- obsValues: !(selectedObs !== null && selectedObs !== void 0 && selectedObs.omit.length) ? null : _.difference(selectedObs === null || selectedObs === void 0 ? void 0 : selectedObs.values, selectedObs === null || selectedObs === void 0 ? void 0 : selectedObs.omit),
67
- varKeys: selectedMultiVar.map(i => i.isSet ? {
68
- name: i.name,
69
- indices: i.vars.map(v => v.index)
70
- } : i.index),
71
- obsIndices: isSliced ? [...(obsIndices || [])] : null,
72
- standardScale: settings.controls.scale.matrixplot,
73
- varNamesCol: dataset.varNamesCol
74
- });
75
- });
76
- }, [settings.controls.scale.matrixplot, selectedMultiVar, selectedObs, dataset.url, dataset.varNamesCol, obsIndices, isSliced]);
62
+ }, [selectedMultiVar.length, selectedObs]);
77
63
  const updateColorscale = useCallback(colorscale => {
78
64
  setLayout(l => {
79
65
  return _objectSpread(_objectSpread({}, l), {}, {
@@ -88,13 +74,16 @@ export function Matrixplot(_ref) {
88
74
  isPending,
89
75
  serverError
90
76
  } = useDebouncedFetch(ENDPOINT, params, 500, {
91
- enabled: !!params.obsCol && !!params.varKeys.length
77
+ isEnabled: params => !!params.obsCol && !!params.varKeys.length
92
78
  });
93
79
  useEffect(() => {
94
- if (hasSelections && !isPending && !serverError) {
80
+ if (hasSelections && !!fetchedData && !isPending && !serverError) {
95
81
  setData(fetchedData.data);
96
82
  setLayout(fetchedData.layout);
97
83
  updateColorscale(colorscale.current);
84
+ } else {
85
+ setData([]);
86
+ setLayout({});
98
87
  }
99
88
  }, [fetchedData, hasSelections, isPending, serverError, updateColorscale]);
100
89
  useEffect(() => {
@@ -3,7 +3,7 @@ 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 { useEffect, useState } from "react";
6
+ import { useEffect, useMemo, 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";
@@ -45,7 +45,7 @@ export function Violin(_ref) {
45
45
  const selectedMultiVar = useSelectedMultiVar();
46
46
  const selectedVar = useSelectedVar();
47
47
  const selectedObs = useSelectedObs();
48
- const [params, setParams] = useState(_objectSpread({
48
+ const params = useMemo(() => _objectSpread({
49
49
  url: dataset.url,
50
50
  mode: mode,
51
51
  scale: settings.controls.scale.violinplot,
@@ -67,7 +67,7 @@ export function Violin(_ref) {
67
67
  obsValues: !(selectedObs !== null && selectedObs !== void 0 && selectedObs.omit.length) ? null : _.difference(selectedObs === null || selectedObs === void 0 ? void 0 : selectedObs.values, selectedObs === null || selectedObs === void 0 ? void 0 : selectedObs.omit),
68
68
  obsIndices: isSliced ? [...(obsIndices || [])] : null
69
69
  }
70
- }[mode]));
70
+ }[mode]), [dataset.url, dataset.varNamesCol, isSliced, mode, obsIndices, selectedMultiVar, selectedObs, selectedVar === null || selectedVar === void 0 ? void 0 : selectedVar.index, selectedVar === null || selectedVar === void 0 ? void 0 : selectedVar.isSet, selectedVar === null || selectedVar === void 0 ? void 0 : selectedVar.name, selectedVar === null || selectedVar === void 0 ? void 0 : selectedVar.vars, settings.controls.scale.violinplot]);
71
71
  // @TODO: set default scale
72
72
 
73
73
  useEffect(() => {
@@ -77,39 +77,12 @@ export function Violin(_ref) {
77
77
  } else {
78
78
  setHasSelections(false);
79
79
  }
80
- setParams(p => {
81
- return _objectSpread(_objectSpread({}, p), {}, {
82
- url: dataset.url,
83
- mode: mode,
84
- varKeys: selectedMultiVar.map(i => i.isSet ? {
85
- name: i.name,
86
- indices: i.vars.map(v => v.index)
87
- } : i.index),
88
- scale: settings.controls.scale.violinplot,
89
- varNamesCol: dataset.varNamesCol
90
- });
91
- });
92
80
  } else if (mode === VIOLIN_MODES.GROUPBY) {
93
81
  if (selectedObs && selectedVar) {
94
82
  setHasSelections(true);
95
83
  } else {
96
84
  setHasSelections(false);
97
85
  }
98
- setParams(p => {
99
- return _objectSpread(_objectSpread({}, p), {}, {
100
- url: dataset.url,
101
- mode: mode,
102
- varKey: selectedVar !== null && selectedVar !== void 0 && selectedVar.isSet ? {
103
- name: selectedVar === null || selectedVar === void 0 ? void 0 : selectedVar.name,
104
- indices: selectedVar === null || selectedVar === void 0 ? void 0 : selectedVar.vars.map(v => v.index)
105
- } : selectedVar === null || selectedVar === void 0 ? void 0 : selectedVar.index,
106
- obsCol: selectedObs,
107
- obsValues: !(selectedObs !== null && selectedObs !== void 0 && selectedObs.omit.length) ? null : _.difference(selectedObs === null || selectedObs === void 0 ? void 0 : selectedObs.values, selectedObs === null || selectedObs === void 0 ? void 0 : selectedObs.omit),
108
- obsIndices: isSliced ? [...(obsIndices || [])] : null,
109
- scale: settings.controls.scale.violinplot,
110
- varNamesCol: dataset.varNamesCol
111
- });
112
- });
113
86
  }
114
87
  }, [settings.controls.scale.violinplot, selectedMultiVar, selectedObs, selectedVar, dataset.url, dataset.varNamesCol, obsIndices, isSliced, mode]);
115
88
  const {
@@ -117,12 +90,15 @@ export function Violin(_ref) {
117
90
  isPending,
118
91
  serverError
119
92
  } = useDebouncedFetch(ENDPOINT, params, 500, {
120
- enabled: mode === VIOLIN_MODES.MULTIKEY && (!!params.varKeys.length || !!params.obsKeys.length) || mode === VIOLIN_MODES.GROUPBY && !!params.varKey && !!params.obsCol
93
+ isEnabled: mode === VIOLIN_MODES.MULTIKEY && (params => !!params.varKeys.length || !!params.obsKeys.length) || mode === VIOLIN_MODES.GROUPBY && (params => !!params.varKey && !!params.obsCol)
121
94
  });
122
95
  useEffect(() => {
123
- if (hasSelections && !isPending && !serverError) {
96
+ if (hasSelections && !!fetchedData && !isPending && !serverError) {
124
97
  setData(fetchedData.data);
125
98
  setLayout(fetchedData.layout);
99
+ } else {
100
+ setData([]);
101
+ setLayout({});
126
102
  }
127
103
  }, [fetchedData, hasSelections, isPending, serverError]);
128
104
  const customModeBarButtons = _.compact([showObsBtn && ObsPlotlyToolbar({
@@ -51,7 +51,7 @@ const persistOptions = {
51
51
  return false;
52
52
  }
53
53
  },
54
- buster: "1.4.1-dev.2025-10-23.3179e534" || "0.0.0"
54
+ buster: "1.4.1-dev.2025-10-23.e95b5a90" || "0.0.0"
55
55
  // @TODO: add maxAge and api version numbers as buster
56
56
  };
57
57
  const initialDataset = {
@@ -163,7 +163,7 @@ export function SettingsProvider(_ref2) {
163
163
 
164
164
  // If the buster is not set or does not match the current package version,
165
165
  // reset localSettings to avoid stale data
166
- if (!buster || buster !== "1.4.1-dev.2025-10-23.3179e534") {
166
+ if (!buster || buster !== "1.4.1-dev.2025-10-23.e95b5a90") {
167
167
  localSettings = {};
168
168
  }
169
169
  const initSettings = useRef(initializer({
@@ -188,7 +188,7 @@ export function SettingsProvider(_ref2) {
188
188
  if (canOverrideSettings && settings) {
189
189
  try {
190
190
  localStorage.setItem(DATASET_STORAGE_KEY, JSON.stringify(_objectSpread({
191
- buster: "1.4.1-dev.2025-10-23.3179e534" || "0.0.0",
191
+ buster: "1.4.1-dev.2025-10-23.e95b5a90" || "0.0.0",
192
192
  timestamp: Date.now()
193
193
  }, _.omit(settings, "data"))));
194
194
  } catch (err) {
@@ -81,15 +81,17 @@ export const useDebouncedFetch = function (endpoint, params) {
81
81
  refetchOnWindowFocus: false
82
82
  };
83
83
  let apiUrl = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : null;
84
+ // Optional isEnabled function to determine if enabled based on debouncedParams instead of params
84
85
  const {
85
- retry = null
86
+ enabled = true,
87
+ isEnabled = () => true
86
88
  } = opts;
87
89
  const debouncedParams = useDebounce(params, delay);
88
90
  const {
89
91
  data: fetchedData = null,
90
92
  isLoading: isPending = false,
91
93
  error: serverError = null
92
- } = useQuery(_objectSpread({
94
+ } = useQuery(_objectSpread(_objectSpread({
93
95
  queryKey: [endpoint, debouncedParams],
94
96
  queryFn: _ref3 => {
95
97
  let {
@@ -97,14 +99,16 @@ export const useDebouncedFetch = function (endpoint, params) {
97
99
  } = _ref3;
98
100
  return fetchData(endpoint, debouncedParams, signal, apiUrl);
99
101
  },
100
- retry: retry || ((failureCount, _ref4) => {
102
+ retry: (failureCount, _ref4) => {
101
103
  let {
102
104
  error
103
105
  } = _ref4;
104
106
  if ([400, 401, 403, 404, 422].includes(error === null || error === void 0 ? void 0 : error.status)) return false;
105
107
  return failureCount < 3;
106
- })
107
- }, opts));
108
+ }
109
+ }, opts), {}, {
110
+ enabled: enabled && isEnabled(debouncedParams)
111
+ }));
108
112
  return {
109
113
  fetchedData,
110
114
  isPending,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@haniffalab/cherita-react",
3
- "version": "1.4.1-dev.2025-10-23.3179e534",
3
+ "version": "1.4.1-dev.2025-10-23.e95b5a90",
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": "3179e53458e4da1158925c45812fd62956ccd143"
131
+ "prereleaseSha": "e95b5a903f92bf1e0c03c34b0b16f6dfaeab333c"
132
132
  }