@haniffalab/cherita-react 0.2.0-dev.2024-12-16.ab9c5057 → 0.2.0-dev.2024-12-16.67617f27

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 (32) hide show
  1. package/dist/components/dotplot/Dotplot.js +6 -1
  2. package/dist/components/full-page/FullPage.js +81 -7
  3. package/dist/components/heatmap/Heatmap.js +6 -1
  4. package/dist/components/matrixplot/Matrixplot.js +6 -1
  5. package/dist/components/obs-list/ObsItem.js +74 -24
  6. package/dist/components/obs-list/ObsList.js +27 -5
  7. package/dist/components/pseudospatial/Pseudospatial.js +219 -0
  8. package/dist/components/pseudospatial/PseudospatialControls.js +12 -0
  9. package/dist/components/pseudospatial/PseudospatialToolbar.js +157 -0
  10. package/dist/components/scatterplot/Scatterplot.js +100 -166
  11. package/dist/components/var-list/VarItem.js +35 -53
  12. package/dist/components/var-list/VarList.js +4 -2
  13. package/dist/components/var-list/VarSet.js +1 -0
  14. package/dist/components/violin/Violin.js +7 -2
  15. package/dist/constants/constants.js +17 -1
  16. package/dist/context/DatasetContext.js +40 -2
  17. package/dist/css/cherita.css +44 -0
  18. package/dist/css/cherita.css.map +1 -1
  19. package/dist/helpers/color-helper.js +12 -8
  20. package/dist/helpers/zarr-helper.js +2 -0
  21. package/dist/index.js +25 -0
  22. package/dist/utils/Filter.js +129 -0
  23. package/dist/utils/Histogram.js +54 -0
  24. package/dist/utils/ImageViewer.js +40 -0
  25. package/dist/{components/scatterplot → utils}/Legend.js +12 -7
  26. package/dist/utils/VirtualizedList.js +2 -2
  27. package/dist/utils/requests.js +8 -2
  28. package/dist/utils/search.js +4 -2
  29. package/dist/utils/string.js +7 -3
  30. package/package.json +2 -2
  31. package/scss/cherita.scss +44 -0
  32. package/dist/App.scss +0 -270
@@ -0,0 +1,219 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.Pseudospatial = Pseudospatial;
7
+ exports.PseudospatialImage = PseudospatialImage;
8
+ var _react = _interopRequireWildcard(require("react"));
9
+ var _lodash = _interopRequireDefault(require("lodash"));
10
+ var _reactBootstrap = require("react-bootstrap");
11
+ var _reactPlotly = _interopRequireDefault(require("react-plotly.js"));
12
+ var _PseudospatialToolbar = require("./PseudospatialToolbar");
13
+ var _constants = require("../../constants/constants");
14
+ var _DatasetContext = require("../../context/DatasetContext");
15
+ var _FilterContext = require("../../context/FilterContext");
16
+ var _colorHelper = require("../../helpers/color-helper");
17
+ var _ImageViewer = require("../../utils/ImageViewer");
18
+ var _Legend = require("../../utils/Legend");
19
+ var _LoadingIndicators = require("../../utils/LoadingIndicators");
20
+ var _requests = require("../../utils/requests");
21
+ var _jsxRuntime = require("react/jsx-runtime");
22
+ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
23
+ function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
24
+ function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
25
+ function usePseudospatialData(plotType) {
26
+ const ENDPOINT = "pseudospatial";
27
+ const dataset = (0, _DatasetContext.useDataset)();
28
+ const filteredData = (0, _FilterContext.useFilteredData)();
29
+ const isSliced = dataset.sliceBy.obs || dataset.sliceBy.polygons;
30
+ const baseParams = (0, _react.useMemo)(() => {
31
+ return {
32
+ url: dataset.url,
33
+ maskSet: dataset.pseudospatial.maskSet,
34
+ maskValues: dataset.pseudospatial.maskValues,
35
+ obsIndices: isSliced ? [...(filteredData.obsIndices || [])] : null,
36
+ varNamesCol: dataset.varNamesCol,
37
+ showColorbar: false,
38
+ format: "json"
39
+ };
40
+ }, [dataset.url, dataset.pseudospatial.maskSet, dataset.pseudospatial.maskValues, dataset.varNamesCol, isSliced, filteredData.obsIndices]);
41
+ const getPlotParams = (0, _react.useCallback)(() => {
42
+ if (plotType === _constants.PSEUDOSPATIAL_PLOT_TYPES.GENE) {
43
+ return {
44
+ varKey: dataset.selectedVar?.isSet ? {
45
+ name: dataset.selectedVar?.name,
46
+ indices: dataset.selectedVar?.vars.map(v => v.index)
47
+ } : dataset.selectedVar?.index,
48
+ ...(dataset.sliceBy.obs ? {
49
+ obsCol: dataset.selectedObs,
50
+ obsValues: !dataset.selectedObs?.omit.length ? null : _lodash.default.difference(_lodash.default.values(dataset.selectedObs?.codes), dataset.selectedObs?.omit).map(c => dataset.selectedObs?.codesMap[c])
51
+ } : {})
52
+ };
53
+ } else if (plotType === _constants.PSEUDOSPATIAL_PLOT_TYPES.CATEGORICAL) {
54
+ return {
55
+ obsCol: dataset.selectedObs,
56
+ obsValues: !dataset.selectedObs?.omit.length ? null : _lodash.default.difference(_lodash.default.values(dataset.selectedObs?.codes), dataset.selectedObs?.omit).map(c => dataset.selectedObs?.codesMap[c]),
57
+ mode: dataset.pseudospatial.categoricalMode
58
+ };
59
+ } else if (plotType === "continuous") {
60
+ return {
61
+ obsCol: dataset.selectedObs,
62
+ obsValues: !dataset.selectedObs?.omit.length ? null : _lodash.default.difference(_lodash.default.values(dataset.selectedObs?.codes), dataset.selectedObs?.omit).map(c => dataset.selectedObs?.codesMap[c])
63
+ };
64
+ }
65
+ }, [dataset.pseudospatial.categoricalMode, dataset.selectedObs, dataset.selectedVar?.index, dataset.selectedVar?.isSet, dataset.selectedVar?.name, dataset.selectedVar?.vars, dataset.sliceBy.obs, plotType]);
66
+ const params = (0, _react.useMemo)(() => {
67
+ return {
68
+ ...baseParams,
69
+ ...getPlotParams()
70
+ };
71
+ }, [baseParams, getPlotParams]);
72
+ return (0, _requests.useDebouncedFetch)(ENDPOINT + "/" + plotType, params, 500, {
73
+ enabled: !!plotType && !!dataset.pseudospatial.maskSet
74
+ });
75
+ }
76
+ function Pseudospatial(_ref) {
77
+ let {
78
+ showLegend = true,
79
+ sharedScaleRange = false
80
+ } = _ref;
81
+ const dataset = (0, _DatasetContext.useDataset)();
82
+ const [data, setData] = (0, _react.useState)([]);
83
+ const [layout, setLayout] = (0, _react.useState)({});
84
+ const {
85
+ getColor
86
+ } = (0, _colorHelper.useColor)();
87
+ const colorscale = (0, _react.useRef)(dataset.controls.colorScale);
88
+ const plotType = dataset.colorEncoding === _constants.COLOR_ENCODINGS.VAR ? _constants.PSEUDOSPATIAL_PLOT_TYPES.GENE : dataset.selectedObs?.type === _constants.OBS_TYPES.CATEGORICAL || dataset.selectedObs?.type === _constants.OBS_TYPES.BOOLEAN ? _constants.PSEUDOSPATIAL_PLOT_TYPES.CATEGORICAL : dataset.selectedObs?.type === _constants.OBS_TYPES.CONTINUOUS ? _constants.PSEUDOSPATIAL_PLOT_TYPES.CONTINUOUS : _constants.PSEUDOSPATIAL_PLOT_TYPES.MASKS;
89
+ const updateColorscale = (0, _react.useCallback)(colorscale => {
90
+ setLayout(l => {
91
+ return {
92
+ ...l,
93
+ coloraxis: {
94
+ ...l.coloraxis,
95
+ colorscale: colorscale
96
+ }
97
+ };
98
+ });
99
+ setData(d => {
100
+ const min = layout?.coloraxis?.cmin;
101
+ const max = layout?.coloraxis?.cmax;
102
+ return _lodash.default.map(d, trace => {
103
+ const v = trace.meta.value;
104
+ if (v === null) {
105
+ return trace;
106
+ }
107
+ const color = (0, _colorHelper.rgbToHex)(getColor({
108
+ value: (v - min) / (max - min)
109
+ }));
110
+ return {
111
+ ...trace,
112
+ fillcolor: color,
113
+ line: {
114
+ ...trace.line,
115
+ color: color
116
+ }
117
+ };
118
+ });
119
+ });
120
+ }, [getColor, layout?.coloraxis?.cmax, layout?.coloraxis?.cmin]);
121
+ const {
122
+ fetchedData,
123
+ isPending,
124
+ serverError
125
+ } = usePseudospatialData(plotType);
126
+ (0, _react.useEffect)(() => {
127
+ if (!isPending && !serverError) {
128
+ setData(fetchedData.data);
129
+ setLayout(fetchedData.layout);
130
+ updateColorscale(colorscale.current);
131
+ }
132
+ }, [fetchedData, isPending, serverError, sharedScaleRange, updateColorscale]);
133
+ (0, _react.useEffect)(() => {
134
+ colorscale.current = dataset.controls.colorScale;
135
+ updateColorscale(colorscale.current);
136
+ }, [dataset.controls.colorScale, updateColorscale]);
137
+ (0, _react.useEffect)(() => {
138
+ if (sharedScaleRange) {
139
+ const {
140
+ min,
141
+ max
142
+ } = {
143
+ min: dataset.controls.range[0] * (dataset.controls.valueRange[1] - dataset.controls.valueRange[0]) + dataset.controls.valueRange[0],
144
+ max: dataset.controls.range[1] * (dataset.controls.valueRange[1] - dataset.controls.valueRange[0]) + dataset.controls.valueRange[0]
145
+ };
146
+ setData(d => {
147
+ return _lodash.default.map(d, trace => {
148
+ const v = trace.meta.value;
149
+ if (v === null) {
150
+ return trace;
151
+ }
152
+ const color = (0, _colorHelper.rgbToHex)(getColor({
153
+ value: (v - min) / (max - min)
154
+ }));
155
+ return {
156
+ ...trace,
157
+ fillcolor: color,
158
+ line: {
159
+ ...trace.line,
160
+ color: color
161
+ }
162
+ };
163
+ });
164
+ });
165
+ setLayout(l => {
166
+ return {
167
+ ...l,
168
+ coloraxis: {
169
+ ...l.coloraxis,
170
+ cmin: min,
171
+ cmax: max
172
+ }
173
+ };
174
+ });
175
+ }
176
+ }, [dataset.controls.range, dataset.controls.valueMax, dataset.controls.valueMin, dataset.controls.valueRange, getColor, sharedScaleRange]);
177
+ const hasSelections = !!plotType && !!dataset.pseudospatial.maskSet;
178
+ if (!serverError) {
179
+ return /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", {
180
+ className: "cherita-pseudospatial position-relative",
181
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_PseudospatialToolbar.PseudospatialToolbar, {
182
+ plotType: plotType
183
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
184
+ children: [hasSelections && isPending && /*#__PURE__*/(0, _jsxRuntime.jsx)(_LoadingIndicators.LoadingSpinner, {}), hasSelections && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactPlotly.default, {
185
+ data: data,
186
+ layout: layout,
187
+ useResizeHandler: true,
188
+ className: "cherita-pseudospatial-plot",
189
+ config: {
190
+ displaylogo: false
191
+ }
192
+ }), hasSelections && showLegend && /*#__PURE__*/(0, _jsxRuntime.jsx)(_Legend.Legend, {
193
+ min: layout?.coloraxis?.cmin,
194
+ max: layout?.coloraxis?.cmax
195
+ })]
196
+ })]
197
+ });
198
+ } else {
199
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_jsxRuntime.Fragment, {
200
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactBootstrap.Alert, {
201
+ variant: "danger",
202
+ children: serverError.message
203
+ })
204
+ });
205
+ }
206
+ }
207
+
208
+ // @TODO: explore making it a minimap
209
+ function PseudospatialImage() {
210
+ const dataset = (0, _DatasetContext.useDataset)();
211
+ if (dataset.imageUrl) {
212
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_ImageViewer.ImageViewer, {
213
+ src: dataset.imageUrl,
214
+ alt: "Pseudospatial reference image"
215
+ });
216
+ } else {
217
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_jsxRuntime.Fragment, {});
218
+ }
219
+ }
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.PseudospatialControls = PseudospatialControls;
7
+ var _react = _interopRequireDefault(require("react"));
8
+ var _jsxRuntime = require("react/jsx-runtime");
9
+ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
10
+ function PseudospatialControls() {
11
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_jsxRuntime.Fragment, {});
12
+ }
@@ -0,0 +1,157 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.PseudospatialToolbar = PseudospatialToolbar;
7
+ var _react = _interopRequireWildcard(require("react"));
8
+ var _lodash = _interopRequireDefault(require("lodash"));
9
+ var _reactBootstrap = require("react-bootstrap");
10
+ var _constants = require("../../constants/constants");
11
+ var _DatasetContext = require("../../context/DatasetContext");
12
+ var _requests = require("../../utils/requests");
13
+ var _jsxRuntime = require("react/jsx-runtime");
14
+ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
15
+ function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
16
+ function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
17
+ function CategoricalMode() {
18
+ const dataset = (0, _DatasetContext.useDataset)();
19
+ const dispatch = (0, _DatasetContext.useDatasetDispatch)();
20
+ const modeList = _lodash.default.map(_constants.PSEUDOSPATIAL_CATEGORICAL_MODES, (m, key) => /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactBootstrap.Dropdown.Item, {
21
+ active: dataset.pseudospatial.categoricalMode === m,
22
+ onClick: () => {
23
+ dispatch({
24
+ type: "set.pseudospatial.categoricalMode",
25
+ categoricalMode: m.value
26
+ });
27
+ },
28
+ children: _lodash.default.capitalize(m.name)
29
+ }, key));
30
+ const mode = _lodash.default.find(_constants.PSEUDOSPATIAL_CATEGORICAL_MODES, {
31
+ value: dataset.pseudospatial.categoricalMode
32
+ });
33
+ return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactBootstrap.Dropdown, {
34
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactBootstrap.Dropdown.Toggle, {
35
+ variant: "light",
36
+ children: _lodash.default.capitalize(mode.name)
37
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactBootstrap.Dropdown.Menu, {
38
+ children: modeList
39
+ })]
40
+ });
41
+ }
42
+ function MaskSet() {
43
+ const ENDPOINT = "masks";
44
+ const dataset = (0, _DatasetContext.useDataset)();
45
+ const dispatch = (0, _DatasetContext.useDatasetDispatch)();
46
+ const [maskSets, setMaskSets] = (0, _react.useState)(null);
47
+ const [params, setParams] = (0, _react.useState)({
48
+ url: dataset.url
49
+ });
50
+ (0, _react.useEffect)(() => {
51
+ setParams(p => {
52
+ return {
53
+ ...p,
54
+ url: dataset.url
55
+ };
56
+ });
57
+ }, [dataset.url]);
58
+ const {
59
+ fetchedData,
60
+ isPending,
61
+ serverError
62
+ } = (0, _requests.useFetch)(ENDPOINT, params, {
63
+ refetchOnMount: false
64
+ });
65
+ (0, _react.useEffect)(() => {
66
+ if (!isPending && !serverError) {
67
+ setMaskSets(fetchedData);
68
+ }
69
+ }, [fetchedData, isPending, serverError]);
70
+ const maskSetList = _lodash.default.map(maskSets, (ms, key) => /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactBootstrap.Dropdown.Item, {
71
+ active: dataset.pseudospatial.maskSet === key,
72
+ onClick: () => {
73
+ dispatch({
74
+ type: "set.pseudospatial.maskSet",
75
+ maskSet: key
76
+ });
77
+ },
78
+ children: _lodash.default.capitalize(key)
79
+ }, key));
80
+ const handleMaskChange = mask => {
81
+ let newMasks = dataset.pseudospatial.maskValues || maskSets?.[dataset.pseudospatial?.maskSet];
82
+ newMasks = newMasks.includes(mask) ? newMasks.filter(m => m !== mask) : [...newMasks, mask];
83
+ if (!_lodash.default.difference(maskSets?.[dataset.pseudospatial?.maskSet], newMasks).length) {
84
+ newMasks = null;
85
+ }
86
+ dispatch({
87
+ type: "set.pseudospatial.maskValues",
88
+ maskValues: newMasks
89
+ });
90
+ };
91
+ const toggleMasks = () => {
92
+ if (!dataset.pseudospatial.maskValues || dataset.pseudospatial.maskValues?.length === maskSets?.[dataset.pseudospatial?.maskSet]?.length) {
93
+ dispatch({
94
+ type: "set.pseudospatial.maskValues",
95
+ maskValues: []
96
+ });
97
+ } else {
98
+ dispatch({
99
+ type: "set.pseudospatial.maskValues",
100
+ maskValues: null
101
+ });
102
+ }
103
+ };
104
+ const masksList = _lodash.default.map(maskSets?.[dataset.pseudospatial?.maskSet], mask => /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactBootstrap.Dropdown.ItemText, {
105
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactBootstrap.Form.Check, {
106
+ type: "checkbox",
107
+ label: mask,
108
+ checked: !dataset.pseudospatial.maskValues || dataset.pseudospatial.maskValues.includes(mask),
109
+ onChange: () => handleMaskChange(mask)
110
+ })
111
+ }, mask));
112
+ const nMasks = dataset.pseudospatial.maskValues ? dataset.pseudospatial.maskValues?.length : maskSets?.[dataset.pseudospatial?.maskSet]?.length || "No";
113
+ const toggleAllChecked = !dataset.pseudospatial.maskValues || dataset.pseudospatial.maskValues?.length === maskSets?.[dataset.pseudospatial?.maskSet]?.length;
114
+ return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
115
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactBootstrap.Dropdown, {
116
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactBootstrap.Dropdown.Toggle, {
117
+ variant: "light",
118
+ children: _lodash.default.capitalize(dataset.pseudospatial.maskSet || "Select a mask set")
119
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactBootstrap.Dropdown.Menu, {
120
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactBootstrap.Dropdown.Header, {
121
+ children: "Mask set"
122
+ }), maskSetList]
123
+ })]
124
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactBootstrap.Dropdown, {
125
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactBootstrap.Dropdown.Toggle, {
126
+ variant: "light",
127
+ children: [nMasks, " masks selected"]
128
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactBootstrap.Dropdown.Menu, {
129
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactBootstrap.Dropdown.Header, {
130
+ children: "Masks"
131
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactBootstrap.Dropdown.ItemText, {
132
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactBootstrap.Form.Check, {
133
+ type: "checkbox",
134
+ label: "Toggle all",
135
+ checked: toggleAllChecked,
136
+ onChange: toggleMasks
137
+ })
138
+ }, "toggle-all"), masksList]
139
+ })]
140
+ })]
141
+ });
142
+ }
143
+
144
+ // @TODO: add colormap, colorbar slider
145
+ function PseudospatialToolbar(_ref) {
146
+ let {
147
+ plotType
148
+ } = _ref;
149
+ return /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", {
150
+ className: "cherita-pseudospatial-toolbar",
151
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactBootstrap.ButtonGroup, {
152
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(MaskSet, {})
153
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactBootstrap.ButtonGroup, {
154
+ children: plotType === _constants.PSEUDOSPATIAL_PLOT_TYPES.CATEGORICAL && /*#__PURE__*/(0, _jsxRuntime.jsx)(CategoricalMode, {})
155
+ })]
156
+ });
157
+ }