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

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/App.scss ADDED
@@ -0,0 +1,270 @@
1
+ // Theme CSS
2
+ $accordion-button-active-bg: #e7edf0;
3
+
4
+ @import "bootstrap/scss/bootstrap";
5
+
6
+ input[type="checkbox"], .cursor-pointer {
7
+ cursor: pointer;
8
+ }
9
+
10
+ .loading-spinner {
11
+ @extend .bg-light;
12
+ height: 100%;
13
+ width: 100%;
14
+ display: flex;
15
+ z-index: 1;
16
+ opacity: 75%;
17
+ position: absolute;
18
+ justify-content: center;
19
+ align-items: center;
20
+ }
21
+
22
+ .cherita-container {
23
+ margin: 40px auto;
24
+ background-color: #fff;
25
+ border-radius: 0.25rem;
26
+ box-shadow: 0 0.5em 1em 0.3em rgba(10, 10, 10, .15), 0 0 0 1px rgba(10, 10, 10, .02);
27
+ color: #4a4a4a;
28
+ max-width: 100%;
29
+ position: relative;
30
+ overflow: hidden;
31
+ }
32
+
33
+ .cherita-navbar {
34
+ @extend .mx-1;
35
+ @extend .my-2;
36
+ position: absolute;
37
+ z-index: 11;
38
+ top: 0;
39
+ left: 0;
40
+ right: 0;
41
+ border-radius: 0.25rem;
42
+ }
43
+
44
+ .cherita-container .cherita-navbar-item>.dropdown-menu {
45
+ height: var(--dropdown-height);
46
+ overflow-x: hidden;
47
+ overflow-y: scroll;
48
+ }
49
+
50
+ /* Scrollbar styling */
51
+
52
+ /* Works on Firefox */
53
+ .cherita-container * {
54
+ scrollbar-width: thin;
55
+ scrollbar-color: rgb(70, 70, 70) auto;
56
+ }
57
+
58
+ /* Works on Chrome, Edge, and Safari */
59
+ .cherita-container *::-webkit-scrollbar {
60
+ width: 7px;
61
+ }
62
+
63
+ .cherita-container *::-webkit-scrollbar-track {
64
+ background: #212529;
65
+ }
66
+
67
+ .cherita-container *::-webkit-scrollbar-thumb {
68
+ background-color: rgb(70, 70, 70);
69
+ }
70
+
71
+ /* End scrollbar styling*/
72
+
73
+ .cherita-container-plot {
74
+ margin-top: 76px;
75
+ padding: 20px;
76
+ position: relative;
77
+ min-height: 500px;
78
+ }
79
+
80
+ .cherita-container-scatterplot {
81
+ position: relative;
82
+ min-height: 500px;
83
+ height: 100%;
84
+ }
85
+
86
+ .cherita-spatial-controls {
87
+ position: absolute;
88
+ z-index: 10;
89
+ top: 1rem;
90
+ left: 1rem;
91
+ width: 3rem;
92
+ }
93
+
94
+ .cherita-spatial-footer {
95
+ position: absolute;
96
+ z-index: 10;
97
+ bottom: 1rem;
98
+ display: flex;
99
+ justify-content: space-between;
100
+ align-items: flex-end;
101
+ flex-wrap: wrap;
102
+ width: 100%;
103
+ padding-left: 2rem;
104
+ padding-right: 2rem;
105
+ padding-bottom: 1rem;
106
+ pointer-events: none;
107
+ > * {
108
+ pointer-events: auto;
109
+ }
110
+ }
111
+
112
+ .cherita-toolbox-footer {
113
+ display: flex;
114
+ flex-direction: column;
115
+ }
116
+
117
+ .cherita-toolbox-footer .alert {
118
+ width: fit-content;
119
+ }
120
+
121
+ .cherita-toolbox {
122
+ order: 1;
123
+ }
124
+
125
+ .cherita-legend {
126
+ order: 2;
127
+ width: 12rem;
128
+ }
129
+
130
+ @include media-breakpoint-down(xl) {
131
+ .cherita-spatial-controls {
132
+ top: 100px;
133
+ }
134
+ }
135
+
136
+ .cherita-accordion-active .accordion-button {
137
+ background-color: rgb(204, 227, 237)
138
+ }
139
+
140
+ .obs-value-list-check {
141
+ padding-right: 1rem;
142
+ word-break: auto-phrase;
143
+ overflow-wrap: anywhere;
144
+ }
145
+
146
+ .grad-step {
147
+ display: inline-block;
148
+ height: 20px;
149
+ width: 1%;
150
+ }
151
+ .gradient {
152
+ width: 100%;
153
+ white-space: nowrap;
154
+ position: relative;
155
+ display: inline-block;
156
+ top: 4px;
157
+ padding-bottom: 15px;
158
+ }
159
+
160
+ .gradient .domain-min {
161
+ position: absolute;
162
+ left: 0;
163
+ font-size: 11px;
164
+ bottom: 3px;
165
+ }
166
+ .gradient .domain-med {
167
+ position: absolute;
168
+ right: 25%;
169
+ left: 25%;
170
+ text-align: center;
171
+ font-size: 11px;
172
+ bottom: 3px;
173
+ }
174
+ .gradient .domain-max {
175
+ position: absolute;
176
+ right: 0;
177
+ font-size: 11px;
178
+ bottom: 3px;
179
+ }
180
+
181
+ .cm-string {
182
+ padding: 0px 4px;
183
+ display: inline-block;
184
+ min-width: 13px;
185
+ min-height: 13px;
186
+ border-radius: 3px;
187
+ box-sizing: border-box;
188
+ position: relative;
189
+ vertical-align: middle;
190
+ margin-left: 2px;
191
+ margin-bottom: 2px;
192
+ position: relative;
193
+ top: 1px;
194
+ }
195
+ .cm-small {
196
+ height: 15px;
197
+ width: 15px;
198
+ }
199
+
200
+ .obs-distribution {
201
+ width: 100%;
202
+ height: 4rem;
203
+ }
204
+
205
+ .obs-continuous-stats {
206
+ font-weight: lighter;
207
+ margin: 0;
208
+ }
209
+
210
+ .value-count-badge {
211
+ color: black !important;
212
+ background-color: rgb(222, 222, 222) !important;
213
+ }
214
+
215
+ .value-pct-gauge-container {
216
+ width: 1.5rem;
217
+ height: 1.5rem;
218
+ padding: 0.2rem;
219
+ }
220
+
221
+ .feature-histogram-container {
222
+ height: 1.25rem;
223
+ width: 5rem;
224
+ display: flex;
225
+ align-items: center;
226
+ }
227
+
228
+ .feature-histogram {
229
+ @extend .feature-histogram-container;
230
+ background-color: rgb(222, 222, 222);
231
+ }
232
+
233
+ .feature-histogram-tooltip .MuiChartsTooltip-markCell,.MuiChartsTooltip-labelCell {
234
+ display: none;
235
+ }
236
+
237
+ .feature-histogram-tooltip .MuiChartsTooltip-valueCell {
238
+ padding: 0.5rem !important;
239
+ }
240
+
241
+ .feature-histogram-tooltip .MuiChartsTooltip-valueCell > p {
242
+ font-size: 0.85rem !important;
243
+ }
244
+
245
+ .feature-histogram-tooltip td > p {
246
+ font-size: 0.85rem !important;
247
+ }
248
+
249
+ .feature-disease-info {
250
+ font-weight: lighter;
251
+ border-top: 1px solid var(list-group-border-color);
252
+ border-right: 0;
253
+ border-left: 0;
254
+ border-bottom: 0;
255
+ }
256
+
257
+ .feature-disease-info table {
258
+ margin: 0;
259
+ }
260
+
261
+ .var-list-toolbar {
262
+ display: flex;
263
+ justify-content: space-between;
264
+ align-items: center;
265
+ height: 3rem;
266
+ }
267
+
268
+ .var-list-toolbar .svg-inline--fa {
269
+ height: 1.2rem;
270
+ }
@@ -211,7 +211,7 @@ function ObsContinuousStats(_ref3) {
211
211
  const dataset = (0, _DatasetContext.useDataset)();
212
212
  const params = {
213
213
  url: dataset.url,
214
- obs_colname: obs.name
214
+ obsColname: obs.name
215
215
  };
216
216
  const {
217
217
  fetchedData,
@@ -309,7 +309,7 @@ function ContinuousObs(_ref4) {
309
309
  const binnedObs = binContinuous(obs);
310
310
  const params = {
311
311
  url: dataset.url,
312
- obs_col: binnedObs.name,
312
+ obsCol: binnedObs.name,
313
313
  thresholds: binnedObs.bins.thresholds,
314
314
  nBins: binnedObs.bins.nBins
315
315
  };
@@ -22,9 +22,9 @@ var _DatasetContext = require("../../context/DatasetContext");
22
22
  var _FilterContext = require("../../context/FilterContext");
23
23
  var _colorHelper = require("../../helpers/color-helper");
24
24
  var _mapHelper = require("../../helpers/map-helper");
25
- var _zarrHelper = require("../../helpers/zarr-helper");
26
25
  var _LoadingIndicators = require("../../utils/LoadingIndicators");
27
26
  var _string = require("../../utils/string");
27
+ var _zarrData = require("../../utils/zarrData");
28
28
  var _jsxRuntime = require("react/jsx-runtime");
29
29
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
30
30
  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); }
@@ -39,14 +39,6 @@ const INITIAL_VIEW_STATE = {
39
39
  bearing: 0
40
40
  };
41
41
  const EPSILON = 1e-6;
42
- const meanData = (_i, data) => {
43
- return _lodash.default.zipWith(...data, function () {
44
- for (var _len = arguments.length, values = new Array(_len), _key = 0; _key < _len; _key++) {
45
- values[_key] = arguments[_key];
46
- }
47
- return _lodash.default.mean(values);
48
- });
49
- };
50
42
  function Scatterplot(_ref) {
51
43
  let {
52
44
  radius = 30
@@ -74,83 +66,10 @@ function Scatterplot(_ref) {
74
66
  features: []
75
67
  });
76
68
  const [selectedFeatureIndexes, setSelectedFeatureIndexes] = (0, _react.useState)([]);
77
- const [obsmParams, setObsmParams] = (0, _react.useState)({
78
- url: dataset.url,
79
- path: "obsm/" + dataset.selectedObsm
80
- });
81
- const [xParams, setXParams] = (0, _react.useState)(!dataset.selectedVar ? [] : !dataset.selectedVar?.isSet ? [{
82
- url: dataset.url,
83
- path: "X",
84
- s: [null, dataset.selectedVar?.matrix_index]
85
- }] : _lodash.default.map(dataset.selectedVar?.vars, v => {
86
- return {
87
- url: dataset.url,
88
- path: "X",
89
- s: [null, v.matrix_index]
90
- };
91
- }));
92
- const [obsParams, setObsParams] = (0, _react.useState)({
93
- url: dataset.url,
94
- path: "obs/" + dataset.selectedObs?.name + (dataset.selectedObs?.type === _constants.OBS_TYPES.CATEGORICAL ? "/codes" : "")
95
- });
96
- const [labelObsParams, setLabelObsParams] = (0, _react.useState)([]);
97
- const obsmData = (0, _zarrHelper.useZarr)(obsmParams, null, _zarrHelper.GET_OPTIONS);
98
- const xData = (0, _zarrHelper.useMultipleZarr)(xParams, _zarrHelper.GET_OPTIONS, meanData);
99
- const obsData = (0, _zarrHelper.useZarr)(obsParams, null, _zarrHelper.GET_OPTIONS);
100
- const labelObsData = (0, _zarrHelper.useMultipleZarr)(labelObsParams, _zarrHelper.GET_OPTIONS);
101
- (0, _react.useEffect)(() => {
102
- setObsmParams(p => {
103
- return {
104
- ...p,
105
- path: "obsm/" + dataset.selectedObsm
106
- };
107
- });
108
- }, [dataset.selectedObsm]);
109
- (0, _react.useEffect)(() => {
110
- setXParams(!dataset.selectedVar ? [] : !dataset.selectedVar?.isSet ? [{
111
- url: dataset.url,
112
- path: "X",
113
- s: [null, dataset.selectedVar?.matrix_index]
114
- }] : _lodash.default.map(dataset.selectedVar?.vars, v => {
115
- return {
116
- url: dataset.url,
117
- path: "X",
118
- s: [null, v.matrix_index]
119
- };
120
- }));
121
- }, [dataset.selectedVar, dataset.url]);
122
- (0, _react.useEffect)(() => {
123
- setObsParams(p => {
124
- return {
125
- ...p,
126
- path: "obs/" + dataset.selectedObs?.name + (dataset.selectedObs?.type === _constants.OBS_TYPES.CATEGORICAL ? "/codes" : "")
127
- };
128
- });
129
- }, [dataset.selectedObs]);
130
- (0, _react.useEffect)(() => {
131
- setLabelObsParams(_lodash.default.map(dataset.labelObs, obs => {
132
- return {
133
- url: dataset.url,
134
- path: "obs/" + obs.name + (obs.type === _constants.OBS_TYPES.CATEGORICAL ? "/codes" : ""),
135
- key: obs.name
136
- };
137
- }));
138
- }, [dataset.labelObs, dataset.url]);
139
- (0, _react.useEffect)(() => {
140
- setObsmParams(p => {
141
- return {
142
- ...p,
143
- url: dataset.url
144
- };
145
- });
146
- setObsParams(p => {
147
- return {
148
- ...p,
149
- url: dataset.url
150
- };
151
- });
152
- }, [dataset.url]);
153
-
69
+ const obsmData = (0, _zarrData.useObsmData)();
70
+ const xData = (0, _zarrData.useXData)();
71
+ const obsData = (0, _zarrData.useObsData)();
72
+ const labelObsData = (0, _zarrData.useLabelObsData)();
154
73
  // @TODO: assert length of obsmData, xData, obsData is equal
155
74
 
156
75
  (0, _react.useEffect)(() => {
@@ -299,6 +218,8 @@ function Scatterplot(_ref) {
299
218
  }
300
219
  return inSlice;
301
220
  }, [dataset.colorEncoding, dataset.selectedObs?.bins?.binEdges, dataset.selectedObs?.omit, dataset.selectedObs?.type, dataset.sliceBy.obs, dataset.sliceBy.polygons, features.features, isCategorical]);
221
+
222
+ // @TODO: abstract filtering out of this component, maybe in FilterContext ?
302
223
  const {
303
224
  filteredIndices,
304
225
  valueMin,
@@ -34,14 +34,14 @@ function VarHistogram(_ref) {
34
34
  const isSliced = dataset.sliceBy.obs || dataset.sliceBy.polygons;
35
35
  const [params, setParams] = (0, _react.useState)({
36
36
  url: dataset.url,
37
- var_index: item.matrix_index,
38
- obs_indices: isSliced && Array.from(filteredData.obsIndices || [])
37
+ varKey: item.matrix_index,
38
+ obsIndices: isSliced && Array.from(filteredData.obsIndices || [])
39
39
  });
40
40
  (0, _react.useEffect)(() => {
41
41
  setParams(p => {
42
42
  return {
43
43
  ...p,
44
- obs_indices: isSliced && Array.from(filteredData.obsIndices || [])
44
+ obsIndices: isSliced && Array.from(filteredData.obsIndices || [])
45
45
  };
46
46
  });
47
47
  }, [filteredData.obsIndices, isSliced]);
@@ -9,14 +9,47 @@ var _iconsMaterial = require("@mui/icons-material");
9
9
  var _lodash = _interopRequireDefault(require("lodash"));
10
10
  var _reactBootstrap = require("react-bootstrap");
11
11
  var _VarItem = require("./VarItem");
12
+ var _VarListToolbar = require("./VarListToolbar");
12
13
  var _VarSet = require("./VarSet");
13
14
  var _constants = require("../../constants/constants");
14
15
  var _DatasetContext = require("../../context/DatasetContext");
16
+ var _LoadingIndicators = require("../../utils/LoadingIndicators");
15
17
  var _requests = require("../../utils/requests");
16
18
  var _jsxRuntime = require("react/jsx-runtime");
17
19
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
18
20
  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); }
19
21
  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; }
22
+ // @TODO: optimize
23
+ const useVarMean = function (varKeys) {
24
+ let enabled = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
25
+ const ENDPOINT = "matrix/mean";
26
+ const dataset = (0, _DatasetContext.useDataset)();
27
+ const [params, setParams] = (0, _react.useState)({
28
+ url: dataset.url,
29
+ varKeys: _lodash.default.map(varKeys, v => v.isSet ? {
30
+ name: v.name,
31
+ indices: v.vars.map(v => v.index)
32
+ } : v.index),
33
+ varNamesCol: dataset.varNamesCol
34
+ });
35
+ (0, _react.useEffect)(() => {
36
+ setParams(p => {
37
+ return {
38
+ ...p,
39
+ varKeys: _lodash.default.map(varKeys, v => v.isSet ? {
40
+ name: v.name,
41
+ indices: v.vars.map(v => v.index)
42
+ } : v.index)
43
+ };
44
+ });
45
+ }, [varKeys]);
46
+ return (0, _requests.useFetch)(ENDPOINT, params, {
47
+ enabled: enabled
48
+ });
49
+ };
50
+
51
+ // @TODO: display where disease data comes from
52
+ // add to disease dataset metadata
20
53
  function DiseaseVarList(_ref) {
21
54
  let {
22
55
  makeListItem
@@ -25,6 +58,7 @@ function DiseaseVarList(_ref) {
25
58
  const dataset = (0, _DatasetContext.useDataset)();
26
59
  const dispatch = (0, _DatasetContext.useDatasetDispatch)();
27
60
  const [diseaseVars, setDiseaseVars] = (0, _react.useState)([]);
61
+ const [sortedDiseaseVars, setSortedDiseaseVars] = (0, _react.useState)([]);
28
62
  const [params, setParams] = (0, _react.useState)({
29
63
  url: dataset.url,
30
64
  col: dataset.varNamesCol,
@@ -39,25 +73,43 @@ function DiseaseVarList(_ref) {
39
73
  };
40
74
  });
41
75
  }, [dataset.selectedDisease]);
42
- const {
43
- fetchedData,
44
- isPending,
45
- serverError
46
- } = (0, _requests.useFetch)(ENDPOINT, params, {
76
+ const diseaseData = (0, _requests.useFetch)(ENDPOINT, params, {
47
77
  enabled: !!params.diseaseId
48
78
  });
49
79
  (0, _react.useEffect)(() => {
50
- if (!isPending && !serverError) {
51
- setDiseaseVars(fetchedData);
80
+ if (!diseaseData.isPending && !diseaseData.serverError) {
81
+ setDiseaseVars(diseaseData.fetchedData);
82
+ }
83
+ }, [diseaseData.fetchedData, diseaseData.isPending, diseaseData.serverError]);
84
+ const varMeans = useVarMean(diseaseVars, diseaseVars && dataset.varSort.disease.sort === _constants.VAR_SORT.MATRIX);
85
+ (0, _react.useEffect)(() => {
86
+ if (dataset.varSort.disease.sort === _constants.VAR_SORT.MATRIX) {
87
+ if (!varMeans.isPending && !varMeans.serverError) {
88
+ setSortedDiseaseVars(_lodash.default.orderBy(diseaseVars, o => {
89
+ return varMeans.fetchedData[o.name];
90
+ }, dataset.varSort.disease.sortOrder));
91
+ }
92
+ } else if (dataset.varSort.disease.sort === _constants.VAR_SORT.NAME) {
93
+ setSortedDiseaseVars(_lodash.default.orderBy(diseaseVars, "name", dataset.varSort.disease.sortOrder));
94
+ } else {
95
+ setSortedDiseaseVars(diseaseVars);
52
96
  }
53
- }, [fetchedData, isPending, serverError]);
54
- const diseaseVarList = _lodash.default.map(diseaseVars, item => {
97
+ }, [dataset.varSort.disease.sort, dataset.varSort.disease.sortOrder, diseaseVars, varMeans.fetchedData, varMeans.isPending, varMeans.serverError]);
98
+ const diseaseVarList = _lodash.default.map(sortedDiseaseVars, item => {
55
99
  return makeListItem(item, true);
56
100
  });
101
+ const isPending = diseaseData.isPending || varMeans.isPending && dataset.varSort.disease.sort === _constants.VAR_SORT.MATRIX;
57
102
  return /*#__PURE__*/(0, _jsxRuntime.jsx)(_jsxRuntime.Fragment, {
58
- children: dataset.selectedDisease && (!diseaseVars.length ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactBootstrap.Alert, {
59
- variant: "light",
60
- children: "No disease genes found."
103
+ children: dataset.selectedDisease && (!isPending && !diseaseVars?.length ? /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
104
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
105
+ className: "d-flex justify-content-between mt-3",
106
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)("h5", {
107
+ children: "Disease genes"
108
+ })
109
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactBootstrap.Alert, {
110
+ variant: "light",
111
+ children: "No disease genes found."
112
+ })]
61
113
  }) : /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
62
114
  children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)("div", {
63
115
  className: "d-flex justify-content-between mt-3",
@@ -74,8 +126,13 @@ function DiseaseVarList(_ref) {
74
126
  })]
75
127
  }), /*#__PURE__*/(0, _jsxRuntime.jsx)("p", {
76
128
  children: dataset.selectedDisease?.name
77
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactBootstrap.ListGroup, {
78
- children: diseaseVarList
129
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_VarListToolbar.VarListToolbar, {
130
+ varType: "disease"
131
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", {
132
+ className: "position-relative",
133
+ children: [isPending && /*#__PURE__*/(0, _jsxRuntime.jsx)(_LoadingIndicators.LoadingSpinner, {}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactBootstrap.ListGroup, {
134
+ children: diseaseVarList
135
+ })]
79
136
  })]
80
137
  }))
81
138
  });
@@ -90,6 +147,7 @@ function VarNamesList(_ref2) {
90
147
  const dispatch = (0, _DatasetContext.useDatasetDispatch)();
91
148
  const [varButtons, setVarButtons] = (0, _react.useState)(mode === _constants.SELECTION_MODES.SINGLE ? dataset.selectedVar ? _lodash.default.unionWith([dataset.selectedVar], dataset.varSets, _lodash.default.isEqual) : [...dataset.varSets] : [...dataset.selectedMultiVar, ...dataset.varSets]);
92
149
  const [active, setActive] = (0, _react.useState)(mode === _constants.SELECTION_MODES.SINGLE ? dataset.selectedVar?.matrix_index || dataset.selectedVar?.name : dataset.selectedMultiVar.map(i => i.matrix_index || i.name));
150
+ const [sortedVarButtons, setSortedVarButtons] = (0, _react.useState)([]);
93
151
  (0, _react.useEffect)(() => {
94
152
  if (mode === _constants.SELECTION_MODES.SINGLE) {
95
153
  setVarButtons(v => {
@@ -139,6 +197,22 @@ function VarNamesList(_ref2) {
139
197
  });
140
198
  }
141
199
  }, [mode, dataset.varSets, dataset.selectedVar?.isSet, dataset.selectedVar?.name, dispatch]);
200
+ const varMeans = useVarMean(varButtons, dataset.varSort.var.sort === _constants.VAR_SORT.MATRIX);
201
+
202
+ // @TODO: deferr sortedVarButtons ?
203
+ (0, _react.useEffect)(() => {
204
+ if (dataset.varSort.var.sort === _constants.VAR_SORT.MATRIX) {
205
+ if (!varMeans.isPending && !varMeans.serverError) {
206
+ setSortedVarButtons(_lodash.default.orderBy(varButtons, o => {
207
+ return varMeans.fetchedData[o.name];
208
+ }, dataset.varSort.var.sortOrder));
209
+ }
210
+ } else if (dataset.varSort.var.sort === _constants.VAR_SORT.NAME) {
211
+ setSortedVarButtons(_lodash.default.orderBy(varButtons, "name", dataset.varSort.var.sortOrder));
212
+ } else {
213
+ setSortedVarButtons(varButtons);
214
+ }
215
+ }, [dataset.varSort.var.sort, dataset.varSort.var.sortOrder, varButtons, varMeans.isPending, varMeans.serverError, varMeans.fetchedData]);
142
216
  const makeListItem = function (item) {
143
217
  let isDiseaseGene = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
144
218
  return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactBootstrap.ListGroup.Item, {
@@ -160,7 +234,7 @@ function VarNamesList(_ref2) {
160
234
  })
161
235
  }, set.name);
162
236
  };
163
- const varList = _lodash.default.map(varButtons, item => {
237
+ const varList = _lodash.default.map(sortedVarButtons, item => {
164
238
  if (item.isSet) {
165
239
  return makeSetListItem(item);
166
240
  } else {
@@ -179,6 +253,7 @@ function VarNamesList(_ref2) {
179
253
  }
180
254
  return setName;
181
255
  };
256
+ const isPending = varMeans.isPending && dataset.varSort.var.sort === _constants.VAR_SORT.MATRIX;
182
257
  return /*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
183
258
  className: "position-relative",
184
259
  children: /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", {
@@ -216,13 +291,22 @@ function VarNamesList(_ref2) {
216
291
  children: "clear"
217
292
  })]
218
293
  })]
219
- }), !varList.length ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactBootstrap.Alert, {
220
- variant: "light",
221
- children: "Search for a feature."
222
- }) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactBootstrap.ListGroup, {
223
- children: varList
224
- }), showDiseaseVarList && /*#__PURE__*/(0, _jsxRuntime.jsx)(DiseaseVarList, {
225
- makeListItem: makeListItem
294
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_jsxRuntime.Fragment, {
295
+ children: !varList.length ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactBootstrap.Alert, {
296
+ variant: "light",
297
+ children: "Search for a feature."
298
+ }) : /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
299
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_VarListToolbar.VarListToolbar, {}), /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", {
300
+ className: "position-relative",
301
+ children: [isPending && /*#__PURE__*/(0, _jsxRuntime.jsx)(_LoadingIndicators.LoadingSpinner, {}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactBootstrap.ListGroup, {
302
+ children: varList
303
+ })]
304
+ })]
305
+ })
306
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_jsxRuntime.Fragment, {
307
+ children: showDiseaseVarList && /*#__PURE__*/(0, _jsxRuntime.jsx)(DiseaseVarList, {
308
+ makeListItem: makeListItem
309
+ })
226
310
  })]
227
311
  })
228
312
  });
@@ -0,0 +1,101 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.VarListToolbar = VarListToolbar;
7
+ var _react = _interopRequireWildcard(require("react"));
8
+ var _freeSolidSvgIcons = require("@fortawesome/free-solid-svg-icons");
9
+ var _reactFontawesome = require("@fortawesome/react-fontawesome");
10
+ var _material = require("@mui/material");
11
+ var _reactBootstrap = require("react-bootstrap");
12
+ var _constants = require("../../constants/constants");
13
+ var _DatasetContext = require("../../context/DatasetContext");
14
+ var _jsxRuntime = require("react/jsx-runtime");
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
+ // @TODO: set option for "var" and "disease"
18
+ function VarListToolbar(_ref) {
19
+ let {
20
+ varType = "var"
21
+ } = _ref;
22
+ const dataset = (0, _DatasetContext.useDataset)();
23
+ const dispatch = (0, _DatasetContext.useDatasetDispatch)();
24
+ const [sort, setSort] = (0, _react.useState)(dataset.varSort.var.sort);
25
+ const [nameSortOrder, setNameSortOrder] = (0, _react.useState)(dataset.varSort.var.sortOrder);
26
+ const [matrixSortOrder, setMatrixSortOrder] = (0, _react.useState)(dataset.varSort.var.sortOrder);
27
+ const handleSort = (sortValue, sortOrder, setSortOrder) => {
28
+ if (sort !== sortValue) {
29
+ setSort(sortValue);
30
+ dispatch({
31
+ type: "set.varSort",
32
+ var: varType,
33
+ sort: sortValue,
34
+ sortOrder: sortOrder
35
+ });
36
+ } else {
37
+ const newSortOrder = sortOrder === _constants.VAR_SORT_ORDER.ASC ? _constants.VAR_SORT_ORDER.DESC : _constants.VAR_SORT_ORDER.ASC;
38
+ setSortOrder(newSortOrder);
39
+ dispatch({
40
+ type: "set.varSort",
41
+ var: varType,
42
+ sort: sortValue,
43
+ sortOrder: newSortOrder
44
+ });
45
+ }
46
+ };
47
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactBootstrap.Navbar, {
48
+ className: "var-list-toolbar",
49
+ children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactBootstrap.InputGroup, {
50
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactBootstrap.InputGroup.Text, {
51
+ children: "Sort by:"
52
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_material.ToggleButtonGroup, {
53
+ "aria-label": "Sort feature by",
54
+ size: "small",
55
+ className: "mh-100",
56
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_material.ToggleButton, {
57
+ value: _constants.VAR_SORT.NAME,
58
+ "aria-label": "alphabetical",
59
+ title: "Sort alphabetically",
60
+ selected: sort === _constants.VAR_SORT.NAME,
61
+ onChange: () => {
62
+ handleSort(_constants.VAR_SORT.NAME, nameSortOrder, setNameSortOrder);
63
+ },
64
+ children: nameSortOrder === _constants.VAR_SORT_ORDER.ASC ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactFontawesome.FontAwesomeIcon, {
65
+ icon: _freeSolidSvgIcons.faArrowDownAZ
66
+ }) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactFontawesome.FontAwesomeIcon, {
67
+ icon: _freeSolidSvgIcons.faArrowUpZA
68
+ })
69
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.ToggleButton, {
70
+ value: _constants.VAR_SORT.MATRIX,
71
+ "aria-label": "matrix value",
72
+ title: "Sort by matrix value",
73
+ selected: sort === _constants.VAR_SORT.MATRIX,
74
+ onChange: () => {
75
+ handleSort(_constants.VAR_SORT.MATRIX, matrixSortOrder, setMatrixSortOrder);
76
+ },
77
+ children: matrixSortOrder === _constants.VAR_SORT_ORDER.ASC ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactFontawesome.FontAwesomeIcon, {
78
+ icon: _freeSolidSvgIcons.faArrowDown19
79
+ }) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactFontawesome.FontAwesomeIcon, {
80
+ icon: _freeSolidSvgIcons.faArrowUp91
81
+ })
82
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.ToggleButton, {
83
+ value: "none",
84
+ "aria-label": "none",
85
+ title: "No sorting",
86
+ onClick: () => {
87
+ setSort(_constants.VAR_SORT.NONE);
88
+ dispatch({
89
+ type: "set.varSort.sort",
90
+ var: varType,
91
+ sort: _constants.VAR_SORT.NONE
92
+ });
93
+ },
94
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactFontawesome.FontAwesomeIcon, {
95
+ icon: _freeSolidSvgIcons.faXmark
96
+ })
97
+ })]
98
+ })]
99
+ })
100
+ });
101
+ }
@@ -3,7 +3,7 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.VIOLIN_MODES = exports.VIOLINPLOT_SCALES = exports.UNSELECTED_POLYGON_FILLCOLOR = exports.SELECTION_MODES = exports.SELECTED_POLYGON_FILLCOLOR = exports.OBS_TYPES = exports.MATRIXPLOT_SCALES = exports.LOCAL_STORAGE_KEY = exports.DOTPLOT_SCALES = exports.COLOR_ENCODINGS = void 0;
6
+ exports.VIOLIN_MODES = exports.VIOLINPLOT_SCALES = exports.VAR_SORT_ORDER = exports.VAR_SORT = exports.UNSELECTED_POLYGON_FILLCOLOR = exports.SELECTION_MODES = exports.SELECTED_POLYGON_FILLCOLOR = exports.OBS_TYPES = exports.MATRIXPLOT_SCALES = exports.LOCAL_STORAGE_KEY = exports.DOTPLOT_SCALES = exports.COLOR_ENCODINGS = void 0;
7
7
  const LOCAL_STORAGE_KEY = exports.LOCAL_STORAGE_KEY = "CHERITA";
8
8
  const COLOR_ENCODINGS = exports.COLOR_ENCODINGS = {
9
9
  VAR: "var",
@@ -15,6 +15,15 @@ const OBS_TYPES = exports.OBS_TYPES = {
15
15
  CONTINUOUS: "continuous",
16
16
  BOOLEAN: "boolean"
17
17
  };
18
+ const VAR_SORT = exports.VAR_SORT = {
19
+ NONE: null,
20
+ NAME: "name",
21
+ MATRIX: "matrix"
22
+ };
23
+ const VAR_SORT_ORDER = exports.VAR_SORT_ORDER = {
24
+ ASC: "asc",
25
+ DESC: "desc"
26
+ };
18
27
  const SELECTED_POLYGON_FILLCOLOR = exports.SELECTED_POLYGON_FILLCOLOR = [107, 170, 209, 255 / 2];
19
28
  const UNSELECTED_POLYGON_FILLCOLOR = exports.UNSELECTED_POLYGON_FILLCOLOR = [167, 191, 211, 255 / 3];
20
29
  const SELECTION_MODES = exports.SELECTION_MODES = {
@@ -85,7 +85,17 @@ const initialDataset = {
85
85
  expressionCutoff: 0.0
86
86
  },
87
87
  diseaseDatasets: [],
88
- selectedDisease: null
88
+ selectedDisease: null,
89
+ varSort: {
90
+ var: {
91
+ sort: _constants.VAR_SORT.NONE,
92
+ sortOrder: _constants.VAR_SORT_ORDER.ASC
93
+ },
94
+ disease: {
95
+ sort: _constants.VAR_SORT.NONE,
96
+ sortOrder: _constants.VAR_SORT_ORDER.ASC
97
+ }
98
+ }
89
99
  };
90
100
  const initializer = initialState => {
91
101
  const localObj = (JSON.parse(localStorage.getItem(_constants.LOCAL_STORAGE_KEY)) || {})[initialState.url] || {};
@@ -478,6 +488,45 @@ function datasetReducer(dataset, action) {
478
488
  labelObs: []
479
489
  };
480
490
  }
491
+ case "set.varSort":
492
+ {
493
+ return {
494
+ ...dataset,
495
+ varSort: {
496
+ ...dataset.varSort,
497
+ [action.var]: {
498
+ sort: action.sort,
499
+ sortOrder: action.sortOrder
500
+ }
501
+ }
502
+ };
503
+ }
504
+ case "set.varSort.sort":
505
+ {
506
+ return {
507
+ ...dataset,
508
+ varSort: {
509
+ ...dataset.varSort,
510
+ [action.var]: {
511
+ ...dataset.varSort[action.var],
512
+ sort: action.sort
513
+ }
514
+ }
515
+ };
516
+ }
517
+ case "set.varSort.sortOrder":
518
+ {
519
+ return {
520
+ ...dataset,
521
+ varSort: {
522
+ ...dataset.varSort,
523
+ [action.var]: {
524
+ ...dataset.varSort[action.var],
525
+ sortOrder: action.sortOrder
526
+ }
527
+ }
528
+ };
529
+ }
481
530
  default:
482
531
  {
483
532
  throw Error("Unknown action: " + action.type);
@@ -15,6 +15,7 @@ const FilteredDataContext = exports.FilteredDataContext = /*#__PURE__*/(0, _reac
15
15
  const FilteredDataDispatchContext = exports.FilteredDataDispatchContext = /*#__PURE__*/(0, _react.createContext)(null);
16
16
  const initialFilterData = {
17
17
  obsIndices: [],
18
+ // @TODO: split to byObs and byPolygons ?
18
19
  varIndices: []
19
20
  };
20
21
  function FilterProvider(_ref) {
@@ -7,7 +7,9 @@ exports.fetchData = fetchData;
7
7
  exports.useFetch = exports.useDebouncedFetch = void 0;
8
8
  var _reactQuery = require("@tanstack/react-query");
9
9
  var _usehooks = require("@uidotdev/usehooks");
10
+ var _lodash = _interopRequireDefault(require("lodash"));
10
11
  var _errors = require("./errors");
12
+ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
11
13
  async function fetchData(endpoint, params) {
12
14
  let signal = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;
13
15
  let ms = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 300000;
@@ -46,9 +48,9 @@ const useFetch = function (endpoint, params) {
46
48
  enabled = true
47
49
  } = opts;
48
50
  const {
49
- data: fetchedData,
50
- isLoading: isPending,
51
- error: serverError
51
+ data: fetchedData = null,
52
+ isLoading: isPending = false,
53
+ error: serverError = null
52
54
  } = (0, _reactQuery.useQuery)({
53
55
  queryKey: [endpoint, params],
54
56
  queryFn: _ref => {
@@ -79,9 +81,9 @@ const useDebouncedFetch = function (endpoint, params) {
79
81
  } = opts;
80
82
  const debouncedParams = (0, _usehooks.useDebounce)(params, delay);
81
83
  const {
82
- data: fetchedData,
83
- isLoading: isPending,
84
- error: serverError
84
+ data: fetchedData = null,
85
+ isLoading: isPending = false,
86
+ error: serverError = null
85
87
  } = (0, _reactQuery.useQuery)({
86
88
  queryKey: [endpoint, debouncedParams],
87
89
  queryFn: _ref2 => {
@@ -0,0 +1,101 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.useXData = exports.useObsmData = exports.useObsData = exports.useLabelObsData = void 0;
7
+ var _react = require("react");
8
+ var _lodash = _interopRequireDefault(require("lodash"));
9
+ var _constants = require("../constants/constants");
10
+ var _DatasetContext = require("../context/DatasetContext");
11
+ var _zarrHelper = require("../helpers/zarr-helper");
12
+ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
13
+ const useObsmData = () => {
14
+ const dataset = (0, _DatasetContext.useDataset)();
15
+ const [obsmParams, setObsmParams] = (0, _react.useState)({
16
+ url: dataset.url,
17
+ path: "obsm/" + dataset.selectedObsm
18
+ });
19
+ (0, _react.useEffect)(() => {
20
+ setObsmParams({
21
+ url: dataset.url,
22
+ path: "obsm/" + dataset.selectedObsm
23
+ });
24
+ }, [dataset.url, dataset.selectedObsm]);
25
+ return (0, _zarrHelper.useZarr)(obsmParams, null, _zarrHelper.GET_OPTIONS);
26
+ };
27
+ exports.useObsmData = useObsmData;
28
+ const meanData = (_i, data) => {
29
+ return _lodash.default.zipWith(...data, function () {
30
+ for (var _len = arguments.length, values = new Array(_len), _key = 0; _key < _len; _key++) {
31
+ values[_key] = arguments[_key];
32
+ }
33
+ return _lodash.default.mean(values);
34
+ });
35
+ };
36
+ const useXData = function () {
37
+ let agg = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : meanData;
38
+ const dataset = (0, _DatasetContext.useDataset)();
39
+ const [xParams, setXParams] = (0, _react.useState)(!dataset.selectedVar ? [] : !dataset.selectedVar?.isSet ? [{
40
+ url: dataset.url,
41
+ path: "X",
42
+ s: [null, dataset.selectedVar?.matrix_index]
43
+ }] : _lodash.default.map(dataset.selectedVar?.vars, v => {
44
+ return {
45
+ url: dataset.url,
46
+ path: "X",
47
+ s: [null, v.matrix_index]
48
+ };
49
+ }));
50
+ (0, _react.useEffect)(() => {
51
+ setXParams(!dataset.selectedVar ? [] : !dataset.selectedVar?.isSet ? [{
52
+ url: dataset.url,
53
+ path: "X",
54
+ s: [null, dataset.selectedVar?.matrix_index]
55
+ }] : _lodash.default.map(dataset.selectedVar?.vars, v => {
56
+ return {
57
+ url: dataset.url,
58
+ path: "X",
59
+ s: [null, v.matrix_index]
60
+ };
61
+ }));
62
+ }, [dataset.url, dataset.selectedVar]);
63
+ return (0, _zarrHelper.useMultipleZarr)(xParams, _zarrHelper.GET_OPTIONS, agg);
64
+ };
65
+ exports.useXData = useXData;
66
+ const useObsData = () => {
67
+ const dataset = (0, _DatasetContext.useDataset)();
68
+ const [obsParams, setObsParams] = (0, _react.useState)({
69
+ url: dataset.url,
70
+ path: "obs/" + dataset.selectedObs?.name + (dataset.selectedObs?.type === _constants.OBS_TYPES.CATEGORICAL ? "/codes" : "")
71
+ });
72
+ (0, _react.useEffect)(() => {
73
+ setObsParams({
74
+ url: dataset.url,
75
+ path: "obs/" + dataset.selectedObs?.name + (dataset.selectedObs?.type === _constants.OBS_TYPES.CATEGORICAL ? "/codes" : "")
76
+ });
77
+ }, [dataset.url, dataset.selectedObs]);
78
+ return (0, _zarrHelper.useZarr)(obsParams, null, _zarrHelper.GET_OPTIONS);
79
+ };
80
+ exports.useObsData = useObsData;
81
+ const useLabelObsData = () => {
82
+ const dataset = (0, _DatasetContext.useDataset)();
83
+ const [labelObsParams, setLabelObsParams] = (0, _react.useState)(_lodash.default.map(dataset.labelObs, obs => {
84
+ return {
85
+ url: dataset.url,
86
+ path: "obs/" + obs.name + (obs.type === _constants.OBS_TYPES.CATEGORICAL ? "/codes" : ""),
87
+ key: obs.name
88
+ };
89
+ }));
90
+ (0, _react.useEffect)(() => {
91
+ setLabelObsParams(_lodash.default.map(dataset.labelObs, obs => {
92
+ return {
93
+ url: dataset.url,
94
+ path: "obs/" + obs.name + (obs.type === _constants.OBS_TYPES.CATEGORICAL ? "/codes" : ""),
95
+ key: obs.name
96
+ };
97
+ }));
98
+ }, [dataset.labelObs, dataset.url]);
99
+ return (0, _zarrHelper.useMultipleZarr)(labelObsParams, _zarrHelper.GET_OPTIONS);
100
+ };
101
+ exports.useLabelObsData = useLabelObsData;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@haniffalab/cherita-react",
3
- "version": "0.2.0-dev.2024-12-16.f6e39628",
3
+ "version": "0.2.0-dev.2024-12-16.ab9c5057",
4
4
  "author": "",
5
5
  "license": "",
6
6
  "main": "dist/index.js",
@@ -101,5 +101,5 @@
101
101
  "url": "https://github.com/haniffalab/cherita-react/issues"
102
102
  },
103
103
  "homepage": "https://github.com/haniffalab/cherita-react#readme",
104
- "prereleaseSha": "f6e396285b478f157db2dd9d8bdd8adbaa262d4c"
104
+ "prereleaseSha": "ab9c5057639276863637b6dd046ba3dcbc3b6804"
105
105
  }