@haniffalab/cherita-react 1.4.0 → 1.4.1-dev.2025-06-30.e26168b5
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.
- package/dist/cjs/components/full-page/FullPage.js +10 -42
- package/dist/cjs/components/full-page/PlotTypeSelector.js +4 -4
- package/dist/cjs/components/obs-list/ObsItem.js +27 -13
- package/dist/cjs/components/obs-list/ObsList.js +6 -2
- package/dist/cjs/components/obsm-list/ObsmList.js +30 -6
- package/dist/cjs/components/offcanvas/index.js +9 -4
- package/dist/cjs/components/scatterplot/Scatterplot.js +1 -1
- package/dist/cjs/components/search-bar/SearchBar.js +10 -1
- package/dist/cjs/components/search-bar/SearchResults.js +2 -2
- package/dist/cjs/components/var-list/VarItem.js +26 -25
- package/dist/cjs/components/var-list/VarList.js +3 -5
- package/dist/cjs/components/violin/Violin.js +15 -5
- package/dist/cjs/constants/constants.js +5 -2
- package/dist/cjs/context/DatasetContext.js +1 -1
- package/dist/cjs/context/SettingsContext.js +19 -2
- package/dist/cjs/utils/VirtualizedList.js +2 -2
- package/dist/css/cherita.css +85 -37
- package/dist/css/cherita.css.map +1 -1
- package/dist/esm/components/full-page/FullPage.js +11 -43
- package/dist/esm/components/full-page/PlotTypeSelector.js +4 -4
- package/dist/esm/components/obs-list/ObsItem.js +27 -13
- package/dist/esm/components/obs-list/ObsList.js +6 -2
- package/dist/esm/components/obsm-list/ObsmList.js +29 -6
- package/dist/esm/components/offcanvas/index.js +9 -4
- package/dist/esm/components/scatterplot/Scatterplot.js +1 -1
- package/dist/esm/components/search-bar/SearchBar.js +10 -1
- package/dist/esm/components/search-bar/SearchResults.js +2 -2
- package/dist/esm/components/var-list/VarItem.js +26 -25
- package/dist/esm/components/var-list/VarList.js +3 -5
- package/dist/esm/components/violin/Violin.js +15 -5
- package/dist/esm/constants/constants.js +4 -1
- package/dist/esm/context/DatasetContext.js +1 -1
- package/dist/esm/context/SettingsContext.js +19 -2
- package/dist/esm/utils/VirtualizedList.js +2 -2
- package/package.json +4 -3
- package/scss/cherita.scss +23 -0
- package/scss/components/accordions.scss +11 -1
- package/scss/components/layouts.scss +68 -32
- package/scss/components/lists.scss +8 -1
|
@@ -165,19 +165,20 @@ function CategoricalItem(_ref2) {
|
|
|
165
165
|
key: value,
|
|
166
166
|
className: "obs-item"
|
|
167
167
|
}, /*#__PURE__*/React.createElement("div", {
|
|
168
|
-
className: "d-flex align-items-center"
|
|
168
|
+
className: "d-flex align-items-center flex-wrap"
|
|
169
169
|
}, /*#__PURE__*/React.createElement("div", {
|
|
170
|
-
className: "flex-grow-1"
|
|
170
|
+
className: "flex-grow-1 me-auto mw-100"
|
|
171
171
|
}, /*#__PURE__*/React.createElement(Form.Check, {
|
|
172
172
|
className: "obs-value-list-check",
|
|
173
173
|
type: "switch",
|
|
174
|
+
title: label,
|
|
174
175
|
label: label,
|
|
175
176
|
checked: !isOmitted,
|
|
176
177
|
onChange: () => onChange(value)
|
|
177
178
|
})), /*#__PURE__*/React.createElement("div", {
|
|
178
|
-
className: "d-flex align-items-center"
|
|
179
|
-
}, /*#__PURE__*/React.createElement("div", {
|
|
180
|
-
className: "pl-1 m-0
|
|
179
|
+
className: "d-flex align-items-center ms-auto"
|
|
180
|
+
}, (!!histogramData.data || histogramData.isPending) && /*#__PURE__*/React.createElement("div", {
|
|
181
|
+
className: "pl-1 m-0"
|
|
181
182
|
}, /*#__PURE__*/React.createElement(Histogram, {
|
|
182
183
|
data: histogramData.data,
|
|
183
184
|
isPending: histogramData.isPending,
|
|
@@ -192,7 +193,7 @@ function CategoricalItem(_ref2) {
|
|
|
192
193
|
className: "d-flex align-items-center"
|
|
193
194
|
}, /*#__PURE__*/React.createElement(Badge, {
|
|
194
195
|
className: "value-count-badge"
|
|
195
|
-
}, " ", isSliced && /*#__PURE__*/React.createElement(React.Fragment, null, formatNumerical(parseInt(filteredStats.value_counts)), " ", "out of", " "), formatNumerical(parseInt(stats.value_counts), FORMATS.EXPONENTIAL)), /*#__PURE__*/React.createElement("div", {
|
|
196
|
+
}, " ", isSliced && parseInt(filteredStats.value_counts) !== parseInt(stats.value_counts) && /*#__PURE__*/React.createElement(React.Fragment, null, formatNumerical(parseInt(filteredStats.value_counts)), " ", "out of", " "), formatNumerical(parseInt(stats.value_counts), FORMATS.EXPONENTIAL)), /*#__PURE__*/React.createElement("div", {
|
|
196
197
|
className: "value-pct-gauge-container"
|
|
197
198
|
}, isSliced ? /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Gauge, {
|
|
198
199
|
className: "pct-gauge filtered-pct-gauge",
|
|
@@ -272,6 +273,7 @@ export function CategoricalObs(_ref3) {
|
|
|
272
273
|
enabled: showHistograms
|
|
273
274
|
});
|
|
274
275
|
const filteredObsData = useFilteredObsData(obs);
|
|
276
|
+
const enabledHistograms = useMemo(() => showHistograms && settings.colorEncoding === COLOR_ENCODINGS.VAR, [settings.colorEncoding, showHistograms]);
|
|
275
277
|
const getDataAtIndex = useCallback(index => {
|
|
276
278
|
var _obsHistograms$fetche;
|
|
277
279
|
return {
|
|
@@ -283,7 +285,7 @@ export function CategoricalObs(_ref3) {
|
|
|
283
285
|
},
|
|
284
286
|
isOmitted: _.includes(obs.omit, obs.codes[obs.values[index]]),
|
|
285
287
|
label: obs.values[index],
|
|
286
|
-
histogramData:
|
|
288
|
+
histogramData: enabledHistograms ? {
|
|
287
289
|
data: (_obsHistograms$fetche = obsHistograms.fetchedData) === null || _obsHistograms$fetche === void 0 ? void 0 : _obsHistograms$fetche[obs.values[index]],
|
|
288
290
|
isPending: obsHistograms.isPending,
|
|
289
291
|
altColor: isSliced
|
|
@@ -298,7 +300,7 @@ export function CategoricalObs(_ref3) {
|
|
|
298
300
|
isSliced: isSliced,
|
|
299
301
|
colors: obs.colors
|
|
300
302
|
};
|
|
301
|
-
}, [obs.values, obs.codes, obs.value_counts, obs.omit,
|
|
303
|
+
}, [obs.values, obs.codes, obs.value_counts, obs.omit, obs.colors, totalCounts, enabledHistograms, obsHistograms.fetchedData, obsHistograms.isPending, isSliced, filteredObsData === null || filteredObsData === void 0 ? void 0 : filteredObsData.value_counts, filteredObsData === null || filteredObsData === void 0 ? void 0 : filteredObsData.pct]);
|
|
302
304
|
showColor &= settings.colorEncoding === COLOR_ENCODINGS.OBS;
|
|
303
305
|
return /*#__PURE__*/React.createElement(ListGroup, {
|
|
304
306
|
variant: "flush",
|
|
@@ -317,7 +319,11 @@ export function CategoricalObs(_ref3) {
|
|
|
317
319
|
max: max,
|
|
318
320
|
onChange: toggleObs,
|
|
319
321
|
showColor: showColor,
|
|
320
|
-
estimateSize:
|
|
322
|
+
estimateSize: i =>
|
|
323
|
+
// rough attempt to determine size based on label length
|
|
324
|
+
// estimate size of 68 pixels if label is long (>=25 chars if enabledHistograms, >=30 if showColor, >=35 otherwise), else 42
|
|
325
|
+
// TODO: consider isSliced as count badge will be longer ?
|
|
326
|
+
obs.values[i].length >= (enabledHistograms ? 25 : showColor ? 30 : 35) ? 68 : 42
|
|
321
327
|
}));
|
|
322
328
|
}
|
|
323
329
|
function ObsContinuousStats(_ref4) {
|
|
@@ -401,6 +407,10 @@ export function ContinuousObs(_ref5) {
|
|
|
401
407
|
enabled: showHistograms
|
|
402
408
|
});
|
|
403
409
|
const filteredObsData = useFilteredObsData(obs);
|
|
410
|
+
const enabledHistograms = useMemo(() => showHistograms && settings.colorEncoding === COLOR_ENCODINGS.VAR, [settings.colorEncoding, showHistograms]);
|
|
411
|
+
const getLabel = useCallback(index => {
|
|
412
|
+
return isNaN(obs.values[index]) ? "NaN" : getContinuousLabel(obs.codes[obs.values[index]], obs.bins.binEdges);
|
|
413
|
+
}, [obs.bins.binEdges, obs.codes, obs.values]);
|
|
404
414
|
const getDataAtIndex = useCallback(index => {
|
|
405
415
|
var _obsHistograms$fetche2;
|
|
406
416
|
return {
|
|
@@ -411,8 +421,8 @@ export function ContinuousObs(_ref5) {
|
|
|
411
421
|
pct: obs.value_counts[obs.values[index]] / totalCounts * 100
|
|
412
422
|
},
|
|
413
423
|
isOmitted: _.includes(obs.omit, obs.codes[obs.values[index]]),
|
|
414
|
-
label:
|
|
415
|
-
histogramData:
|
|
424
|
+
label: getLabel(index),
|
|
425
|
+
histogramData: enabledHistograms ? {
|
|
416
426
|
data: (_obsHistograms$fetche2 = obsHistograms.fetchedData) === null || _obsHistograms$fetche2 === void 0 ? void 0 : _obsHistograms$fetche2[obs.values[index]],
|
|
417
427
|
isPending: obsHistograms.isPending,
|
|
418
428
|
altColor: isSliced
|
|
@@ -426,7 +436,7 @@ export function ContinuousObs(_ref5) {
|
|
|
426
436
|
},
|
|
427
437
|
isSliced: isSliced
|
|
428
438
|
};
|
|
429
|
-
}, [filteredObsData === null || filteredObsData === void 0 ? void 0 : filteredObsData.pct, filteredObsData === null || filteredObsData === void 0 ? void 0 : filteredObsData.value_counts,
|
|
439
|
+
}, [enabledHistograms, filteredObsData === null || filteredObsData === void 0 ? void 0 : filteredObsData.pct, filteredObsData === null || filteredObsData === void 0 ? void 0 : filteredObsData.value_counts, getLabel, isSliced, obs.codes, obs.omit, obs.value_counts, obs.values, obsHistograms.fetchedData, obsHistograms.isPending, totalCounts]);
|
|
430
440
|
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(ListGroup, {
|
|
431
441
|
variant: "flush",
|
|
432
442
|
className: "cherita-list"
|
|
@@ -444,7 +454,11 @@ export function ContinuousObs(_ref5) {
|
|
|
444
454
|
max: max,
|
|
445
455
|
onChange: toggleObs,
|
|
446
456
|
showColor: false,
|
|
447
|
-
estimateSize:
|
|
457
|
+
estimateSize: i =>
|
|
458
|
+
// rough attempt to determine size based on label length
|
|
459
|
+
// estimate size of 68 pixels if label is long (>=20 chars if enabledHistograms, >=30 otherwise), else 42
|
|
460
|
+
// TODO: consider isSliced as count badge will be longer ?
|
|
461
|
+
getLabel(i).length >= (enabledHistograms ? 20 : 30) ? 68 : 42
|
|
448
462
|
})), /*#__PURE__*/React.createElement(ObsContinuousStats, {
|
|
449
463
|
obs: obs
|
|
450
464
|
}));
|
|
@@ -41,7 +41,7 @@ const ObsAccordionToggle = _ref => {
|
|
|
41
41
|
}, /*#__PURE__*/React.createElement(FontAwesomeIcon, {
|
|
42
42
|
icon: isCurrentEventKey ? faChevronDown : faChevronRight
|
|
43
43
|
})), /*#__PURE__*/React.createElement("span", {
|
|
44
|
-
className: "obs-accordion-header-
|
|
44
|
+
className: "obs-accordion-header-span"
|
|
45
45
|
}, children));
|
|
46
46
|
};
|
|
47
47
|
export function ObsColsList(_ref2) {
|
|
@@ -224,7 +224,11 @@ export function ObsColsList(_ref2) {
|
|
|
224
224
|
}, /*#__PURE__*/React.createElement(ObsAccordionToggle, {
|
|
225
225
|
eventKey: item.name,
|
|
226
226
|
handleAccordionToggle: handleAccordionToggle
|
|
227
|
-
}, /*#__PURE__*/React.createElement("div",
|
|
227
|
+
}, /*#__PURE__*/React.createElement("div", {
|
|
228
|
+
className: "accordion-header-title"
|
|
229
|
+
}, item.name), /*#__PURE__*/React.createElement("div", {
|
|
230
|
+
className: "accordion-header-toolbar"
|
|
231
|
+
}, /*#__PURE__*/React.createElement("span", {
|
|
228
232
|
className: "mx-1 cursor-pointer ".concat(inLabelObs ? "active-icon" : "text-muted opacity-50"),
|
|
229
233
|
onClick: event => {
|
|
230
234
|
event.stopPropagation();
|
|
@@ -4,7 +4,9 @@ function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object
|
|
|
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
6
|
import React, { useEffect, useState } from "react";
|
|
7
|
+
import _ from "lodash";
|
|
7
8
|
import { Button, ButtonGroup, Dropdown, DropdownButton, OverlayTrigger, Tooltip } from "react-bootstrap";
|
|
9
|
+
import { DEFAULT_OBSM_KEYS } from "../../constants/constants";
|
|
8
10
|
import { useDataset } from "../../context/DatasetContext";
|
|
9
11
|
import { useSettings, useSettingsDispatch } from "../../context/SettingsContext";
|
|
10
12
|
import { useFetch } from "../../utils/requests";
|
|
@@ -36,13 +38,34 @@ export function ObsmKeysList() {
|
|
|
36
38
|
useEffect(() => {
|
|
37
39
|
if (!isPending && !serverError && fetchedData) {
|
|
38
40
|
setObsmKeysList(fetchedData);
|
|
41
|
+
|
|
42
|
+
// Set default obsm if in keys list and not selected
|
|
43
|
+
if (!settings.selectedObsm && !!fetchedData.length) {
|
|
44
|
+
// Follow DEFAULT_OBSM_KEYS order
|
|
45
|
+
_.each(DEFAULT_OBSM_KEYS, k => {
|
|
46
|
+
const defaultObsm = _.find(fetchedData, item => item.toLowerCase() === k);
|
|
47
|
+
if (defaultObsm) {
|
|
48
|
+
dispatch({
|
|
49
|
+
type: "select.obsm",
|
|
50
|
+
obsm: defaultObsm
|
|
51
|
+
});
|
|
52
|
+
return false; // break
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
if (settings.selectedObsm) {
|
|
57
|
+
// If selected obsm is not in keys list, reset to null
|
|
58
|
+
if (!_.includes(fetchedData, settings.selectedObsm)) {
|
|
59
|
+
dispatch({
|
|
60
|
+
type: "select.obsm",
|
|
61
|
+
obsm: null
|
|
62
|
+
});
|
|
63
|
+
} else {
|
|
64
|
+
setActive(settings.selectedObsm);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
39
67
|
}
|
|
40
|
-
}, [fetchedData, isPending, serverError]);
|
|
41
|
-
useEffect(() => {
|
|
42
|
-
if (settings.selectedObsm) {
|
|
43
|
-
setActive(settings.selectedObsm);
|
|
44
|
-
}
|
|
45
|
-
}, [settings.selectedObsm]);
|
|
68
|
+
}, [dispatch, fetchedData, isPending, serverError, settings.selectedObsm]);
|
|
46
69
|
const obsmList = obsmKeysList.map(item => {
|
|
47
70
|
return /*#__PURE__*/React.createElement(Dropdown.Item, {
|
|
48
71
|
key: item,
|
|
@@ -45,14 +45,19 @@ export function OffcanvasVars(_ref3) {
|
|
|
45
45
|
} = _ref3;
|
|
46
46
|
return /*#__PURE__*/React.createElement(Offcanvas, {
|
|
47
47
|
show: show,
|
|
48
|
-
onHide: handleClose
|
|
48
|
+
onHide: handleClose,
|
|
49
|
+
className: "offcanvas-vars"
|
|
49
50
|
}, /*#__PURE__*/React.createElement(Offcanvas.Header, {
|
|
50
51
|
closeButton: true
|
|
51
|
-
}, /*#__PURE__*/React.createElement(Offcanvas.Title, null, "Features")), /*#__PURE__*/React.createElement(Offcanvas.Body, null, /*#__PURE__*/React.createElement(
|
|
52
|
+
}, /*#__PURE__*/React.createElement(Offcanvas.Title, null, "Features")), /*#__PURE__*/React.createElement(Offcanvas.Body, null, /*#__PURE__*/React.createElement("div", {
|
|
53
|
+
className: "sidebar-features"
|
|
54
|
+
}, /*#__PURE__*/React.createElement(SearchBar, {
|
|
52
55
|
searchDiseases: true
|
|
53
|
-
}), /*#__PURE__*/React.createElement(
|
|
56
|
+
}), /*#__PURE__*/React.createElement("div", {
|
|
57
|
+
className: "sidebar-features-list"
|
|
58
|
+
}, /*#__PURE__*/React.createElement(VarNamesList, {
|
|
54
59
|
mode: mode
|
|
55
|
-
})));
|
|
60
|
+
})))));
|
|
56
61
|
}
|
|
57
62
|
export function OffcanvasControls(_ref4) {
|
|
58
63
|
let {
|
|
@@ -146,7 +146,7 @@ export function Scatterplot(_ref) {
|
|
|
146
146
|
});
|
|
147
147
|
});
|
|
148
148
|
}
|
|
149
|
-
}, [
|
|
149
|
+
}, [getRadiusScale, obsmData.data, obsmData.isPending, obsmData.serverError, data.positions]);
|
|
150
150
|
const getBounds = useCallback(() => {
|
|
151
151
|
var _deckRef$current3, _deckRef$current4;
|
|
152
152
|
const {
|
|
@@ -6,6 +6,7 @@ function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e =
|
|
|
6
6
|
import React, { useState } from "react";
|
|
7
7
|
import CloseIcon from "@mui/icons-material/Close";
|
|
8
8
|
import SearchIcon from "@mui/icons-material/Search";
|
|
9
|
+
import _ from "lodash";
|
|
9
10
|
import { Button, Form, FormGroup, InputGroup, Modal } from "react-bootstrap";
|
|
10
11
|
import Col from "react-bootstrap/Col";
|
|
11
12
|
import Container from "react-bootstrap/Container";
|
|
@@ -15,7 +16,7 @@ import Tab from "react-bootstrap/Tab";
|
|
|
15
16
|
import { DiseaseInfo, VarInfo } from "./SearchInfo";
|
|
16
17
|
import { DiseasesSearchResults, VarSearchResults } from "./SearchResults";
|
|
17
18
|
import { COLOR_ENCODINGS } from "../../constants/constants";
|
|
18
|
-
|
|
19
|
+
const select = (dispatch, item) => {
|
|
19
20
|
dispatch({
|
|
20
21
|
type: "select.var",
|
|
21
22
|
var: item
|
|
@@ -28,6 +29,14 @@ function onVarSelect(dispatch, item) {
|
|
|
28
29
|
type: "set.colorEncoding",
|
|
29
30
|
value: COLOR_ENCODINGS.VAR
|
|
30
31
|
});
|
|
32
|
+
};
|
|
33
|
+
const debounceSelect = _.debounce(select, 2000);
|
|
34
|
+
function onVarSelect(dispatch, item) {
|
|
35
|
+
dispatch({
|
|
36
|
+
type: "add.var",
|
|
37
|
+
var: item
|
|
38
|
+
});
|
|
39
|
+
debounceSelect(dispatch, item);
|
|
31
40
|
}
|
|
32
41
|
function addVarSet(dispatch, _ref) {
|
|
33
42
|
let {
|
|
@@ -89,7 +89,7 @@ export function VarSearchResults(_ref) {
|
|
|
89
89
|
count: deferredData.length,
|
|
90
90
|
ItemComponent: ItemComponent,
|
|
91
91
|
overscan: 500,
|
|
92
|
-
estimateSize: 42,
|
|
92
|
+
estimateSize: () => 42,
|
|
93
93
|
maxHeight: "70vh"
|
|
94
94
|
}) : /*#__PURE__*/React.createElement(ListGroup.Item, {
|
|
95
95
|
key: "empty",
|
|
@@ -160,7 +160,7 @@ export function DiseasesSearchResults(_ref2) {
|
|
|
160
160
|
count: deferredData.length,
|
|
161
161
|
ItemComponent: ItemComponent,
|
|
162
162
|
overscan: 250,
|
|
163
|
-
estimateSize: 32,
|
|
163
|
+
estimateSize: () => 32,
|
|
164
164
|
maxHeight: "70vh"
|
|
165
165
|
}) : /*#__PURE__*/React.createElement(ListGroup.Item, {
|
|
166
166
|
key: "empty",
|
|
@@ -113,14 +113,17 @@ export function SelectionItem(_ref3) {
|
|
|
113
113
|
});
|
|
114
114
|
const hasDiseaseInfo = !isPending && !serverError && !!(fetchedData !== null && fetchedData !== void 0 && fetchedData.length);
|
|
115
115
|
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("div", {
|
|
116
|
-
className:
|
|
116
|
+
className: hasDiseaseInfo ? "cursor-pointer" : "",
|
|
117
117
|
onClick: () => {
|
|
118
118
|
setOpenInfo(o => !o);
|
|
119
119
|
}
|
|
120
120
|
}, /*#__PURE__*/React.createElement("div", {
|
|
121
|
-
className: "d-flex
|
|
122
|
-
}, /*#__PURE__*/React.createElement("div",
|
|
123
|
-
className: "
|
|
121
|
+
className: "d-flex align-items-center"
|
|
122
|
+
}, /*#__PURE__*/React.createElement("div", {
|
|
123
|
+
className: "var-item-name",
|
|
124
|
+
title: item.name
|
|
125
|
+
}, item.name), /*#__PURE__*/React.createElement("div", {
|
|
126
|
+
className: "ms-auto d-flex align-items-center gap-1"
|
|
124
127
|
}, hasDiseaseInfo && /*#__PURE__*/React.createElement(MoreVert, null), /*#__PURE__*/React.createElement(VarHistogram, {
|
|
125
128
|
item: item
|
|
126
129
|
}), showSetColorEncoding && /*#__PURE__*/React.createElement(Button, {
|
|
@@ -159,6 +162,24 @@ export function SelectionItem(_ref3) {
|
|
|
159
162
|
data: fetchedData
|
|
160
163
|
}))));
|
|
161
164
|
}
|
|
165
|
+
const select = (dispatch, mode, item) => {
|
|
166
|
+
if (mode === SELECTION_MODES.SINGLE) {
|
|
167
|
+
dispatch({
|
|
168
|
+
type: "select.var",
|
|
169
|
+
var: item
|
|
170
|
+
});
|
|
171
|
+
} else if (mode === SELECTION_MODES.MULTIPLE) {
|
|
172
|
+
dispatch({
|
|
173
|
+
type: "select.multivar",
|
|
174
|
+
var: item
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
dispatch({
|
|
178
|
+
type: "set.colorEncoding",
|
|
179
|
+
value: COLOR_ENCODINGS.VAR
|
|
180
|
+
});
|
|
181
|
+
};
|
|
182
|
+
const debounceSelect = _.debounce(select, 200);
|
|
162
183
|
export function VarItem(_ref4) {
|
|
163
184
|
let {
|
|
164
185
|
item,
|
|
@@ -167,27 +188,7 @@ export function VarItem(_ref4) {
|
|
|
167
188
|
} = _ref4;
|
|
168
189
|
const settings = useSettings();
|
|
169
190
|
const dispatch = useSettingsDispatch();
|
|
170
|
-
const selectVar = () =>
|
|
171
|
-
if (mode === SELECTION_MODES.SINGLE) {
|
|
172
|
-
dispatch({
|
|
173
|
-
type: "select.var",
|
|
174
|
-
var: item
|
|
175
|
-
});
|
|
176
|
-
dispatch({
|
|
177
|
-
type: "set.colorEncoding",
|
|
178
|
-
value: COLOR_ENCODINGS.VAR
|
|
179
|
-
});
|
|
180
|
-
} else if (mode === SELECTION_MODES.MULTIPLE) {
|
|
181
|
-
dispatch({
|
|
182
|
-
type: "select.multivar",
|
|
183
|
-
var: item
|
|
184
|
-
});
|
|
185
|
-
dispatch({
|
|
186
|
-
type: "set.colorEncoding",
|
|
187
|
-
value: COLOR_ENCODINGS.VAR
|
|
188
|
-
});
|
|
189
|
-
}
|
|
190
|
-
};
|
|
191
|
+
const selectVar = () => debounceSelect(dispatch, mode, item);
|
|
191
192
|
const removeVar = () => {
|
|
192
193
|
dispatch({
|
|
193
194
|
type: "remove.var",
|
|
@@ -131,9 +131,7 @@ export function VarNamesList(_ref) {
|
|
|
131
131
|
};
|
|
132
132
|
const isPending = varMeans.isPending && settings.varSort.var.sort === VAR_SORT.MATRIX;
|
|
133
133
|
return /*#__PURE__*/React.createElement("div", {
|
|
134
|
-
className: "
|
|
135
|
-
}, /*#__PURE__*/React.createElement("div", {
|
|
136
|
-
className: "overflow-auto mt-3"
|
|
134
|
+
className: "mt-3 d-flex flex-column h-100"
|
|
137
135
|
}, /*#__PURE__*/React.createElement("div", {
|
|
138
136
|
className: "d-flex justify-content-between mb-2"
|
|
139
137
|
}, /*#__PURE__*/React.createElement("h5", null, _.capitalize(displayName)), /*#__PURE__*/React.createElement(ButtonGroup, {
|
|
@@ -164,9 +162,9 @@ export function VarNamesList(_ref) {
|
|
|
164
162
|
}), "Clear"))), /*#__PURE__*/React.createElement(React.Fragment, null, !varList.length ? /*#__PURE__*/React.createElement(Alert, {
|
|
165
163
|
variant: "light"
|
|
166
164
|
}, "Search for a feature.") : /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(VarListToolbar, null), /*#__PURE__*/React.createElement("div", {
|
|
167
|
-
className: "
|
|
165
|
+
className: "overflow-auto flex-grow-1 modern-scrollbars"
|
|
168
166
|
}, isPending && /*#__PURE__*/React.createElement(LoadingSpinner, null), /*#__PURE__*/React.createElement(ListGroup, {
|
|
169
167
|
variant: "flush",
|
|
170
168
|
className: "cherita-list"
|
|
171
|
-
}, varList)))))
|
|
169
|
+
}, varList)))));
|
|
172
170
|
}
|
|
@@ -135,8 +135,15 @@ export function Violin(_ref) {
|
|
|
135
135
|
if (!serverError) {
|
|
136
136
|
if (hasSelections) {
|
|
137
137
|
return /*#__PURE__*/React.createElement("div", {
|
|
138
|
-
className: "cherita-plot cherita-violin
|
|
139
|
-
}, isPending && /*#__PURE__*/React.createElement(LoadingSpinner, null), /*#__PURE__*/React.createElement(
|
|
138
|
+
className: "cherita-plot cherita-violin"
|
|
139
|
+
}, isPending && /*#__PURE__*/React.createElement(LoadingSpinner, null), /*#__PURE__*/React.createElement("div", {
|
|
140
|
+
className: "d-flex flex-column h-100"
|
|
141
|
+
}, /*#__PURE__*/React.createElement("div", {
|
|
142
|
+
className: "flex-grow-1 position-relative",
|
|
143
|
+
style: {
|
|
144
|
+
minHeight: "0"
|
|
145
|
+
}
|
|
146
|
+
}, /*#__PURE__*/React.createElement(Plot, {
|
|
140
147
|
data: data,
|
|
141
148
|
layout: layout,
|
|
142
149
|
useResizeHandler: true,
|
|
@@ -148,14 +155,17 @@ export function Violin(_ref) {
|
|
|
148
155
|
displaylogo: false,
|
|
149
156
|
modeBarButtons: modeBarButtons
|
|
150
157
|
}
|
|
151
|
-
}), (fetchedData === null || fetchedData === void 0 ? void 0 : fetchedData.resampled) && /*#__PURE__*/React.createElement(
|
|
152
|
-
|
|
158
|
+
})), (fetchedData === null || fetchedData === void 0 ? void 0 : fetchedData.resampled) && /*#__PURE__*/React.createElement("div", {
|
|
159
|
+
className: "flex-shrink-0"
|
|
160
|
+
}, /*#__PURE__*/React.createElement(Alert, {
|
|
161
|
+
variant: "warning",
|
|
162
|
+
className: "mb-1"
|
|
153
163
|
}, /*#__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, {
|
|
154
164
|
placement: "top",
|
|
155
165
|
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.")
|
|
156
166
|
}, /*#__PURE__*/React.createElement(FontAwesomeIcon, {
|
|
157
167
|
icon: faCircleInfo
|
|
158
|
-
}))));
|
|
168
|
+
}))))));
|
|
159
169
|
}
|
|
160
170
|
return /*#__PURE__*/React.createElement("div", {
|
|
161
171
|
className: "cherita-violin"
|
|
@@ -97,4 +97,7 @@ export const PLOTLY_MODEBAR_BUTTONS = ["toImage", "zoom2d", "pan2d", "zoomIn2d",
|
|
|
97
97
|
export const BREAKPOINTS = {
|
|
98
98
|
LG: "(max-width: 991.98px)",
|
|
99
99
|
XL: "(max-width: 1199.98px)"
|
|
100
|
-
};
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
// In order of priority
|
|
103
|
+
export const DEFAULT_OBSM_KEYS = ["x_umap", "x_tsne", "x_scvi", "x_pca"];
|
|
@@ -1,8 +1,11 @@
|
|
|
1
|
+
const _excluded = ["buster", "timestamp"];
|
|
1
2
|
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
3
|
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
4
|
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
5
|
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
|
|
5
6
|
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); }
|
|
7
|
+
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; }
|
|
8
|
+
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; }
|
|
6
9
|
import React, { createContext, useContext, useEffect, useReducer } from "react";
|
|
7
10
|
import _ from "lodash";
|
|
8
11
|
import { COLOR_ENCODINGS, DOTPLOT_SCALES, LOCAL_STORAGE_KEY, MATRIXPLOT_SCALES, OBS_TYPES, PSEUDOSPATIAL_CATEGORICAL_MODES, VAR_SORT, VAR_SORT_ORDER, VIOLINPLOT_SCALES } from "../constants/constants";
|
|
@@ -117,7 +120,18 @@ export function SettingsProvider(_ref2) {
|
|
|
117
120
|
const DATASET_STORAGE_KEY = "".concat(LOCAL_STORAGE_KEY, "-").concat(dataset_url);
|
|
118
121
|
// Use localStorage directly instead of useLocalStorage due to unnecessary re-renders
|
|
119
122
|
// https://github.com/uidotdev/usehooks/issues/157
|
|
120
|
-
|
|
123
|
+
let _ref3 = JSON.parse(localStorage.getItem(DATASET_STORAGE_KEY)) || {},
|
|
124
|
+
{
|
|
125
|
+
buster,
|
|
126
|
+
timestamp
|
|
127
|
+
} = _ref3,
|
|
128
|
+
localSettings = _objectWithoutProperties(_ref3, _excluded);
|
|
129
|
+
|
|
130
|
+
// If the buster is not set or does not match the current package version,
|
|
131
|
+
// reset localSettings to avoid stale data
|
|
132
|
+
if (!buster || buster !== "1.4.1-dev.2025-06-30.e26168b5") {
|
|
133
|
+
localSettings = {};
|
|
134
|
+
}
|
|
121
135
|
const [settings, dispatch] = useReducer(settingsReducer, {
|
|
122
136
|
canOverrideSettings,
|
|
123
137
|
defaultSettings,
|
|
@@ -126,7 +140,10 @@ export function SettingsProvider(_ref2) {
|
|
|
126
140
|
useEffect(() => {
|
|
127
141
|
if (canOverrideSettings) {
|
|
128
142
|
try {
|
|
129
|
-
localStorage.setItem(DATASET_STORAGE_KEY, JSON.stringify(
|
|
143
|
+
localStorage.setItem(DATASET_STORAGE_KEY, JSON.stringify(_objectSpread({
|
|
144
|
+
buster: "1.4.1-dev.2025-06-30.e26168b5" || "0.0.0",
|
|
145
|
+
timestamp: Date.now()
|
|
146
|
+
}, settings)));
|
|
130
147
|
} catch (err) {
|
|
131
148
|
if (err.code === 22 || err.code === 1014 || err.name === "QuotaExceededError" || err.name === "NS_ERROR_DOM_QUOTA_REACHED") {
|
|
132
149
|
console.err("Browser storage quota exceeded");
|
|
@@ -10,7 +10,7 @@ export function VirtualizedList(_ref) {
|
|
|
10
10
|
getDataAtIndex,
|
|
11
11
|
count,
|
|
12
12
|
ItemComponent,
|
|
13
|
-
estimateSize = 45,
|
|
13
|
+
estimateSize = () => 45,
|
|
14
14
|
overscan = 25,
|
|
15
15
|
maxHeight = "65vh"
|
|
16
16
|
} = _ref,
|
|
@@ -19,7 +19,7 @@ export function VirtualizedList(_ref) {
|
|
|
19
19
|
const itemVirtualizer = useVirtualizer({
|
|
20
20
|
count: count,
|
|
21
21
|
getScrollElement: () => parentNode,
|
|
22
|
-
estimateSize:
|
|
22
|
+
estimateSize: i => estimateSize(i),
|
|
23
23
|
overscan: overscan
|
|
24
24
|
});
|
|
25
25
|
const refCallback = useCallback(node => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@haniffalab/cherita-react",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.1-dev.2025-06-30.e26168b5",
|
|
4
4
|
"author": "Haniffa Lab",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"keywords": [
|
|
@@ -127,5 +127,6 @@
|
|
|
127
127
|
"bugs": {
|
|
128
128
|
"url": "https://github.com/haniffalab/cherita-react/issues"
|
|
129
129
|
},
|
|
130
|
-
"homepage": "https://github.com/haniffalab/cherita-react#readme"
|
|
131
|
-
|
|
130
|
+
"homepage": "https://github.com/haniffalab/cherita-react#readme",
|
|
131
|
+
"prereleaseSha": "e26168b526ee80a8dfb494c61e68a64112c8dbc1"
|
|
132
|
+
}
|
package/scss/cherita.scss
CHANGED
|
@@ -21,6 +21,10 @@ $prefix: "bs-" !default;
|
|
|
21
21
|
align-items: center;
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
+
.cursor-pointer {
|
|
25
|
+
cursor: pointer;
|
|
26
|
+
}
|
|
27
|
+
|
|
24
28
|
.cherita-container {
|
|
25
29
|
margin: 40px auto;
|
|
26
30
|
background-color: #fff;
|
|
@@ -151,6 +155,13 @@ $prefix: "bs-" !default;
|
|
|
151
155
|
padding-right: 1rem;
|
|
152
156
|
word-break: auto-phrase;
|
|
153
157
|
overflow-wrap: anywhere;
|
|
158
|
+
|
|
159
|
+
.form-check-label {
|
|
160
|
+
white-space: nowrap;
|
|
161
|
+
overflow: hidden;
|
|
162
|
+
text-overflow: ellipsis;
|
|
163
|
+
display: block;
|
|
164
|
+
}
|
|
154
165
|
}
|
|
155
166
|
|
|
156
167
|
.grad-step {
|
|
@@ -404,4 +415,16 @@ $gauge-padding: 0.15rem;
|
|
|
404
415
|
white-space: nowrap;
|
|
405
416
|
overflow: hidden;
|
|
406
417
|
text-overflow: ellipsis;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
.var-item-name {
|
|
421
|
+
@extend .me-auto;
|
|
422
|
+
padding-right: 0.5rem;
|
|
423
|
+
white-space: nowrap;
|
|
424
|
+
overflow: hidden;
|
|
425
|
+
text-overflow: ellipsis;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
input[type="checkbox"] {
|
|
429
|
+
cursor: pointer;
|
|
407
430
|
}
|
|
@@ -19,12 +19,22 @@
|
|
|
19
19
|
height: 20px;
|
|
20
20
|
margin-right: 10px;
|
|
21
21
|
}
|
|
22
|
-
.obs-accordion-header-
|
|
22
|
+
.obs-accordion-header-span {
|
|
23
23
|
font-size: 1rem;
|
|
24
24
|
width: 100%;
|
|
25
25
|
display: flex;
|
|
26
26
|
justify-content: space-between;
|
|
27
27
|
align-items: center;
|
|
28
|
+
|
|
29
|
+
.accordion-header-title {
|
|
30
|
+
@extend .text-break;
|
|
31
|
+
@extend .me-1;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.accordion-header-toolbar {
|
|
35
|
+
@extend .d-flex;
|
|
36
|
+
@extend .flex-nowrap;
|
|
37
|
+
}
|
|
28
38
|
}
|
|
29
39
|
.active-icon {
|
|
30
40
|
color: #0c63e4;
|