@haniffalab/cherita-react 1.5.0 → 2.0.0

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 (80) hide show
  1. package/dist/cjs/components/dotplot/Dotplot.js +24 -19
  2. package/dist/cjs/components/heatmap/Heatmap.js +24 -19
  3. package/dist/cjs/components/matrixplot/Matrixplot.js +24 -19
  4. package/dist/cjs/components/obs-list/ObsItem.js +2 -2
  5. package/dist/cjs/components/offcanvas/{index.js → OffCanvas.js} +34 -1
  6. package/dist/cjs/components/{full-page → plot}/PlotAlert.js +1 -1
  7. package/dist/cjs/components/scatterplot/Scatterplot.js +57 -32
  8. package/dist/cjs/components/scatterplot/ScatterplotControls.js +74 -28
  9. package/dist/cjs/components/scatterplot/SpatialControls.js +37 -24
  10. package/dist/cjs/components/search-bar/SearchBar.js +9 -4
  11. package/dist/cjs/components/search-bar/SearchInfo.js +68 -25
  12. package/dist/cjs/components/search-bar/SearchResults.js +56 -47
  13. package/dist/cjs/components/toolbar/Toolbar.js +47 -42
  14. package/dist/cjs/components/var-list/VarList.js +18 -12
  15. package/dist/cjs/components/var-list/VarSet.js +1 -1
  16. package/dist/cjs/components/violin/Violin.js +26 -21
  17. package/dist/cjs/context/DatasetContext.js +10 -2
  18. package/dist/cjs/context/SettingsContext.js +19 -5
  19. package/dist/cjs/helpers/zarr-helper.js +20 -6
  20. package/dist/cjs/index.js +16 -23
  21. package/dist/cjs/utils/Skeleton.js +38 -2
  22. package/dist/cjs/utils/Slider.js +61 -0
  23. package/dist/cjs/utils/VirtualizedTable.js +97 -0
  24. package/dist/cjs/utils/parquetData.js +60 -0
  25. package/dist/cjs/utils/useNCBIData.js +35 -0
  26. package/dist/cjs/utils/usePlotVisibility.js +21 -0
  27. package/dist/cjs/utils/zarrData.js +51 -2
  28. package/dist/cjs/{components/plot/Plot.js → views/ObservationFeature/EmbeddedPlot.js} +41 -37
  29. package/dist/cjs/{components/full-page/FullPage.js → views/ObservationFeature/StandardView.js} +40 -49
  30. package/dist/cjs/views/ObservationFeature/index.js +12 -0
  31. package/dist/cjs/views/PerturbationMap/EmbeddedPlot.js +56 -0
  32. package/dist/cjs/views/PerturbationMap/ObsExplorer.js +299 -0
  33. package/dist/cjs/{apps/perturbgen/PerturbGen.js → views/PerturbationMap/StandardView.js} +19 -27
  34. package/dist/cjs/views/PerturbationMap/index.js +12 -0
  35. package/dist/css/cherita.css +16 -7
  36. package/dist/css/cherita.css.map +1 -1
  37. package/dist/esm/components/dotplot/Dotplot.js +25 -20
  38. package/dist/esm/components/heatmap/Heatmap.js +25 -20
  39. package/dist/esm/components/matrixplot/Matrixplot.js +25 -20
  40. package/dist/esm/components/obs-list/ObsItem.js +2 -2
  41. package/dist/esm/components/offcanvas/{index.js → OffCanvas.js} +33 -1
  42. package/dist/esm/components/{full-page → plot}/PlotAlert.js +1 -1
  43. package/dist/esm/components/scatterplot/Scatterplot.js +57 -32
  44. package/dist/esm/components/scatterplot/ScatterplotControls.js +74 -28
  45. package/dist/esm/components/scatterplot/SpatialControls.js +37 -24
  46. package/dist/esm/components/search-bar/SearchBar.js +9 -4
  47. package/dist/esm/components/search-bar/SearchInfo.js +69 -26
  48. package/dist/esm/components/search-bar/SearchResults.js +56 -47
  49. package/dist/esm/components/toolbar/Toolbar.js +44 -39
  50. package/dist/esm/components/var-list/VarList.js +18 -12
  51. package/dist/esm/components/var-list/VarSet.js +1 -1
  52. package/dist/esm/components/violin/Violin.js +27 -22
  53. package/dist/esm/context/DatasetContext.js +10 -2
  54. package/dist/esm/context/SettingsContext.js +19 -5
  55. package/dist/esm/helpers/zarr-helper.js +19 -5
  56. package/dist/esm/index.js +4 -5
  57. package/dist/esm/utils/Skeleton.js +35 -1
  58. package/dist/esm/utils/Slider.js +54 -0
  59. package/dist/esm/utils/VirtualizedTable.js +89 -0
  60. package/dist/esm/utils/parquetData.js +51 -0
  61. package/dist/esm/utils/useNCBIData.js +28 -0
  62. package/dist/esm/utils/usePlotVisibility.js +14 -0
  63. package/dist/esm/utils/zarrData.js +48 -0
  64. package/dist/esm/{components/plot/Plot.js → views/ObservationFeature/EmbeddedPlot.js} +37 -33
  65. package/dist/esm/{components/full-page/FullPage.js → views/ObservationFeature/StandardView.js} +35 -44
  66. package/dist/esm/views/ObservationFeature/index.js +6 -0
  67. package/dist/esm/views/PerturbationMap/EmbeddedPlot.js +50 -0
  68. package/dist/esm/views/PerturbationMap/ObsExplorer.js +292 -0
  69. package/dist/esm/{apps/perturbgen/PerturbGen.js → views/PerturbationMap/StandardView.js} +15 -23
  70. package/dist/esm/views/PerturbationMap/index.js +6 -0
  71. package/package.json +8 -4
  72. package/scss/cherita.scss +1 -1
  73. package/scss/components/layouts.scss +0 -1
  74. package/scss/components/lists.scss +1 -1
  75. package/scss/components/plotly.scss +10 -1
  76. package/scss/components/plots.scss +7 -4
  77. package/dist/cjs/apps/perturbgen/ObsExplorer.js +0 -153
  78. package/dist/esm/apps/perturbgen/ObsExplorer.js +0 -147
  79. /package/dist/cjs/components/{full-page → plot}/PlotTypeSelector.js +0 -0
  80. /package/dist/esm/components/{full-page → plot}/PlotTypeSelector.js +0 -0
@@ -6,16 +6,19 @@ function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e =
6
6
  import { useState, useEffect } from 'react';
7
7
  import { faPlus } from '@fortawesome/free-solid-svg-icons';
8
8
  import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
9
+ import Tooltip from '@mui/material/Tooltip';
9
10
  import _ from 'lodash';
10
11
  import { Button, ListGroup } from 'react-bootstrap';
11
12
  import { VAR_SORT } from '../../constants/constants';
12
13
  import { useDataset } from '../../context/DatasetContext';
13
14
  import { useSettings, useSettingsDispatch } from '../../context/SettingsContext';
14
15
  import { useFetch } from '../../utils/requests';
16
+ import { useNCBIData } from '../../utils/useNCBIData';
15
17
  import { VarDiseaseInfo } from '../var-list/VarItem';
16
18
  import { sortMeans, useVarMean } from '../var-list/VarList';
17
- import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
19
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
18
20
  export function VarInfo(_ref) {
21
+ var _ncbiData$reports;
19
22
  let {
20
23
  varItem
21
24
  } = _ref;
@@ -32,6 +35,14 @@ export function VarInfo(_ref) {
32
35
  });
33
36
  });
34
37
  }, [varItem.name]);
38
+ const {
39
+ fetchedData: ncbiData,
40
+ isPending: isNCBIPending,
41
+ serverError: ncbiError
42
+ } = useNCBIData({
43
+ symbol: varItem.name
44
+ });
45
+ const geneDescription = ncbiData === null || ncbiData === void 0 || (_ncbiData$reports = ncbiData.reports) === null || _ncbiData$reports === void 0 || (_ncbiData$reports = _ncbiData$reports[0]) === null || _ncbiData$reports === void 0 || (_ncbiData$reports = _ncbiData$reports.gene) === null || _ncbiData$reports === void 0 || (_ncbiData$reports = _ncbiData$reports.summary) === null || _ncbiData$reports === void 0 || (_ncbiData$reports = _ncbiData$reports[0]) === null || _ncbiData$reports === void 0 ? void 0 : _ncbiData$reports.description;
35
46
  const {
36
47
  fetchedData,
37
48
  isPending,
@@ -42,13 +53,42 @@ export function VarInfo(_ref) {
42
53
  });
43
54
  const hasDiseaseInfo = !isPending && !serverError && !!(fetchedData !== null && fetchedData !== void 0 && fetchedData.length);
44
55
  return /*#__PURE__*/_jsxs("div", {
45
- children: [/*#__PURE__*/_jsx("h5", {
56
+ children: [/*#__PURE__*/_jsx("h2", {
46
57
  children: varItem.name
58
+ }), isNCBIPending && /*#__PURE__*/_jsx("p", {
59
+ className: "text-muted small",
60
+ children: "Loading gene description\u2026"
61
+ }), !isNCBIPending && !ncbiError && geneDescription && /*#__PURE__*/_jsxs(_Fragment, {
62
+ children: [/*#__PURE__*/_jsx("p", {
63
+ className: "small",
64
+ children: geneDescription
65
+ }), /*#__PURE__*/_jsxs("p", {
66
+ className: "text-muted small",
67
+ children: ["Data fetched from", ' ', /*#__PURE__*/_jsx("a", {
68
+ href: "https://www.ncbi.nlm.nih.gov/datasets/docs/v2/api/rest-api/",
69
+ target: "_blank",
70
+ rel: "noreferrer",
71
+ children: "NCBI API"
72
+ }), ' · ', /*#__PURE__*/_jsx("a", {
73
+ href: "https://www.ncbi.nlm.nih.gov/gene/?term=".concat(encodeURIComponent(varItem.name)),
74
+ target: "_blank",
75
+ rel: "noreferrer",
76
+ children: "Gene page"
77
+ })]
78
+ })]
47
79
  }), !!dataset.diseaseDatasets.length && isPending && /*#__PURE__*/_jsx("p", {
48
80
  children: "Loading..."
49
81
  }), hasDiseaseInfo && /*#__PURE__*/_jsxs(_Fragment, {
50
- children: [/*#__PURE__*/_jsx("h6", {
51
- children: "Associated diseases"
82
+ children: [/*#__PURE__*/_jsx("h5", {
83
+ className: "fw-bold mb-2",
84
+ children: /*#__PURE__*/_jsx(Tooltip, {
85
+ title: /*#__PURE__*/_jsx(_Fragment, {
86
+ children: "Reported and inferred diseases associated with this gene, including supporting metadata."
87
+ }),
88
+ placement: "left",
89
+ arrow: true,
90
+ children: "Associated Diseases"
91
+ })
52
92
  }), /*#__PURE__*/_jsx(VarDiseaseInfo, {
53
93
  data: fetchedData
54
94
  })]
@@ -104,29 +144,32 @@ export function DiseaseInfo(_ref2) {
104
144
  }
105
145
  }, [settings.varSort.disease.sort, settings.varSort.disease.sortOrder, diseaseVars, varMeans.fetchedData, varMeans.isPending, varMeans.serverError]);
106
146
  const diseaseVarList = _.map(sortedDiseaseVars, v => {
107
- return /*#__PURE__*/_jsx(ListGroup.Item, {
108
- children: /*#__PURE__*/_jsxs("div", {
109
- className: "d-flex justify-content-between align-items-center w-100",
110
- children: [v.name, /*#__PURE__*/_jsx("div", {
111
- className: "d-flex align-items-center gap-1",
112
- children: /*#__PURE__*/_jsx(Button, {
113
- type: "button",
114
- className: "m-0 p-0 px-1",
115
- variant: "outline-secondary",
116
- title: "Add to list",
117
- onClick: () => {
118
- handleSelect(dispatch, {
119
- name: v.name,
120
- index: v.index,
121
- matrix_index: v.matrix_index
122
- });
123
- },
124
- children: /*#__PURE__*/_jsx(FontAwesomeIcon, {
125
- icon: faPlus
147
+ return /*#__PURE__*/_jsx("div", {
148
+ className: "virtualized-list-wrapper",
149
+ children: /*#__PURE__*/_jsx(ListGroup.Item, {
150
+ children: /*#__PURE__*/_jsxs("div", {
151
+ className: "d-flex justify-content-between align-items-center w-100",
152
+ children: [v.name, /*#__PURE__*/_jsx("div", {
153
+ className: "d-flex align-items-center gap-1",
154
+ children: /*#__PURE__*/_jsx(Button, {
155
+ type: "button",
156
+ className: "m-0 p-0 px-1",
157
+ variant: "outline-secondary",
158
+ title: "Add to list",
159
+ onClick: () => {
160
+ handleSelect(dispatch, {
161
+ name: v.name,
162
+ index: v.index,
163
+ matrix_index: v.matrix_index
164
+ });
165
+ },
166
+ children: /*#__PURE__*/_jsx(FontAwesomeIcon, {
167
+ icon: faPlus
168
+ })
126
169
  })
127
- })
128
- })]
129
- })
170
+ })]
171
+ })
172
+ }, v.gene_id)
130
173
  }, v.gene_id);
131
174
  });
132
175
  const isPending = diseaseData.isPending || varMeans.isPending && settings.varSort.disease.sort === VAR_SORT.MATRIX;
@@ -93,7 +93,7 @@ export function VarSearchResults(props) {
93
93
  searchHook: useVarSearch,
94
94
  emptyLabel: "Search features",
95
95
  itemRenderer: _ref2 => {
96
- var _item$index;
96
+ var _item$index, _item$index2;
97
97
  let {
98
98
  item,
99
99
  dispatch,
@@ -102,25 +102,28 @@ export function VarSearchResults(props) {
102
102
  setSelectedResult,
103
103
  isStale
104
104
  } = _ref2;
105
- return /*#__PURE__*/_jsx(ListGroup.Item, {
106
- onClick: () => setSelectedResult(item),
107
- active: (selectedResult === null || selectedResult === void 0 ? void 0 : selectedResult.index) === item.index,
108
- children: /*#__PURE__*/_jsxs("div", {
109
- className: "d-flex justify-content-between align-items-center w-100",
110
- children: [/*#__PURE__*/_jsx("div", {
111
- children: item.name
112
- }), /*#__PURE__*/_jsx(Button, {
113
- type: "button",
114
- className: "m-0 p-0 px-1",
115
- variant: "outline-secondary",
116
- title: "Add to list",
117
- disabled: isStale,
118
- onClick: () => handleSelect(dispatch, item),
119
- children: /*#__PURE__*/_jsx(FontAwesomeIcon, {
120
- icon: faPlus
121
- })
122
- })]
123
- })
105
+ return /*#__PURE__*/_jsx("div", {
106
+ className: "virtualized-list-wrapper",
107
+ children: /*#__PURE__*/_jsx(ListGroup.Item, {
108
+ onClick: () => setSelectedResult(item),
109
+ active: (selectedResult === null || selectedResult === void 0 ? void 0 : selectedResult.index) === item.index,
110
+ children: /*#__PURE__*/_jsxs("div", {
111
+ className: "d-flex justify-content-between align-items-center w-100",
112
+ children: [/*#__PURE__*/_jsx("div", {
113
+ children: item.name
114
+ }), /*#__PURE__*/_jsx(Button, {
115
+ type: "button",
116
+ className: "m-0 p-0 px-1",
117
+ variant: "outline-secondary",
118
+ title: "Add to list",
119
+ disabled: isStale,
120
+ onClick: () => handleSelect(dispatch, item),
121
+ children: /*#__PURE__*/_jsx(FontAwesomeIcon, {
122
+ icon: faPlus
123
+ })
124
+ })]
125
+ })
126
+ }, (_item$index2 = item.index) !== null && _item$index2 !== void 0 ? _item$index2 : item.name)
124
127
  }, (_item$index = item.index) !== null && _item$index !== void 0 ? _item$index : item.name);
125
128
  }
126
129
  }));
@@ -145,25 +148,28 @@ export function ObsSearchResults(props) {
145
148
  });
146
149
  if (closeModal) closeModal();
147
150
  };
148
- return /*#__PURE__*/_jsx(ListGroup.Item, {
149
- onClick: () => setSelectedResult(item),
150
- active: (selectedResult === null || selectedResult === void 0 ? void 0 : selectedResult.matrix_index) === item.matrix_index,
151
- children: /*#__PURE__*/_jsxs("div", {
152
- className: "d-flex justify-content-between align-items-center w-100",
153
- children: [/*#__PURE__*/_jsx("div", {
154
- children: item.name
155
- }), /*#__PURE__*/_jsx(Button, {
156
- type: "button",
157
- className: "m-0 p-0 px-1",
158
- variant: "outline-secondary",
159
- title: "Add to list",
160
- disabled: isStale,
161
- onClick: () => onObsSelect(dispatch, item, props.handleClose),
162
- children: /*#__PURE__*/_jsx(FontAwesomeIcon, {
163
- icon: faPlus
164
- })
165
- })]
166
- })
151
+ return /*#__PURE__*/_jsx("div", {
152
+ className: "virtualized-list-wrapper",
153
+ children: /*#__PURE__*/_jsx(ListGroup.Item, {
154
+ onClick: () => setSelectedResult(item),
155
+ active: (selectedResult === null || selectedResult === void 0 ? void 0 : selectedResult.matrix_index) === item.matrix_index,
156
+ children: /*#__PURE__*/_jsxs("div", {
157
+ className: "d-flex justify-content-between align-items-center w-100",
158
+ children: [/*#__PURE__*/_jsx("div", {
159
+ children: item.name
160
+ }), /*#__PURE__*/_jsx(Button, {
161
+ type: "button",
162
+ className: "m-0 p-0 px-1",
163
+ variant: "outline-secondary",
164
+ title: "Add to list",
165
+ disabled: isStale,
166
+ onClick: () => onObsSelect(dispatch, item, props.handleClose),
167
+ children: /*#__PURE__*/_jsx(FontAwesomeIcon, {
168
+ icon: faPlus
169
+ })
170
+ })]
171
+ })
172
+ }, item.matrix_index)
167
173
  }, item.matrix_index);
168
174
  }
169
175
  }));
@@ -175,21 +181,24 @@ export function DiseasesSearchResults(props) {
175
181
  overscan: 250,
176
182
  estimateSize: () => 32,
177
183
  itemRenderer: _ref4 => {
178
- var _item$id;
184
+ var _item$id, _item$id2;
179
185
  let {
180
186
  item,
181
187
  setSelectedResult,
182
188
  selectedResult
183
189
  } = _ref4;
184
- return /*#__PURE__*/_jsx(ListGroup.Item, {
185
- onClick: () => setSelectedResult(item),
186
- active: (selectedResult === null || selectedResult === void 0 ? void 0 : selectedResult.id) === item.id,
187
- children: /*#__PURE__*/_jsx("div", {
188
- className: "d-flex justify-content-between align-items-center w-100",
190
+ return /*#__PURE__*/_jsx("div", {
191
+ className: "virtualized-list-wrapper",
192
+ children: /*#__PURE__*/_jsx(ListGroup.Item, {
193
+ onClick: () => setSelectedResult(item),
194
+ active: (selectedResult === null || selectedResult === void 0 ? void 0 : selectedResult.id) === item.id,
189
195
  children: /*#__PURE__*/_jsx("div", {
190
- children: item.disease_name
196
+ className: "d-flex justify-content-between align-items-center w-100",
197
+ children: /*#__PURE__*/_jsx("div", {
198
+ children: item.disease_name
199
+ })
191
200
  })
192
- })
201
+ }, (_item$id2 = item.id) !== null && _item$id2 !== void 0 ? _item$id2 : item.name)
193
202
  }, (_item$id = item.id) !== null && _item$id !== void 0 ? _item$id : item.name);
194
203
  }
195
204
  }));
@@ -1,16 +1,19 @@
1
1
  import { faList, faSearch, faSliders } from '@fortawesome/free-solid-svg-icons';
2
2
  import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
3
- import { Container, Nav, Navbar } from 'react-bootstrap';
3
+ import { Container, Nav, Navbar, Button, ButtonGroup } from 'react-bootstrap';
4
+ import usePlotVisibility from '../../utils/usePlotVisibility';
4
5
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
5
6
  export const Toolbar = _ref => {
6
7
  let {
7
- showObsBtn = true,
8
- showVarsBtn = true,
9
- showCtrlsBtn = true,
10
- setShowObs,
11
- setShowVars,
12
- setShowControls
8
+ setShowCategories,
9
+ setShowSearch,
10
+ setShowControls,
11
+ Fullscreen
13
12
  } = _ref;
13
+ const {
14
+ showCategoriesBtn,
15
+ showSearchBtn
16
+ } = usePlotVisibility(isFullscreen);
14
17
  return /*#__PURE__*/_jsx(Navbar, {
15
18
  expand: "md",
16
19
  bg: "primary",
@@ -24,25 +27,25 @@ export const Toolbar = _ref => {
24
27
  id: "navbarScroll",
25
28
  children: /*#__PURE__*/_jsxs(Nav, {
26
29
  navbarScroll: true,
27
- children: [showObsBtn && /*#__PURE__*/_jsx(Nav.Item, {
30
+ children: [showCategoriesBtn && /*#__PURE__*/_jsx(Nav.Item, {
28
31
  className: "me-2",
29
32
  children: /*#__PURE__*/_jsxs(Nav.Link, {
30
- onClick: () => setShowObs(true),
33
+ onClick: () => setShowCategories(true),
31
34
  children: [/*#__PURE__*/_jsx(FontAwesomeIcon, {
32
35
  icon: faList,
33
36
  className: "me-2"
34
37
  }), "Explore Categories"]
35
38
  })
36
- }), showVarsBtn && /*#__PURE__*/_jsx(Nav.Item, {
39
+ }), showSearchBtn && /*#__PURE__*/_jsx(Nav.Item, {
37
40
  className: "me-2",
38
41
  children: /*#__PURE__*/_jsxs(Nav.Link, {
39
- onClick: () => setShowVars(true),
42
+ onClick: () => setShowSearch(true),
40
43
  children: [/*#__PURE__*/_jsx(FontAwesomeIcon, {
41
44
  icon: faSearch,
42
45
  className: "me-2"
43
46
  }), "Search Genes"]
44
47
  })
45
- }), showCtrlsBtn && /*#__PURE__*/_jsx(Nav.Item, {
48
+ }), /*#__PURE__*/_jsx(Nav.Item, {
46
49
  className: "me-2",
47
50
  children: /*#__PURE__*/_jsxs(Nav.Link, {
48
51
  onClick: () => setShowControls(true),
@@ -51,44 +54,46 @@ export const Toolbar = _ref => {
51
54
  className: "me-2"
52
55
  }), "Controls"]
53
56
  })
54
- })]
57
+ }), ' ']
55
58
  })
56
59
  })]
57
60
  })
58
61
  });
59
62
  };
60
- export const ObsPlotlyToolbar = _ref2 => {
63
+ export const PlotlyToolbar = _ref2 => {
61
64
  let {
62
- onClick
65
+ setShowCategories,
66
+ setShowSearch,
67
+ isFullscreen
63
68
  } = _ref2;
64
- return {
65
- name: 'Categories',
66
- icon: {
67
- width: 512,
68
- height: 512,
69
- path: faList.icon[4]
70
- },
71
- click: onClick
72
- };
69
+ const {
70
+ showCategoriesBtn,
71
+ showSearchBtn
72
+ } = usePlotVisibility(isFullscreen);
73
+ return /*#__PURE__*/_jsxs(ButtonGroup, {
74
+ children: [showCategoriesBtn && /*#__PURE__*/_jsxs(Button, {
75
+ variant: "primary",
76
+ onClick: () => setShowCategories(true),
77
+ title: "Explore Categories",
78
+ children: [/*#__PURE__*/_jsx(FontAwesomeIcon, {
79
+ icon: faList,
80
+ className: "me-1"
81
+ }), "Categories"]
82
+ }), showSearchBtn && /*#__PURE__*/_jsxs(Button, {
83
+ variant: "primary",
84
+ onClick: () => setShowSearch(true),
85
+ title: "Search Genes",
86
+ children: [/*#__PURE__*/_jsx(FontAwesomeIcon, {
87
+ icon: faSearch,
88
+ className: "me-1"
89
+ }), "Genes"]
90
+ })]
91
+ });
73
92
  };
74
- export const VarPlotlyToolbar = _ref3 => {
93
+ export const PlotlyModebarControls = _ref3 => {
75
94
  let {
76
95
  onClick
77
96
  } = _ref3;
78
- return {
79
- name: 'Features',
80
- icon: {
81
- width: 512,
82
- height: 512,
83
- path: faSearch.icon[4]
84
- },
85
- click: onClick
86
- };
87
- };
88
- export const ControlsPlotlyToolbar = _ref4 => {
89
- let {
90
- onClick
91
- } = _ref4;
92
97
  return {
93
98
  name: 'Controls',
94
99
  icon: {
@@ -96,21 +96,27 @@ export function VarNamesList(_ref) {
96
96
  }
97
97
  }, [settings.varSort.var.sort, settings.varSort.var.sortOrder, varMeans.isPending, varMeans.serverError, varMeans.fetchedData, settingsVars]);
98
98
  const makeListItem = item => {
99
- return /*#__PURE__*/_jsx(ListGroup.Item, {
100
- children: /*#__PURE__*/_jsx(VarItem, {
101
- item: item,
102
- active: active,
103
- mode: mode
104
- })
99
+ return /*#__PURE__*/_jsx("div", {
100
+ className: "virtualized-list-wrapper",
101
+ children: /*#__PURE__*/_jsx(ListGroup.Item, {
102
+ children: /*#__PURE__*/_jsx(VarItem, {
103
+ item: item,
104
+ active: active,
105
+ mode: mode
106
+ })
107
+ }, item.matrix_index)
105
108
  }, item.matrix_index);
106
109
  };
107
110
  const makeSetListItem = set => {
108
- return /*#__PURE__*/_jsx(ListGroup.Item, {
109
- children: /*#__PURE__*/_jsx(VarSet, {
110
- set: set,
111
- active: active,
112
- mode: mode
113
- })
111
+ return /*#__PURE__*/_jsx("div", {
112
+ className: "virtualized-list-wrapper",
113
+ children: /*#__PURE__*/_jsx(ListGroup.Item, {
114
+ children: /*#__PURE__*/_jsx(VarSet, {
115
+ set: set,
116
+ active: active,
117
+ mode: mode
118
+ })
119
+ }, set.name)
114
120
  }, set.name);
115
121
  };
116
122
  const varList = _.map(sortedVars, item => {
@@ -3,10 +3,10 @@ import { faCheck, faChevronDown, faChevronUp, faCircleInfo, faDroplet, faPlus, f
3
3
  import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
4
4
  import _ from 'lodash';
5
5
  import { Button, Collapse, ListGroup, OverlayTrigger, Tooltip } from 'react-bootstrap';
6
+ import { SelectionItem } from './VarItem';
6
7
  import { COLOR_ENCODINGS, SELECTION_MODES } from '../../constants/constants';
7
8
  import { useSettings, useSettingsDispatch } from '../../context/SettingsContext';
8
9
  import { SearchModal } from '../search-bar/SearchBar';
9
- import { SelectionItem } from './VarItem';
10
10
 
11
11
  // @TODO: add button to score genes and plot
12
12
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
@@ -17,20 +17,19 @@ import { LoadingSpinner } from '../../utils/LoadingIndicators';
17
17
  import { useDebouncedFetch } from '../../utils/requests';
18
18
  import { useSelectedMultiVar, useSelectedObs, useSelectedVar } from '../../utils/Resolver';
19
19
  import { StyledTooltip } from '../../utils/StyledTooltip';
20
- import { PlotAlert } from '../full-page/PlotAlert';
21
- import { ControlsPlotlyToolbar, ObsPlotlyToolbar, VarPlotlyToolbar } from '../toolbar/Toolbar';
20
+ import usePlotVisibility from '../../utils/usePlotVisibility';
21
+ import { PlotAlert } from '../plot/PlotAlert';
22
+ import { PlotlyToolbar, PlotlyModebarControls } from '../toolbar/Toolbar';
22
23
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
23
24
  export function Violin(_ref) {
24
25
  let {
25
26
  mode = VIOLIN_MODES.MULTIKEY,
26
- showObsBtn = false,
27
- showVarsBtn = false,
28
- showCtrlsBtn = false,
29
- setShowObs,
30
- setShowVars,
27
+ setShowCategories,
28
+ setShowSearch,
31
29
  setShowControls,
32
30
  plotType,
33
- setPlotType
31
+ setPlotType,
32
+ isFullscreen = false
34
33
  } = _ref;
35
34
  const ENDPOINT = 'violin';
36
35
  const dataset = useDataset();
@@ -45,6 +44,10 @@ export function Violin(_ref) {
45
44
  const selectedMultiVar = useSelectedMultiVar();
46
45
  const selectedVar = useSelectedVar();
47
46
  const selectedObs = useSelectedObs();
47
+ const {
48
+ showCategoriesBtn,
49
+ showSearchBtn
50
+ } = usePlotVisibility(isFullscreen);
48
51
  const params = useMemo(() => {
49
52
  var _selectedObs$omit;
50
53
  return _objectSpread({
@@ -104,19 +107,21 @@ export function Violin(_ref) {
104
107
  setLayout({});
105
108
  }
106
109
  }, [fetchedData, hasSelections, isPending, serverError]);
107
- const customModeBarButtons = _.compact([showObsBtn && ObsPlotlyToolbar({
108
- onClick: setShowObs
109
- }), showVarsBtn && VarPlotlyToolbar({
110
- onClick: setShowVars
111
- }), showCtrlsBtn && ControlsPlotlyToolbar({
110
+ const modeBarButtons = [_.compact([PlotlyModebarControls({
112
111
  onClick: setShowControls
113
- })]);
114
- const modeBarButtons = customModeBarButtons.length ? [customModeBarButtons, PLOTLY_MODEBAR_BUTTONS] : [PLOTLY_MODEBAR_BUTTONS];
112
+ }), ...PLOTLY_MODEBAR_BUTTONS])];
115
113
  if (!serverError) {
116
114
  if (hasSelections) {
117
115
  return /*#__PURE__*/_jsxs("div", {
118
116
  className: "cherita-plot cherita-violin",
119
- children: [isPending && /*#__PURE__*/_jsx(LoadingSpinner, {}), /*#__PURE__*/_jsxs("div", {
117
+ children: [/*#__PURE__*/_jsx("div", {
118
+ className: "plotly-toolbar",
119
+ children: /*#__PURE__*/_jsx(PlotlyToolbar, {
120
+ setShowCategories: setShowCategories,
121
+ setShowSearch: setShowSearch,
122
+ isFullscreen: isFullscreen
123
+ })
124
+ }), isPending && /*#__PURE__*/_jsx(LoadingSpinner, {}), /*#__PURE__*/_jsxs("div", {
120
125
  className: "d-flex flex-column h-100",
121
126
  children: [/*#__PURE__*/_jsx("div", {
122
127
  className: "flex-grow-1 position-relative",
@@ -167,23 +172,23 @@ export function Violin(_ref) {
167
172
  setPlotType: setPlotType,
168
173
  children: [mode === VIOLIN_MODES.MULTIKEY && /*#__PURE__*/_jsxs("p", {
169
174
  className: "p-0 m-0",
170
- children: ["Select one or more", ' ', showVarsBtn ? /*#__PURE__*/_jsx(Button, {
175
+ children: ["Select one or more", ' ', showSearchBtn ? /*#__PURE__*/_jsx(Button, {
171
176
  variant: "link",
172
177
  className: "border-0 p-0 align-baseline",
173
- onClick: setShowVars,
178
+ onClick: setShowSearch,
174
179
  children: "features"
175
180
  }) : 'features', ' ', "to display their expression distributions across all observations."]
176
181
  }), mode === VIOLIN_MODES.GROUPBY && /*#__PURE__*/_jsxs("p", {
177
182
  className: "p-0 m-0",
178
- children: ["Select a", ' ', showObsBtn ? /*#__PURE__*/_jsx(Button, {
183
+ children: ["Select a", ' ', showCategoriesBtn ? /*#__PURE__*/_jsx(Button, {
179
184
  variant: "link",
180
185
  className: "border-0 p-0 align-baseline",
181
- onClick: setShowObs,
186
+ onClick: setShowCategories,
182
187
  children: "category"
183
- }) : 'category', ' ', "to group observations, and choose a", ' ', showVarsBtn ? /*#__PURE__*/_jsx(Button, {
188
+ }) : 'category', ' ', "to group observations, and choose a", ' ', showSearchBtn ? /*#__PURE__*/_jsx(Button, {
184
189
  variant: "link",
185
190
  className: "border-0 p-0 align-baseline",
186
- onClick: setShowVars,
191
+ onClick: setShowSearch,
187
192
  children: "feature"
188
193
  }) : 'feature', ' ', "to view its distribution within each group."]
189
194
  })]
@@ -51,7 +51,7 @@ const persistOptions = {
51
51
  return false;
52
52
  }
53
53
  },
54
- buster: "1.5.0" || '0.0.0'
54
+ buster: "2.0.0" || '0.0.0'
55
55
  // @TODO: add maxAge and api version numbers as buster
56
56
  };
57
57
  const initialDataset = {
@@ -64,7 +64,15 @@ const initialDataset = {
64
64
  defaultSettings: {},
65
65
  canOverrideSettings: true,
66
66
  useUnsColors: false,
67
- isPseudospatial: false
67
+ isPseudospatial: false,
68
+ obsExplorer: {
69
+ obsCols: [],
70
+ symbolCol: null,
71
+ // obs col with gene symbols to query NCBI for additional info in ObsExplorer
72
+ dataUrl: null,
73
+ // for additional data in a remote .parquet file
74
+ dataFilterCols: null // map of obs cols to filter data in .parquet file
75
+ }
68
76
  };
69
77
  export function DatasetProvider(_ref2) {
70
78
  let {
@@ -16,7 +16,6 @@ export const SettingsDispatchContext = /*#__PURE__*/createContext(null);
16
16
  const initialSettings = {
17
17
  selectedObs: null,
18
18
  // { name: "obs_name", omit: ["obs_item"], bins: {} }
19
- selectedObsIndex: null,
20
19
  selectedVar: null,
21
20
  // { name: "var_name", isSet: false } or { name: "var_set_name", isSet: true, vars: [{ name: "var1" }, { name: "var2" }] }
22
21
  selectedObsm: null,
@@ -49,7 +48,8 @@ const initialSettings = {
49
48
  violinplot: VIOLINPLOT_SCALES.WIDTH.value
50
49
  },
51
50
  meanOnlyExpressed: false,
52
- expressionCutoff: 0.0
51
+ expressionCutoff: 0.0,
52
+ radiusScale: {}
53
53
  },
54
54
  varSort: {
55
55
  var: {
@@ -70,6 +70,8 @@ const initialSettings = {
70
70
  opacity: 1
71
71
  }
72
72
  },
73
+ // for obsExplorer
74
+ selectedObsIndex: null,
73
75
  // dataset resolved values
74
76
  data: {
75
77
  // store resolved obs and vars from selectedObs, selectedVar, selectedMultiVar, vars, labelObs
@@ -164,7 +166,7 @@ export function SettingsProvider(_ref2) {
164
166
 
165
167
  // If the buster is not set or does not match the current package version,
166
168
  // reset localSettings to avoid stale data
167
- if (!buster || buster !== "1.5.0") {
169
+ if (!buster || buster !== "2.0.0") {
168
170
  localSettings = {};
169
171
  }
170
172
  const initSettings = useRef(initializer({
@@ -189,7 +191,7 @@ export function SettingsProvider(_ref2) {
189
191
  if (canOverrideSettings && settings) {
190
192
  try {
191
193
  localStorage.setItem(DATASET_STORAGE_KEY, JSON.stringify(_objectSpread({
192
- buster: "1.5.0" || '0.0.0',
194
+ buster: "2.0.0" || '0.0.0',
193
195
  timestamp: Date.now()
194
196
  }, _.omit(settings, 'data'))));
195
197
  } catch (err) {
@@ -219,7 +221,9 @@ const OBS_DATA_KEYS = ['name', 'type',
219
221
  // categorical and numerical
220
222
  'codes', 'codesMap', 'values', 'n_values', 'value_counts',
221
223
  // numerical
222
- 'bins', 'min', 'max', 'mean', 'median', 'n_unique'];
224
+ 'bins', 'min', 'max', 'mean', 'median', 'n_unique',
225
+ // optional custom colors
226
+ 'colors'];
223
227
  const splitObs = obs => {
224
228
  if (!obs) return {
225
229
  settings: null,
@@ -590,6 +594,16 @@ function settingsReducer(settings, action) {
590
594
  })
591
595
  });
592
596
  }
597
+ case 'set.controls.radiusScale':
598
+ {
599
+ return _objectSpread(_objectSpread({}, settings), {}, {
600
+ controls: _objectSpread(_objectSpread({}, settings.controls), {}, {
601
+ radiusScale: _objectSpread(_objectSpread({}, settings.controls.radiusScale), {}, {
602
+ [action.obsm]: action.radiusScale
603
+ })
604
+ })
605
+ });
606
+ }
593
607
  case 'toggle.slice.obs':
594
608
  {
595
609
  if (settings.selectedObs && settings.selectedObs.name === action.obs.name) {