@haniffalab/cherita-react 1.0.0 → 1.1.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 (106) hide show
  1. package/LICENSE +1 -1
  2. package/dist/{components → cjs/components}/full-page/FullPage.js +32 -22
  3. package/dist/{components → cjs/components}/full-page/FullPagePseudospatial.js +7 -6
  4. package/dist/{components → cjs/components}/obs-list/ObsItem.js +88 -77
  5. package/dist/{components → cjs/components}/obs-list/ObsList.js +133 -50
  6. package/dist/cjs/components/obs-list/ObsToolbar.js +24 -0
  7. package/dist/{components → cjs/components}/obsm-list/ObsmList.js +8 -5
  8. package/dist/{components → cjs/components}/offcanvas/index.js +24 -20
  9. package/dist/{components → cjs/components}/pseudospatial/Pseudospatial.js +10 -9
  10. package/dist/{components → cjs/components}/pseudospatial/PseudospatialToolbar.js +4 -3
  11. package/dist/{components → cjs/components}/scatterplot/Scatterplot.js +33 -24
  12. package/dist/{components → cjs/components}/scatterplot/SpatialControls.js +43 -42
  13. package/dist/cjs/components/scatterplot/Toolbox.js +62 -0
  14. package/dist/{components → cjs/components}/search-bar/SearchBar.js +24 -7
  15. package/dist/{components → cjs/components}/search-bar/SearchResults.js +13 -17
  16. package/dist/{components → cjs/components}/var-list/VarItem.js +38 -29
  17. package/dist/{components → cjs/components}/var-list/VarList.js +59 -31
  18. package/dist/{components → cjs/components}/var-list/VarListToolbar.js +18 -14
  19. package/dist/{components → cjs/components}/var-list/VarSet.js +24 -20
  20. package/dist/{components → cjs/components}/violin/Violin.js +4 -3
  21. package/dist/{constants → cjs/constants}/constants.js +6 -2
  22. package/dist/{context → cjs/context}/DatasetContext.js +12 -11
  23. package/dist/{context → cjs/context}/FilterContext.js +4 -3
  24. package/dist/{context → cjs/context}/ZarrDataContext.js +4 -3
  25. package/dist/{helpers → cjs/helpers}/color-helper.js +12 -11
  26. package/dist/{helpers → cjs/helpers}/map-helper.js +8 -7
  27. package/dist/{helpers → cjs/helpers}/zarr-helper.js +30 -38
  28. package/dist/{utils → cjs/utils}/Filter.js +1 -1
  29. package/dist/{utils → cjs/utils}/Histogram.js +12 -8
  30. package/dist/{utils → cjs/utils}/ImageViewer.js +6 -5
  31. package/dist/{utils → cjs/utils}/Legend.js +8 -7
  32. package/dist/{utils → cjs/utils}/LoadingIndicators.js +5 -4
  33. package/dist/cjs/utils/Skeleton.js +19 -0
  34. package/dist/{utils → cjs/utils}/VirtualizedList.js +10 -9
  35. package/dist/{utils → cjs/utils}/requests.js +37 -39
  36. package/dist/{utils → cjs/utils}/string.js +9 -4
  37. package/dist/{utils → cjs/utils}/zarrData.js +12 -4
  38. package/dist/css/cherita.css +147 -152
  39. package/dist/css/cherita.css.map +1 -1
  40. package/dist/esm/components/dotplot/Dotplot.js +135 -0
  41. package/dist/esm/components/dotplot/DotplotControls.js +148 -0
  42. package/dist/esm/components/full-page/FullPage.js +143 -0
  43. package/dist/esm/components/full-page/FullPagePseudospatial.js +151 -0
  44. package/dist/esm/components/heatmap/Heatmap.js +105 -0
  45. package/dist/esm/components/heatmap/HeatmapControls.js +23 -0
  46. package/dist/esm/components/matrixplot/Matrixplot.js +107 -0
  47. package/dist/esm/components/matrixplot/MatrixplotControls.js +38 -0
  48. package/dist/esm/components/obs-list/ObsItem.js +484 -0
  49. package/dist/esm/components/obs-list/ObsList.js +338 -0
  50. package/dist/esm/components/obs-list/ObsToolbar.js +17 -0
  51. package/dist/esm/components/obsm-list/ObsmList.js +75 -0
  52. package/dist/esm/components/offcanvas/index.js +67 -0
  53. package/dist/esm/components/pseudospatial/Pseudospatial.js +228 -0
  54. package/dist/esm/components/pseudospatial/PseudospatialToolbar.js +123 -0
  55. package/dist/esm/components/scatterplot/Scatterplot.js +394 -0
  56. package/dist/esm/components/scatterplot/ScatterplotControls.js +71 -0
  57. package/dist/esm/components/scatterplot/SpatialControls.js +140 -0
  58. package/dist/esm/components/scatterplot/Toolbox.js +55 -0
  59. package/dist/esm/components/search-bar/SearchBar.js +90 -0
  60. package/dist/esm/components/search-bar/SearchResults.js +139 -0
  61. package/dist/esm/components/var-list/VarItem.js +254 -0
  62. package/dist/esm/components/var-list/VarList.js +291 -0
  63. package/dist/esm/components/var-list/VarListToolbar.js +87 -0
  64. package/dist/esm/components/var-list/VarSet.js +194 -0
  65. package/dist/esm/components/violin/Violin.js +141 -0
  66. package/dist/esm/components/violin/ViolinControls.js +24 -0
  67. package/dist/esm/constants/colorscales.js +22 -0
  68. package/dist/esm/constants/constants.js +88 -0
  69. package/dist/esm/context/DatasetContext.js +571 -0
  70. package/dist/esm/context/FilterContext.js +48 -0
  71. package/dist/esm/context/ZarrDataContext.js +26 -0
  72. package/dist/esm/helpers/color-helper.js +66 -0
  73. package/dist/esm/helpers/map-helper.js +53 -0
  74. package/dist/esm/helpers/zarr-helper.js +111 -0
  75. package/dist/esm/index.js +22 -0
  76. package/dist/esm/utils/Filter.js +147 -0
  77. package/dist/esm/utils/Histogram.js +44 -0
  78. package/dist/esm/utils/ImageViewer.js +27 -0
  79. package/dist/esm/utils/Legend.js +58 -0
  80. package/dist/esm/utils/LoadingIndicators.js +22 -0
  81. package/dist/esm/utils/Skeleton.js +12 -0
  82. package/dist/esm/utils/VirtualizedList.js +55 -0
  83. package/dist/esm/utils/errors.js +47 -0
  84. package/dist/esm/utils/requests.js +102 -0
  85. package/dist/esm/utils/search.js +39 -0
  86. package/dist/esm/utils/string.js +59 -0
  87. package/dist/esm/utils/zarrData.js +102 -0
  88. package/package.json +19 -7
  89. package/scss/cherita.scss +19 -50
  90. package/scss/components/accordions.scss +32 -0
  91. package/scss/components/layouts.scss +2 -1
  92. package/scss/components/lists.scss +14 -0
  93. package/dist/components/obs-list/ObsToolbar.js +0 -64
  94. package/dist/components/scatterplot/Toolbox.js +0 -31
  95. /package/dist/{components → cjs/components}/dotplot/Dotplot.js +0 -0
  96. /package/dist/{components → cjs/components}/dotplot/DotplotControls.js +0 -0
  97. /package/dist/{components → cjs/components}/heatmap/Heatmap.js +0 -0
  98. /package/dist/{components → cjs/components}/heatmap/HeatmapControls.js +0 -0
  99. /package/dist/{components → cjs/components}/matrixplot/Matrixplot.js +0 -0
  100. /package/dist/{components → cjs/components}/matrixplot/MatrixplotControls.js +0 -0
  101. /package/dist/{components → cjs/components}/scatterplot/ScatterplotControls.js +0 -0
  102. /package/dist/{components → cjs/components}/violin/ViolinControls.js +0 -0
  103. /package/dist/{constants → cjs/constants}/colorscales.js +0 -0
  104. /package/dist/{index.js → cjs/index.js} +0 -0
  105. /package/dist/{utils → cjs/utils}/errors.js +0 -0
  106. /package/dist/{utils → cjs/utils}/search.js +0 -0
@@ -0,0 +1,291 @@
1
+ import React, { useEffect, useState } from "react";
2
+ import { faTimes } from "@fortawesome/free-solid-svg-icons";
3
+ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
4
+ import _ from "lodash";
5
+ import { Alert, Button, ListGroup } from "react-bootstrap";
6
+ import ButtonGroup from "react-bootstrap/ButtonGroup";
7
+ import { SELECTION_MODES, VAR_SORT } from "../../constants/constants";
8
+ import { useDataset, useDatasetDispatch } from "../../context/DatasetContext";
9
+ import { LoadingSpinner } from "../../utils/LoadingIndicators";
10
+ import { useFetch } from "../../utils/requests";
11
+ import { VarItem } from "./VarItem";
12
+ import { VarListToolbar } from "./VarListToolbar";
13
+ import { VarSet } from "./VarSet";
14
+ const useVarMean = function (varKeys) {
15
+ let enabled = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
16
+ const ENDPOINT = "matrix/mean";
17
+ const dataset = useDataset();
18
+ const [params, setParams] = useState({
19
+ url: dataset.url,
20
+ varKeys: _.map(varKeys, v => v.isSet ? {
21
+ name: v.name,
22
+ indices: v.vars.map(v => v.index)
23
+ } : v.index),
24
+ // obsIndices:
25
+ varNamesCol: dataset.varNamesCol
26
+ });
27
+ useEffect(() => {
28
+ setParams(p => {
29
+ return {
30
+ ...p,
31
+ varKeys: _.map(varKeys, v => v.isSet ? {
32
+ name: v.name,
33
+ indices: v.vars.map(v => v.index)
34
+ } : v.index)
35
+ };
36
+ });
37
+ }, [varKeys]);
38
+ return useFetch(ENDPOINT, params, {
39
+ enabled: enabled,
40
+ refetchOnMount: false
41
+ });
42
+ };
43
+
44
+ // ensure nulls are lowest values
45
+ const sortMeans = (i, means) => {
46
+ return means[i.name] || _.min(_.values(means)) - 1;
47
+ };
48
+
49
+ // @TODO: display where disease data comes from
50
+ // add to disease dataset metadata
51
+ function DiseaseVarList(_ref) {
52
+ let {
53
+ makeListItem
54
+ } = _ref;
55
+ const ENDPOINT = "disease/genes";
56
+ const dataset = useDataset();
57
+ const dispatch = useDatasetDispatch();
58
+ const [diseaseVars, setDiseaseVars] = useState([]);
59
+ const [sortedDiseaseVars, setSortedDiseaseVars] = useState([]);
60
+ const [params, setParams] = useState({
61
+ url: dataset.url,
62
+ col: dataset.varNamesCol,
63
+ diseaseId: dataset.selectedDisease?.id,
64
+ diseaseDatasets: dataset.diseaseDatasets
65
+ });
66
+ useEffect(() => {
67
+ setParams(p => {
68
+ return {
69
+ ...p,
70
+ diseaseId: dataset.selectedDisease?.id
71
+ };
72
+ });
73
+ }, [dataset.selectedDisease]);
74
+ const diseaseData = useFetch(ENDPOINT, params, {
75
+ enabled: !!params.diseaseId,
76
+ refetchOnMount: false
77
+ });
78
+ useEffect(() => {
79
+ if (!diseaseData.isPending && !diseaseData.serverError) {
80
+ setDiseaseVars(diseaseData.fetchedData);
81
+ }
82
+ }, [diseaseData.fetchedData, diseaseData.isPending, diseaseData.serverError]);
83
+ const varMeans = useVarMean(diseaseVars, !!diseaseVars?.length && dataset.varSort.disease.sort === VAR_SORT.MATRIX);
84
+ useEffect(() => {
85
+ if (dataset.varSort.disease.sort === VAR_SORT.MATRIX) {
86
+ if (!varMeans.isPending && !varMeans.serverError) {
87
+ setSortedDiseaseVars(_.orderBy(diseaseVars, o => {
88
+ return sortMeans(o, varMeans.fetchedData);
89
+ }, dataset.varSort.disease.sortOrder));
90
+ }
91
+ } else if (dataset.varSort.disease.sort === VAR_SORT.NAME) {
92
+ setSortedDiseaseVars(_.orderBy(diseaseVars, "name", dataset.varSort.disease.sortOrder));
93
+ } else {
94
+ setSortedDiseaseVars(diseaseVars);
95
+ }
96
+ }, [dataset.varSort.disease.sort, dataset.varSort.disease.sortOrder, diseaseVars, varMeans.fetchedData, varMeans.isPending, varMeans.serverError]);
97
+ const diseaseVarList = _.map(sortedDiseaseVars, item => {
98
+ return makeListItem(item, true);
99
+ });
100
+ const isPending = diseaseData.isPending || varMeans.isPending && dataset.varSort.disease.sort === VAR_SORT.MATRIX;
101
+ return /*#__PURE__*/React.createElement(React.Fragment, null, dataset.selectedDisease && (!isPending && !diseaseVars?.length ? /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("div", {
102
+ className: "d-flex justify-content-between mt-3"
103
+ }, /*#__PURE__*/React.createElement("h5", null, "Disease genes")), /*#__PURE__*/React.createElement(Alert, {
104
+ variant: "light"
105
+ }, "No disease genes found.")) : /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("div", {
106
+ className: "d-flex justify-content-between my-2"
107
+ }, /*#__PURE__*/React.createElement("h5", null, "Disease genes"), /*#__PURE__*/React.createElement(ButtonGroup, {
108
+ "aria-label": "Feature options",
109
+ size: "sm"
110
+ }, /*#__PURE__*/React.createElement(Button, {
111
+ variant: "info",
112
+ onClick: () => {
113
+ dispatch({
114
+ type: "reset.disease"
115
+ });
116
+ }
117
+ }, /*#__PURE__*/React.createElement(FontAwesomeIcon, {
118
+ icon: faTimes,
119
+ className: "me-1"
120
+ }), "Clear"))), /*#__PURE__*/React.createElement("p", null, dataset.selectedDisease?.name), /*#__PURE__*/React.createElement(VarListToolbar, {
121
+ varType: "disease"
122
+ }), /*#__PURE__*/React.createElement("div", {
123
+ className: "position-relative"
124
+ }, isPending && /*#__PURE__*/React.createElement(LoadingSpinner, null), /*#__PURE__*/React.createElement(ListGroup, {
125
+ variant: "flush",
126
+ className: "cherita-list"
127
+ }, diseaseVarList)))));
128
+ }
129
+ export function VarNamesList(_ref2) {
130
+ let {
131
+ mode = SELECTION_MODES.SINGLE,
132
+ displayName = "genes",
133
+ showDiseaseVarList = true
134
+ } = _ref2;
135
+ const dataset = useDataset();
136
+ const dispatch = useDatasetDispatch();
137
+ const [varButtons, setVarButtons] = useState(mode === SELECTION_MODES.SINGLE ? dataset.selectedVar ? _.unionWith([dataset.selectedVar], dataset.varSets, _.isEqual) : [...dataset.varSets] : [...dataset.selectedMultiVar, ...dataset.varSets]);
138
+ const [active, setActive] = useState(mode === SELECTION_MODES.SINGLE ? dataset.selectedVar?.matrix_index || dataset.selectedVar?.name : dataset.selectedMultiVar.map(i => i.matrix_index || i.name));
139
+ const [sortedVarButtons, setSortedVarButtons] = useState([]);
140
+ useEffect(() => {
141
+ if (mode === SELECTION_MODES.SINGLE) {
142
+ setVarButtons(v => {
143
+ if (dataset.selectedVar) {
144
+ return _.unionWith(v, [dataset.selectedVar], _.isEqual);
145
+ } else {
146
+ return v;
147
+ }
148
+ });
149
+ setActive(dataset.selectedVar?.matrix_index || dataset.selectedVar?.name);
150
+ }
151
+ }, [mode, dataset.selectedVar]);
152
+ useEffect(() => {
153
+ if (mode === SELECTION_MODES.MULTIPLE) {
154
+ setVarButtons(v => {
155
+ if (dataset.selectedMultiVar.length) {
156
+ return _.unionWith(v, dataset.selectedMultiVar, _.isEqual);
157
+ } else {
158
+ return v;
159
+ }
160
+ });
161
+ setActive(dataset.selectedMultiVar.map(i => i.matrix_index || i.name));
162
+ }
163
+ }, [mode, dataset.selectedMultiVar]);
164
+ useEffect(() => {
165
+ setVarButtons(v => {
166
+ const updated = _.compact(_.map(v, i => {
167
+ if (i.isSet) {
168
+ return dataset.varSets.find(s => s.name === i.name);
169
+ } else return i;
170
+ }));
171
+ const newSets = _.difference(dataset.varSets, updated);
172
+ return [...updated, ...newSets];
173
+ });
174
+ if (mode === SELECTION_MODES.SINGLE) {
175
+ if (dataset.selectedVar?.isSet) {
176
+ const selectedSet = dataset.varSets.find(s => s.name === dataset.selectedVar.name);
177
+ dispatch({
178
+ type: "select.var",
179
+ var: selectedSet
180
+ });
181
+ }
182
+ } else {
183
+ dispatch({
184
+ type: "update.multivar",
185
+ vars: dataset.varSets
186
+ });
187
+ }
188
+ }, [mode, dataset.varSets, dataset.selectedVar?.isSet, dataset.selectedVar?.name, dispatch]);
189
+ const varMeans = useVarMean(varButtons, dataset.varSort.var.sort === VAR_SORT.MATRIX);
190
+
191
+ // @TODO: deferr sortedVarButtons ?
192
+ useEffect(() => {
193
+ if (dataset.varSort.var.sort === VAR_SORT.MATRIX) {
194
+ if (!varMeans.isPending && !varMeans.serverError && varMeans.fetchedData) {
195
+ setSortedVarButtons(_.orderBy(varButtons, o => {
196
+ return sortMeans(o, varMeans.fetchedData);
197
+ }, dataset.varSort.var.sortOrder));
198
+ }
199
+ } else if (dataset.varSort.var.sort === VAR_SORT.NAME) {
200
+ setSortedVarButtons(_.orderBy(varButtons, "name", dataset.varSort.var.sortOrder));
201
+ } else {
202
+ setSortedVarButtons(varButtons);
203
+ }
204
+ }, [dataset.varSort.var.sort, dataset.varSort.var.sortOrder, varButtons, varMeans.isPending, varMeans.serverError, varMeans.fetchedData]);
205
+ const makeListItem = function (item) {
206
+ let isDiseaseGene = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
207
+ return /*#__PURE__*/React.createElement(ListGroup.Item, {
208
+ key: item.matrix_index
209
+ }, /*#__PURE__*/React.createElement(VarItem, {
210
+ item: item,
211
+ active: active,
212
+ setVarButtons: setVarButtons,
213
+ mode: mode,
214
+ isDiseaseGene: isDiseaseGene
215
+ }));
216
+ };
217
+ const makeSetListItem = set => {
218
+ return /*#__PURE__*/React.createElement(ListGroup.Item, {
219
+ key: set.name
220
+ }, /*#__PURE__*/React.createElement(VarSet, {
221
+ set: set,
222
+ active: active,
223
+ mode: mode
224
+ }));
225
+ };
226
+ const varList = _.map(sortedVarButtons, item => {
227
+ if (item.isSet) {
228
+ return makeSetListItem(item);
229
+ } else {
230
+ return makeListItem(item);
231
+ }
232
+ });
233
+ const newSetName = () => {
234
+ let n = 1;
235
+ let setName = `Set ${n}`;
236
+ const setNameExists = name => {
237
+ return dataset.varSets.some(set => set.name === name);
238
+ };
239
+ while (setNameExists(setName)) {
240
+ n++;
241
+ setName = `Set ${n}`;
242
+ }
243
+ return setName;
244
+ };
245
+ const isPending = varMeans.isPending && dataset.varSort.var.sort === VAR_SORT.MATRIX;
246
+ return /*#__PURE__*/React.createElement("div", {
247
+ className: "position-relative"
248
+ }, /*#__PURE__*/React.createElement("div", {
249
+ className: "overflow-auto mt-3"
250
+ }, /*#__PURE__*/React.createElement("div", {
251
+ className: "d-flex justify-content-between mb-2"
252
+ }, /*#__PURE__*/React.createElement("h5", null, _.capitalize(displayName)), /*#__PURE__*/React.createElement(ButtonGroup, {
253
+ "aria-label": "Feature options",
254
+ size: "sm"
255
+ }, /*#__PURE__*/React.createElement(Button, {
256
+ variant: "info",
257
+ onClick: () => {
258
+ dispatch({
259
+ type: "add.varSet",
260
+ varSet: {
261
+ name: newSetName(),
262
+ vars: [],
263
+ isSet: true
264
+ }
265
+ });
266
+ }
267
+ }, "New set"), /*#__PURE__*/React.createElement(Button, {
268
+ variant: "info",
269
+ onClick: () => {
270
+ setVarButtons([]);
271
+ dispatch({
272
+ type: mode === SELECTION_MODES.SINGLE ? "reset.var" : "reset.multiVar"
273
+ });
274
+ dispatch({
275
+ type: "reset.varSets"
276
+ });
277
+ }
278
+ }, /*#__PURE__*/React.createElement(FontAwesomeIcon, {
279
+ icon: faTimes,
280
+ className: "me-1"
281
+ }), "Clear"))), /*#__PURE__*/React.createElement(React.Fragment, null, !varList.length ? /*#__PURE__*/React.createElement(Alert, {
282
+ variant: "light"
283
+ }, "Search for a feature.") : /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(VarListToolbar, null), /*#__PURE__*/React.createElement("div", {
284
+ className: "position-relative"
285
+ }, isPending && /*#__PURE__*/React.createElement(LoadingSpinner, null), /*#__PURE__*/React.createElement(ListGroup, {
286
+ variant: "flush",
287
+ className: "cherita-list"
288
+ }, varList)))), /*#__PURE__*/React.createElement(React.Fragment, null, showDiseaseVarList && /*#__PURE__*/React.createElement(DiseaseVarList, {
289
+ makeListItem: makeListItem
290
+ }))));
291
+ }
@@ -0,0 +1,87 @@
1
+ import React, { useState } from "react";
2
+ import { faArrowDown19, faArrowDownAZ, faArrowUp91, faArrowUpZA, faXmark } from "@fortawesome/free-solid-svg-icons";
3
+ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
4
+ import { ToggleButton, ToggleButtonGroup } from "react-bootstrap";
5
+ import { VAR_SORT, VAR_SORT_ORDER } from "../../constants/constants";
6
+ import { useDataset, useDatasetDispatch } from "../../context/DatasetContext";
7
+
8
+ // @TODO: set option for "var" and "disease"
9
+ export function VarListToolbar(_ref) {
10
+ let {
11
+ varType = "var"
12
+ } = _ref;
13
+ const dataset = useDataset();
14
+ const dispatch = useDatasetDispatch();
15
+ const [sort, setSort] = useState(dataset.varSort.var.sort);
16
+ const [nameSortOrder, setNameSortOrder] = useState(dataset.varSort.var.sortOrder);
17
+ const [matrixSortOrder, setMatrixSortOrder] = useState(dataset.varSort.var.sortOrder);
18
+ const handleSort = (sortValue, sortOrder, setSortOrder) => {
19
+ if (sort !== sortValue) {
20
+ setSort(sortValue);
21
+ dispatch({
22
+ type: "set.varSort",
23
+ var: varType,
24
+ sort: sortValue,
25
+ sortOrder: sortOrder
26
+ });
27
+ } else {
28
+ const newSortOrder = sortOrder === VAR_SORT_ORDER.ASC ? VAR_SORT_ORDER.DESC : VAR_SORT_ORDER.ASC;
29
+ setSortOrder(newSortOrder);
30
+ dispatch({
31
+ type: "set.varSort",
32
+ var: varType,
33
+ sort: sortValue,
34
+ sortOrder: newSortOrder
35
+ });
36
+ }
37
+ };
38
+ return /*#__PURE__*/React.createElement("div", {
39
+ className: "d-flex justify-content-end align-items-center mb-2"
40
+ }, /*#__PURE__*/React.createElement(ToggleButtonGroup, {
41
+ name: "sortfeatures",
42
+ "aria-label": "Sort features by",
43
+ size: "sm",
44
+ type: "radio"
45
+ }, /*#__PURE__*/React.createElement(ToggleButton, {
46
+ id: VAR_SORT.NAME,
47
+ value: VAR_SORT.NAME,
48
+ "aria-label": "alphabetical",
49
+ title: "Sort alphabetically",
50
+ selected: sort === VAR_SORT.NAME,
51
+ onChange: () => {
52
+ handleSort(VAR_SORT.NAME, nameSortOrder, setNameSortOrder);
53
+ }
54
+ }, nameSortOrder === VAR_SORT_ORDER.ASC ? /*#__PURE__*/React.createElement(FontAwesomeIcon, {
55
+ icon: faArrowDownAZ
56
+ }) : /*#__PURE__*/React.createElement(FontAwesomeIcon, {
57
+ icon: faArrowUpZA
58
+ })), /*#__PURE__*/React.createElement(ToggleButton, {
59
+ id: VAR_SORT.MATRIX,
60
+ value: VAR_SORT.MATRIX,
61
+ "aria-label": "matrix value",
62
+ title: "Sort by matrix value",
63
+ selected: sort === VAR_SORT.MATRIX,
64
+ onChange: () => {
65
+ handleSort(VAR_SORT.MATRIX, matrixSortOrder, setMatrixSortOrder);
66
+ }
67
+ }, matrixSortOrder === VAR_SORT_ORDER.ASC ? /*#__PURE__*/React.createElement(FontAwesomeIcon, {
68
+ icon: faArrowDown19
69
+ }) : /*#__PURE__*/React.createElement(FontAwesomeIcon, {
70
+ icon: faArrowUp91
71
+ })), /*#__PURE__*/React.createElement(ToggleButton, {
72
+ id: "none",
73
+ value: "none",
74
+ "aria-label": "none",
75
+ title: "No sorting",
76
+ onClick: () => {
77
+ setSort(VAR_SORT.NONE);
78
+ dispatch({
79
+ type: "set.varSort.sort",
80
+ var: varType,
81
+ sort: VAR_SORT.NONE
82
+ });
83
+ }
84
+ }, /*#__PURE__*/React.createElement(FontAwesomeIcon, {
85
+ icon: faXmark
86
+ }))));
87
+ }
@@ -0,0 +1,194 @@
1
+ import React, { useState } from "react";
2
+ import { faCircleInfo, faDroplet, faTrash } from "@fortawesome/free-solid-svg-icons";
3
+ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
4
+ import { List } from "@mui/icons-material";
5
+ import _ from "lodash";
6
+ import { Button, Collapse, ListGroup, OverlayTrigger, Tooltip } from "react-bootstrap";
7
+ import { COLOR_ENCODINGS, SELECTION_MODES } from "../../constants/constants";
8
+ import { useDataset, useDatasetDispatch } from "../../context/DatasetContext";
9
+ import { SearchBar } from "../search-bar/SearchBar";
10
+ import { SingleSelectionItem } from "./VarItem";
11
+
12
+ // @TODO: add button to score genes and plot
13
+
14
+ const addVarToSet = (dispatch, set, v) => {
15
+ dispatch({
16
+ type: "add.varSet.var",
17
+ varSet: set,
18
+ var: v
19
+ });
20
+ };
21
+ function SingleSelectionSet(_ref) {
22
+ let {
23
+ set,
24
+ isActive,
25
+ selectSet,
26
+ removeSet,
27
+ removeSetVar,
28
+ showSearchBar = true
29
+ } = _ref;
30
+ const [openSet, setOpenSet] = useState(false);
31
+ const varList = set.vars.length ? _.map(set.vars, v => {
32
+ return /*#__PURE__*/React.createElement(ListGroup.Item, {
33
+ key: v.name
34
+ }, /*#__PURE__*/React.createElement(SingleSelectionItem, {
35
+ item: v,
36
+ showSetColorEncoding: false,
37
+ removeVar: () => removeSetVar(v)
38
+ }));
39
+ }) : /*#__PURE__*/React.createElement(ListGroup.Item, null, /*#__PURE__*/React.createElement("div", {
40
+ className: "text-muted"
41
+ }, "No features in this set"));
42
+ return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("div", {
43
+ className: "d-flex justify-content-between cursor-pointer",
44
+ onClick: () => {
45
+ setOpenSet(o => !o);
46
+ }
47
+ }, /*#__PURE__*/React.createElement("div", {
48
+ className: "d-flex justify-content-between align-items-center w-100"
49
+ }, /*#__PURE__*/React.createElement("div", null, set.name), /*#__PURE__*/React.createElement("div", {
50
+ className: "d-flex align-items-center gap-1"
51
+ }, /*#__PURE__*/React.createElement(OverlayTrigger, {
52
+ placement: "top",
53
+ overlay: /*#__PURE__*/React.createElement(Tooltip, null, "This set represents the mean value of its features")
54
+ }, /*#__PURE__*/React.createElement(FontAwesomeIcon, {
55
+ icon: faCircleInfo
56
+ })), /*#__PURE__*/React.createElement(List, null), /*#__PURE__*/React.createElement(Button, {
57
+ type: "button",
58
+ key: set.name,
59
+ variant: isActive ? "primary" : "outline-primary",
60
+ className: "m-0 p-0 px-1",
61
+ onClick: e => {
62
+ e.stopPropagation();
63
+ selectSet();
64
+ },
65
+ disabled: !set.vars.length,
66
+ title: "Set as color encoding"
67
+ }, /*#__PURE__*/React.createElement(FontAwesomeIcon, {
68
+ icon: faDroplet
69
+ })), /*#__PURE__*/React.createElement(Button, {
70
+ type: "button",
71
+ className: "m-0 p-0 px-1",
72
+ variant: "outline-secondary",
73
+ title: "Remove from list",
74
+ onClick: e => {
75
+ e.stopPropagation();
76
+ removeSet();
77
+ }
78
+ }, /*#__PURE__*/React.createElement(FontAwesomeIcon, {
79
+ icon: faTrash
80
+ }))))), /*#__PURE__*/React.createElement(Collapse, {
81
+ in: openSet
82
+ }, /*#__PURE__*/React.createElement("div", {
83
+ className: "mt-2"
84
+ }, showSearchBar &&
85
+ /*#__PURE__*/
86
+ // @TODO: fix how results are displayed, should be placed on top of parent components
87
+ React.createElement(SearchBar, {
88
+ handleSelect: (d, i) => addVarToSet(d, set, i)
89
+ }), /*#__PURE__*/React.createElement("div", {
90
+ className: "mx-2"
91
+ }, /*#__PURE__*/React.createElement(ListGroup, {
92
+ variant: "flush",
93
+ className: "cherita-list"
94
+ }, varList)))));
95
+ }
96
+ function MultipleSelectionSet(_ref2) {
97
+ let {
98
+ set,
99
+ isActive,
100
+ toggleSet
101
+ } = _ref2;
102
+ return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("div", {
103
+ className: "d-flex"
104
+ }, /*#__PURE__*/React.createElement("div", {
105
+ className: "flex-grow-1"
106
+ }, /*#__PURE__*/React.createElement(Button, {
107
+ type: "button",
108
+ key: set.name,
109
+ variant: isActive ? "primary" : "outline-primary",
110
+ className: "m-0 p-0 px-1",
111
+ onClick: toggleSet,
112
+ title: set.name
113
+ }, set.name))));
114
+ }
115
+ export function VarSet(_ref3) {
116
+ let {
117
+ set,
118
+ active,
119
+ mode = SELECTION_MODES.SINGLE
120
+ } = _ref3;
121
+ const dataset = useDataset();
122
+ const dispatch = useDatasetDispatch();
123
+ const selectSet = () => {
124
+ if (mode === SELECTION_MODES.SINGLE) {
125
+ dispatch({
126
+ type: "select.var",
127
+ var: set
128
+ });
129
+ dispatch({
130
+ type: "set.colorEncoding",
131
+ value: "var"
132
+ });
133
+ } else if (mode === SELECTION_MODES.MULTIPLE) {
134
+ dispatch({
135
+ type: "select.multivar",
136
+ var: set
137
+ });
138
+ }
139
+ };
140
+ const removeSet = () => {
141
+ if (mode === SELECTION_MODES.SINGLE) {
142
+ if (active === set.name) {
143
+ dispatch({
144
+ type: "reset.var"
145
+ });
146
+ }
147
+ } else if (mode === SELECTION_MODES.MULTIPLE) {
148
+ if (active.includes(set.name)) {
149
+ dispatch({
150
+ type: "deselect.multivar",
151
+ var: set
152
+ });
153
+ }
154
+ }
155
+ dispatch({
156
+ type: "remove.varSet",
157
+ varSet: set
158
+ });
159
+ };
160
+ const removeSetVar = v => {
161
+ dispatch({
162
+ type: "remove.varSet.var",
163
+ varSet: set,
164
+ var: v
165
+ });
166
+ };
167
+ const toggleSet = () => {
168
+ if (active.includes(set.name)) {
169
+ dispatch({
170
+ type: "deselect.multivar",
171
+ var: set
172
+ });
173
+ } else {
174
+ selectSet();
175
+ }
176
+ };
177
+ if (set && mode === SELECTION_MODES.SINGLE) {
178
+ return /*#__PURE__*/React.createElement(SingleSelectionSet, {
179
+ set: set,
180
+ isActive: dataset.colorEncoding === COLOR_ENCODINGS.VAR && active === set.name,
181
+ selectSet: selectSet,
182
+ removeSet: removeSet,
183
+ removeSetVar: v => removeSetVar(v)
184
+ });
185
+ } else if (mode === SELECTION_MODES.MULTIPLE) {
186
+ return /*#__PURE__*/React.createElement(MultipleSelectionSet, {
187
+ set: set,
188
+ isActive: _.includes(active, set.name),
189
+ toggleSet: () => toggleSet(set)
190
+ });
191
+ } else {
192
+ return null;
193
+ }
194
+ }