@haniffalab/cherita-react 0.2.0-dev.2024-09-26.775b9a06 → 0.2.0-dev.2024-09-26.1ea62883

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/App.scss +77 -8
  2. package/dist/components/Offcanvas/index.js +6 -2
  3. package/dist/components/dotplot/Dotplot.js +22 -5
  4. package/dist/components/dotplot/DotplotControls.js +11 -10
  5. package/dist/components/heatmap/Heatmap.js +22 -5
  6. package/dist/components/matrixplot/Matrixplot.js +22 -5
  7. package/dist/components/matrixplot/MatrixplotControls.js +8 -7
  8. package/dist/components/obs-list/ObsItem.js +394 -0
  9. package/dist/components/obs-list/ObsList.js +116 -299
  10. package/dist/components/obs-list/ObsToolbar.js +76 -0
  11. package/dist/components/scatterplot/Legend.js +4 -3
  12. package/dist/components/scatterplot/Scatterplot.js +129 -61
  13. package/dist/components/scatterplot/Toolbox.js +3 -2
  14. package/dist/components/search-bar/SearchBar.js +18 -2
  15. package/dist/components/search-bar/SearchResults.js +8 -8
  16. package/dist/components/var-list/VarItem.js +316 -0
  17. package/dist/components/var-list/VarList.js +167 -149
  18. package/dist/components/var-list/VarSet.js +214 -0
  19. package/dist/components/violin/Violin.js +46 -13
  20. package/dist/components/violin/ViolinControls.js +13 -18
  21. package/dist/constants/constants.js +41 -29
  22. package/dist/context/DatasetContext.js +91 -31
  23. package/dist/context/FilterContext.js +76 -0
  24. package/dist/helpers/map-helper.js +20 -15
  25. package/dist/helpers/zarr-helper.js +35 -13
  26. package/dist/index.js +7 -0
  27. package/dist/utils/VirtualizedList.js +69 -0
  28. package/dist/utils/requests.js +2 -2
  29. package/dist/utils/search.js +3 -56
  30. package/dist/utils/string.js +18 -0
  31. package/package.json +3 -2
  32. package/dist/components/obs-list/ObsValueList.js +0 -101
@@ -0,0 +1,394 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.CategoricalObs = CategoricalObs;
7
+ exports.ContinuousObs = ContinuousObs;
8
+ var _react = _interopRequireWildcard(require("react"));
9
+ var _material = require("@mui/material");
10
+ var _xCharts = require("@mui/x-charts");
11
+ var _lodash = _interopRequireDefault(require("lodash"));
12
+ var _reactBootstrap = require("react-bootstrap");
13
+ var _ObsToolbar = require("./ObsToolbar");
14
+ var _DatasetContext = require("../../context/DatasetContext");
15
+ var _colorHelper = require("../../helpers/color-helper");
16
+ var _LoadingIndicators = require("../../utils/LoadingIndicators");
17
+ var _requests = require("../../utils/requests");
18
+ var _string = require("../../utils/string");
19
+ var _VirtualizedList = require("../../utils/VirtualizedList");
20
+ var _jsxRuntime = require("react/jsx-runtime");
21
+ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
22
+ 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); }
23
+ 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; }
24
+ const N_BINS = 5;
25
+ function binContinuous(data) {
26
+ let nBins = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : N_BINS;
27
+ const binSize = (data.max - data.min) * (1 / nBins);
28
+ const thresholds = _lodash.default.range(nBins + 1).map(b => {
29
+ return data.min + binSize * b;
30
+ });
31
+ const binEdges = _lodash.default.range(thresholds.length - 1).map(i => [thresholds[i], thresholds[i + 1]]);
32
+ const bins = {
33
+ nBins: nBins,
34
+ binSize: binSize,
35
+ thresholds: thresholds,
36
+ binEdges: binEdges
37
+ };
38
+ return {
39
+ ...data,
40
+ bins: bins
41
+ };
42
+ }
43
+ function binDiscrete(data) {
44
+ let nBins = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : N_BINS;
45
+ const binSize = _lodash.default.round(data.n_values * (1 / nBins));
46
+ const bins = {
47
+ nBins: nBins,
48
+ binSize: binSize
49
+ };
50
+ return {
51
+ ...data,
52
+ bins: bins
53
+ };
54
+ }
55
+ function getContinuousLabel(code, binEdges) {
56
+ return "[ ".concat((0, _string.prettyNumerical)(binEdges[code][0]), ", ").concat((0, _string.prettyNumerical)(binEdges[code][1])).concat(code === binEdges.length - 1 ? " ]" : " )");
57
+ }
58
+ function CategoricalItem(_ref) {
59
+ let {
60
+ value,
61
+ label,
62
+ code,
63
+ value_counts,
64
+ pct,
65
+ isOmitted,
66
+ min,
67
+ max,
68
+ onChange,
69
+ showColor = true
70
+ } = _ref;
71
+ const {
72
+ getColor
73
+ } = (0, _colorHelper.useColor)();
74
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactBootstrap.ListGroup.Item, {
75
+ children: /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", {
76
+ className: "d-flex align-items-center",
77
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
78
+ className: "flex-grow-1",
79
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactBootstrap.Form.Check, {
80
+ className: "obs-value-list-check",
81
+ type: "switch",
82
+ label: label,
83
+ checked: !isOmitted,
84
+ onChange: () => onChange(value)
85
+ })
86
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", {
87
+ className: "d-flex align-items-center",
88
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
89
+ className: "pl-1 m-0",
90
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Tooltip, {
91
+ title: "".concat((0, _string.prettyNumerical)(pct), "%"),
92
+ placement: "left",
93
+ arrow: true,
94
+ children: /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", {
95
+ className: "d-flex align-items-center",
96
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactBootstrap.Badge, {
97
+ className: "value-count-badge",
98
+ style: {
99
+ fontWeight: "lighter"
100
+ },
101
+ children: (0, _string.prettyNumerical)(parseInt(value_counts))
102
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
103
+ className: "value-pct-gauge-container",
104
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_xCharts.Gauge, {
105
+ value: pct,
106
+ text: null,
107
+ innerRadius: "50%",
108
+ margin: {
109
+ top: 0,
110
+ right: 0,
111
+ bottom: 0,
112
+ left: 0
113
+ }
114
+ })
115
+ })]
116
+ })
117
+ })
118
+ }), showColor && /*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
119
+ className: "pl-1",
120
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)("svg", {
121
+ xmlns: "http://www.w3.org/2000/svg",
122
+ width: 24,
123
+ height: 24,
124
+ fill: "currentColor",
125
+ viewBox: "0 0 10 10",
126
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)("rect", {
127
+ x: "0",
128
+ y: "0",
129
+ width: "10",
130
+ height: "10",
131
+ fill: "rgb(".concat(getColor((code - min) / (max - min), true, isOmitted, {
132
+ alpha: 1
133
+ }, "obs"), ")")
134
+ })
135
+ })
136
+ })]
137
+ })]
138
+ })
139
+ }, value);
140
+ }
141
+ function CategoricalObs(_ref2) {
142
+ let {
143
+ obs,
144
+ updateObs,
145
+ toggleAll,
146
+ toggleObs,
147
+ toggleLabel,
148
+ toggleSlice,
149
+ toggleColor,
150
+ showColor = true
151
+ } = _ref2;
152
+ const dataset = (0, _DatasetContext.useDataset)();
153
+ const dispatch = (0, _DatasetContext.useDatasetDispatch)();
154
+ const totalCounts = _lodash.default.sum(_lodash.default.values(obs.value_counts));
155
+ const min = _lodash.default.min(_lodash.default.values(obs.codes));
156
+ const max = _lodash.default.max(_lodash.default.values(obs.codes));
157
+ (0, _react.useEffect)(() => {
158
+ var _dataset$selectedObs;
159
+ if (((_dataset$selectedObs = dataset.selectedObs) === null || _dataset$selectedObs === void 0 ? void 0 : _dataset$selectedObs.name) === obs.name) {
160
+ const selectedObsData = _lodash.default.omit(dataset.selectedObs, ["omit"]);
161
+ const obsData = _lodash.default.omit(obs, ["omit"]);
162
+ if (!_lodash.default.isEqual(selectedObsData, obsData)) {
163
+ // outdated selectedObs
164
+ dispatch({
165
+ type: "select.obs",
166
+ obs: obs
167
+ });
168
+ } else if (!_lodash.default.isEqual(dataset.selectedObs.omit, obs.omit)) {
169
+ updateObs({
170
+ ...obs,
171
+ omit: dataset.selectedObs.omit
172
+ });
173
+ }
174
+ }
175
+ }, [dataset.selectedObs, dispatch, obs, obs.name, updateObs]);
176
+ const getDataAtIndex = index => {
177
+ return {
178
+ value: obs.values[index],
179
+ code: obs.codes[obs.values[index]],
180
+ value_counts: obs.value_counts[obs.values[index]],
181
+ pct: obs.value_counts[obs.values[index]] / totalCounts * 100,
182
+ isOmitted: _lodash.default.includes(obs.omit, obs.codes[obs.values[index]]),
183
+ label: (0, _string.prettyNumerical)(obs.values[index])
184
+ };
185
+ };
186
+ return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactBootstrap.ListGroup, {
187
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactBootstrap.ListGroup.Item, {
188
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_ObsToolbar.ObsToolbar, {
189
+ item: obs,
190
+ onToggleAllObs: toggleAll,
191
+ onToggleLabel: toggleLabel,
192
+ onToggleSlice: toggleSlice,
193
+ onToggleColor: toggleColor
194
+ })
195
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_VirtualizedList.VirtualizedList, {
196
+ getDataAtIndex: getDataAtIndex,
197
+ count: obs.values.length,
198
+ ItemComponent: CategoricalItem,
199
+ totalCounts: totalCounts,
200
+ min: min,
201
+ max: max,
202
+ onChange: toggleObs,
203
+ showColor: showColor
204
+ })]
205
+ });
206
+ }
207
+ function ObsContinuousStats(_ref3) {
208
+ let {
209
+ obs
210
+ } = _ref3;
211
+ const ENDPOINT = "obs/distribution";
212
+ const dataset = (0, _DatasetContext.useDataset)();
213
+ const params = {
214
+ url: dataset.url,
215
+ obs_colname: obs.name
216
+ };
217
+ const {
218
+ fetchedData,
219
+ isPending,
220
+ serverError
221
+ } = (0, _requests.useFetch)(ENDPOINT, params);
222
+
223
+ // @TODO: fix width issue when min/max/etc values are too large
224
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_jsxRuntime.Fragment, {
225
+ children: /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", {
226
+ className: "d-flex justify-content-between mt-3 align-items-center",
227
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactBootstrap.Table, {
228
+ size: "sm",
229
+ className: "obs-continuous-stats",
230
+ striped: true,
231
+ children: /*#__PURE__*/(0, _jsxRuntime.jsxs)("tbody", {
232
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)("tr", {
233
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("td", {
234
+ children: "Min"
235
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)("td", {
236
+ className: "text-end",
237
+ children: (0, _string.prettyNumerical)(obs.min)
238
+ })]
239
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)("tr", {
240
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("td", {
241
+ children: "Max"
242
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)("td", {
243
+ className: "text-end",
244
+ children: (0, _string.prettyNumerical)(obs.max)
245
+ })]
246
+ })]
247
+ })
248
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactBootstrap.Table, {
249
+ size: "sm",
250
+ className: "obs-continuous-stats",
251
+ striped: true,
252
+ children: /*#__PURE__*/(0, _jsxRuntime.jsxs)("tbody", {
253
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)("tr", {
254
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("td", {
255
+ children: "Mean"
256
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)("td", {
257
+ className: "text-end",
258
+ children: (0, _string.prettyNumerical)(obs.mean)
259
+ })]
260
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)("tr", {
261
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("td", {
262
+ children: "Median"
263
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)("td", {
264
+ className: "text-end",
265
+ children: (0, _string.prettyNumerical)(obs.median)
266
+ })]
267
+ })]
268
+ })
269
+ }), isPending && /*#__PURE__*/(0, _jsxRuntime.jsx)(_LoadingIndicators.LoadingLinear, {}), !isPending && !serverError && /*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
270
+ className: "obs-distribution",
271
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_xCharts.SparkLineChart, {
272
+ data: fetchedData.kde_values[1],
273
+ showHighlight: true,
274
+ showTooltip: true // throws Maximum update depth exceeded error. Documented here: https://github.com/mui/mui-x/issues/13450
275
+ ,
276
+ margin: {
277
+ top: 10,
278
+ right: 20,
279
+ bottom: 10,
280
+ left: 20
281
+ },
282
+ xAxis: {
283
+ data: fetchedData.kde_values[0],
284
+ valueFormatter: v => "".concat((0, _string.prettyNumerical)(v))
285
+ },
286
+ valueFormatter: v => "".concat((0, _string.prettyNumerical)(v)),
287
+ slotProps: {
288
+ popper: {
289
+ className: "feature-histogram-tooltip"
290
+ }
291
+ }
292
+ })
293
+ })]
294
+ })
295
+ });
296
+ }
297
+ function ContinuousObs(_ref4) {
298
+ let {
299
+ obs,
300
+ updateObs,
301
+ toggleAll,
302
+ toggleObs,
303
+ toggleLabel,
304
+ toggleSlice,
305
+ toggleColor
306
+ } = _ref4;
307
+ const ENDPOINT = "obs/bins";
308
+ const dataset = (0, _DatasetContext.useDataset)();
309
+ const dispatch = (0, _DatasetContext.useDatasetDispatch)();
310
+ const binnedObs = binContinuous(obs);
311
+ const params = {
312
+ url: dataset.url,
313
+ obs_col: binnedObs.name,
314
+ thresholds: binnedObs.bins.thresholds,
315
+ nBins: binnedObs.bins.nBins
316
+ };
317
+ const {
318
+ fetchedData,
319
+ isPending,
320
+ serverError
321
+ } = (0, _requests.useFetch)(ENDPOINT, params, {
322
+ refetchOnMount: false
323
+ });
324
+ const updatedObs = fetchedData && _lodash.default.isMatch(obs, fetchedData);
325
+ (0, _react.useEffect)(() => {
326
+ // Update ObsList obsCols with bin data
327
+ // after update -> re-render -> obs will already be updated
328
+ if (!isPending && !serverError && !_lodash.default.isMatch(obs, fetchedData)) {
329
+ updateObs({
330
+ ...binnedObs,
331
+ ...fetchedData,
332
+ codesMap: _lodash.default.invert(fetchedData.codes)
333
+ });
334
+ }
335
+ }, [binnedObs, fetchedData, isPending, obs, serverError, updateObs]);
336
+ (0, _react.useEffect)(() => {
337
+ var _dataset$selectedObs2;
338
+ if (updatedObs && ((_dataset$selectedObs2 = dataset.selectedObs) === null || _dataset$selectedObs2 === void 0 ? void 0 : _dataset$selectedObs2.name) === obs.name) {
339
+ const selectedObsData = _lodash.default.omit(dataset.selectedObs, ["omit"]);
340
+ const obsData = _lodash.default.omit(obs, ["omit"]);
341
+ if (!_lodash.default.isEqual(selectedObsData, obsData)) {
342
+ // outdated selectedObs
343
+ dispatch({
344
+ type: "select.obs",
345
+ obs: obs
346
+ });
347
+ } else if (!_lodash.default.isEqual(dataset.selectedObs.omit, obs.omit)) {
348
+ updateObs({
349
+ ...obs,
350
+ omit: dataset.selectedObs.omit
351
+ });
352
+ }
353
+ }
354
+ }, [dataset.selectedObs, dispatch, obs, obs.name, updateObs, updatedObs]);
355
+ const totalCounts = _lodash.default.sum(_lodash.default.values(obs === null || obs === void 0 ? void 0 : obs.value_counts));
356
+ const min = _lodash.default.min(_lodash.default.values(obs === null || obs === void 0 ? void 0 : obs.codes));
357
+ const max = _lodash.default.max(_lodash.default.values(obs === null || obs === void 0 ? void 0 : obs.codes));
358
+ const getDataAtIndex = index => {
359
+ return {
360
+ value: obs.values[index],
361
+ code: obs.codes[obs.values[index]],
362
+ value_counts: obs.value_counts[obs.values[index]],
363
+ pct: obs.value_counts[obs.values[index]] / totalCounts * 100,
364
+ isOmitted: _lodash.default.includes(obs.omit, obs.codes[obs.values[index]]),
365
+ label: getContinuousLabel(obs.codes[obs.values[index]], obs.bins.binEdges)
366
+ };
367
+ };
368
+ return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
369
+ children: [isPending && /*#__PURE__*/(0, _jsxRuntime.jsx)(_LoadingIndicators.LoadingLinear, {}), !serverError && updatedObs && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
370
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactBootstrap.ListGroup, {
371
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactBootstrap.ListGroup.Item, {
372
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_ObsToolbar.ObsToolbar, {
373
+ item: obs,
374
+ onToggleAllObs: toggleAll,
375
+ onToggleLabel: toggleLabel,
376
+ onToggleSlice: toggleSlice,
377
+ onToggleColor: toggleColor
378
+ })
379
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_VirtualizedList.VirtualizedList, {
380
+ getDataAtIndex: getDataAtIndex,
381
+ count: obs.values.length,
382
+ ItemComponent: CategoricalItem,
383
+ totalCounts: totalCounts,
384
+ min: min,
385
+ max: max,
386
+ onChange: toggleObs,
387
+ showColor: false
388
+ })]
389
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(ObsContinuousStats, {
390
+ obs: obs
391
+ })]
392
+ })]
393
+ });
394
+ }