@haniffalab/cherita-react 1.2.0-dev.2025-04-30.0a204a1c → 1.2.0-dev.2025-05-16.4367ee63

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.
@@ -1,8 +1,15 @@
1
- import React, { useEffect, useRef, useState } from "react";
1
+ import React, { useState } from "react";
2
+ import CloseIcon from "@mui/icons-material/Close";
2
3
  import SearchIcon from "@mui/icons-material/Search";
3
- import _ from "lodash";
4
- import { Dropdown, Form, FormGroup, InputGroup } from "react-bootstrap";
4
+ import { Button, Form, FormGroup, InputGroup, Modal } from "react-bootstrap";
5
+ import Col from "react-bootstrap/Col";
6
+ import Container from "react-bootstrap/Container";
7
+ import Nav from "react-bootstrap/Nav";
8
+ import Row from "react-bootstrap/Row";
9
+ import Tab from "react-bootstrap/Tab";
10
+ import { DiseaseInfo, VarInfo } from "./SearchInfo";
5
11
  import { DiseasesSearchResults, VarSearchResults } from "./SearchResults";
12
+ import { COLOR_ENCODINGS } from "../../constants/constants";
6
13
  function onVarSelect(dispatch, item) {
7
14
  dispatch({
8
15
  type: "select.var",
@@ -14,77 +21,164 @@ function onVarSelect(dispatch, item) {
14
21
  });
15
22
  dispatch({
16
23
  type: "set.colorEncoding",
17
- value: "var"
24
+ value: COLOR_ENCODINGS.VAR
18
25
  });
19
26
  }
20
- export function SearchBar(_ref) {
27
+ function addVarSet(dispatch, _ref) {
21
28
  let {
22
- searchVar = true,
23
- searchDiseases = false,
24
- handleSelect = onVarSelect
29
+ name,
30
+ vars
25
31
  } = _ref;
26
- const [showSuggestions, setShowSuggestions] = useState(false);
27
- const [text, setText] = useState("");
28
- const inputRef = useRef(null);
29
- const displayText = [...(searchVar ? ["features"] : []), ...(searchDiseases ? ["diseases"] : [])].join(" and ");
30
- useEffect(() => {
31
- if (text.length > 0) {
32
- setShowSuggestions(true);
32
+ dispatch({
33
+ type: "add.varSet",
34
+ varSet: {
35
+ name: name,
36
+ vars: vars,
37
+ isSet: true
33
38
  }
34
- }, [text]);
35
-
36
- //@TODO: Abstract styles
37
- return /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement(Form, {
39
+ });
40
+ }
41
+ const FEATURE_TYPE = {
42
+ VAR: "var",
43
+ DISEASE: "disease"
44
+ };
45
+ export function SearchModal(_ref2) {
46
+ let {
47
+ show,
48
+ handleClose,
49
+ text,
50
+ setText,
51
+ displayText,
52
+ handleSelect = onVarSelect,
53
+ searchVar,
54
+ searchDiseases
55
+ } = _ref2;
56
+ const [tab, setTab] = useState("var");
57
+ const [selectedResult, setSelectedResult] = useState({
58
+ var: null,
59
+ disease: null
60
+ });
61
+ const [varResultsLength, setVarResultsLength] = useState(null);
62
+ const [diseaseResultsLength, setDiseaseResultsLength] = useState(null);
63
+ return /*#__PURE__*/React.createElement(Modal, {
64
+ show: show,
65
+ onHide: handleClose,
66
+ size: "xl"
67
+ }, /*#__PURE__*/React.createElement(Modal.Header, {
68
+ className: "bg-primary"
69
+ }, /*#__PURE__*/React.createElement(Container, {
70
+ className: "gx-0"
71
+ }, /*#__PURE__*/React.createElement(Row, null, /*#__PURE__*/React.createElement(Col, {
72
+ xs: 12
73
+ }, /*#__PURE__*/React.createElement(Form, {
38
74
  onSubmit: e => {
39
75
  e.preventDefault();
40
76
  }
41
- }, /*#__PURE__*/React.createElement(FormGroup, null, /*#__PURE__*/React.createElement(InputGroup, null, /*#__PURE__*/React.createElement(Form.Control, {
42
- ref: inputRef,
43
- onFocus: () => {
44
- setShowSuggestions(text.length > 0);
45
- },
46
- onBlur: () => {
47
- _.delay(() => {
48
- setShowSuggestions(false);
49
- }, 150);
50
- },
77
+ }, /*#__PURE__*/React.createElement(FormGroup, null, /*#__PURE__*/React.createElement("div", {
78
+ className: "d-flex align-items-center"
79
+ }, /*#__PURE__*/React.createElement(InputGroup, null, /*#__PURE__*/React.createElement(InputGroup.Text, null, /*#__PURE__*/React.createElement(SearchIcon, null)), /*#__PURE__*/React.createElement(Form.Control, {
80
+ autoFocus: true,
51
81
  type: "text",
52
82
  placeholder: "Search " + displayText,
53
83
  value: text,
54
84
  onChange: e => {
55
85
  setText(e.target.value);
56
- },
57
- style: {
58
- paddingLeft: "2.5rem",
59
- borderRadius: "5px"
86
+ setSelectedResult({
87
+ var: null,
88
+ disease: null
89
+ });
90
+ setVarResultsLength(null);
91
+ setDiseaseResultsLength(null);
60
92
  }
61
- }), /*#__PURE__*/React.createElement("div", {
62
- style: {
63
- position: "absolute",
64
- left: "10px",
65
- top: "50%",
66
- transform: "translateY(-50%)",
67
- pointerEvents: "none",
68
- zIndex: 10
69
- }
70
- }, /*#__PURE__*/React.createElement(SearchIcon, null))), /*#__PURE__*/React.createElement(Dropdown, {
71
- show: showSuggestions,
72
- onMouseDown: e => {
93
+ }), /*#__PURE__*/React.createElement(Button, {
94
+ variant: "light",
95
+ onClick: handleClose
96
+ }, /*#__PURE__*/React.createElement(CloseIcon, null)))))))))), /*#__PURE__*/React.createElement(Modal.Body, {
97
+ className: "p-0"
98
+ }, /*#__PURE__*/React.createElement(Container, null, /*#__PURE__*/React.createElement(Row, null, /*#__PURE__*/React.createElement(Col, {
99
+ xs: 12,
100
+ md: 8
101
+ }, /*#__PURE__*/React.createElement(Tab.Container, {
102
+ activeKey: tab,
103
+ onSelect: k => setTab(k)
104
+ }, /*#__PURE__*/React.createElement(Row, {
105
+ className: "w-100"
106
+ }, /*#__PURE__*/React.createElement(Col, {
107
+ sm: 3,
108
+ className: "py-3 border-end"
109
+ }, /*#__PURE__*/React.createElement(Nav, {
110
+ variant: "pills",
111
+ className: "flex-column"
112
+ }, searchVar && /*#__PURE__*/React.createElement(Nav.Item, null, /*#__PURE__*/React.createElement(Nav.Link, {
113
+ eventKey: FEATURE_TYPE.VAR
114
+ }, "Genes", " ", !!varResultsLength && `(${varResultsLength})`)), searchDiseases && /*#__PURE__*/React.createElement(Nav.Item, null, /*#__PURE__*/React.createElement(Nav.Link, {
115
+ eventKey: FEATURE_TYPE.DISEASE
116
+ }, "Diseases", " ", !!diseaseResultsLength && `(${diseaseResultsLength})`)))), /*#__PURE__*/React.createElement(Col, {
117
+ sm: 9,
118
+ className: "py-3"
119
+ }, /*#__PURE__*/React.createElement(Tab.Content, null, searchVar && /*#__PURE__*/React.createElement(Tab.Pane, {
120
+ eventKey: FEATURE_TYPE.VAR
121
+ }, /*#__PURE__*/React.createElement(VarSearchResults, {
122
+ text: text,
123
+ handleSelect: handleSelect,
124
+ selectedResult: selectedResult.var,
125
+ setSelectedResult: item => setSelectedResult(prev => {
126
+ return {
127
+ ...prev,
128
+ var: item
129
+ };
130
+ }),
131
+ setResultsLength: setVarResultsLength
132
+ })), searchDiseases && /*#__PURE__*/React.createElement(Tab.Pane, {
133
+ eventKey: FEATURE_TYPE.DISEASE
134
+ }, /*#__PURE__*/React.createElement(DiseasesSearchResults, {
135
+ text: text,
136
+ selectedResult: selectedResult.disease,
137
+ setSelectedResult: item => setSelectedResult(prev => {
138
+ return {
139
+ ...prev,
140
+ disease: item
141
+ };
142
+ }),
143
+ setResultsLength: setDiseaseResultsLength
144
+ }))))))), /*#__PURE__*/React.createElement(Col, {
145
+ xs: 12,
146
+ md: 4,
147
+ className: "bg-light p-3 search-modal-info"
148
+ }, selectedResult[tab] ? tab === FEATURE_TYPE.DISEASE ? /*#__PURE__*/React.createElement(DiseaseInfo, {
149
+ disease: selectedResult.disease,
150
+ handleSelect: handleSelect,
151
+ addVarSet: addVarSet
152
+ }) : /*#__PURE__*/React.createElement(VarInfo, {
153
+ varItem: selectedResult.var
154
+ }) : /*#__PURE__*/React.createElement("div", {
155
+ className: "text-muted"
156
+ }, "No result selected"))))));
157
+ }
158
+ export function SearchBar(_ref3) {
159
+ let {
160
+ searchVar = true,
161
+ searchDiseases = false
162
+ } = _ref3;
163
+ const [text, setText] = useState("");
164
+ const displayText = [...(searchVar ? ["features"] : []), ...(searchDiseases ? ["diseases"] : [])].join(" and ");
165
+ const [showModal, setShowModal] = useState(false);
166
+ return /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement(Form, {
167
+ onSubmit: e => {
73
168
  e.preventDefault();
74
- },
75
- onSelect: () => {
76
- inputRef.current.blur();
77
- }
78
- }, /*#__PURE__*/React.createElement(Dropdown.Menu, {
79
- style: {
80
- width: "90%"
81
169
  }
82
- }, searchVar && /*#__PURE__*/React.createElement(VarSearchResults, {
83
- text: text,
84
- setShowSuggestions: setShowSuggestions,
85
- handleSelect: handleSelect
86
- }), searchVar && searchDiseases && /*#__PURE__*/React.createElement(Dropdown.Divider, null), searchDiseases && /*#__PURE__*/React.createElement(DiseasesSearchResults, {
170
+ }, /*#__PURE__*/React.createElement(FormGroup, null, /*#__PURE__*/React.createElement(InputGroup, null, /*#__PURE__*/React.createElement(InputGroup.Text, null, /*#__PURE__*/React.createElement(SearchIcon, null)), /*#__PURE__*/React.createElement(Form.Control, {
171
+ onClick: () => setShowModal(true),
172
+ type: "text",
173
+ placeholder: "Search " + displayText,
174
+ defaultValue: text
175
+ })))), /*#__PURE__*/React.createElement(SearchModal, {
176
+ show: showModal,
87
177
  text: text,
88
- setShowSuggestions: setShowSuggestions
89
- }))))));
178
+ setText: setText,
179
+ displayText: displayText,
180
+ searchVar: searchVar,
181
+ searchDiseases: searchDiseases,
182
+ handleClose: () => setShowModal(false)
183
+ }));
90
184
  }
@@ -0,0 +1,165 @@
1
+ import React, { useState, useEffect } from "react";
2
+ import { faPlus } from "@fortawesome/free-solid-svg-icons";
3
+ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
4
+ import _ from "lodash";
5
+ import { Button, ListGroup } from "react-bootstrap";
6
+ import { VAR_SORT } from "../../constants/constants";
7
+ import { useDataset, useDatasetDispatch } from "../../context/DatasetContext";
8
+ import { useFetch } from "../../utils/requests";
9
+ import { VarDiseaseInfo } from "../var-list/VarItem";
10
+ export function VarInfo(_ref) {
11
+ let {
12
+ varItem
13
+ } = _ref;
14
+ const ENDPOINT = "disease/gene";
15
+ const dataset = useDataset();
16
+ const [params, setParams] = useState({
17
+ geneName: varItem.name,
18
+ diseaseDatasets: dataset.diseaseDatasets
19
+ });
20
+ useEffect(() => {
21
+ setParams(p => {
22
+ return {
23
+ ...p,
24
+ geneName: varItem.name
25
+ };
26
+ });
27
+ }, [varItem.name]);
28
+ const {
29
+ fetchedData,
30
+ isPending,
31
+ serverError
32
+ } = useFetch(ENDPOINT, params, {
33
+ refetchOnMount: false,
34
+ enabled: !!dataset.diseaseDatasets.length
35
+ });
36
+ const hasDiseaseInfo = !isPending && !serverError && !!fetchedData?.length;
37
+ return /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("h5", null, varItem.name), !!dataset.diseaseDatasets.length && isPending && /*#__PURE__*/React.createElement("p", null, "Loading..."), hasDiseaseInfo && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("h6", null, "Associated diseases"), /*#__PURE__*/React.createElement(VarDiseaseInfo, {
38
+ data: fetchedData
39
+ })));
40
+ }
41
+ const useVarMean = function (varKeys) {
42
+ let enabled = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
43
+ const ENDPOINT = "matrix/mean";
44
+ const dataset = useDataset();
45
+ const [params, setParams] = useState({
46
+ url: dataset.url,
47
+ varKeys: _.map(varKeys, v => v.isSet ? {
48
+ name: v.name,
49
+ indices: v.vars.map(v => v.index)
50
+ } : v.index),
51
+ // obsIndices:
52
+ varNamesCol: dataset.varNamesCol
53
+ });
54
+ useEffect(() => {
55
+ setParams(p => {
56
+ return {
57
+ ...p,
58
+ varKeys: _.map(varKeys, v => v.isSet ? {
59
+ name: v.name,
60
+ indices: v.vars.map(v => v.index)
61
+ } : v.index)
62
+ };
63
+ });
64
+ }, [varKeys]);
65
+ return useFetch(ENDPOINT, params, {
66
+ enabled: enabled,
67
+ refetchOnMount: false
68
+ });
69
+ };
70
+
71
+ // ensure nulls are lowest values
72
+ const sortMeans = (i, means) => {
73
+ return means[i.name] || _.min(_.values(means)) - 1;
74
+ };
75
+ export function DiseaseInfo(_ref2) {
76
+ let {
77
+ disease,
78
+ handleSelect,
79
+ addVarSet
80
+ } = _ref2;
81
+ const ENDPOINT = "disease/genes";
82
+ const dataset = useDataset();
83
+ const dispatch = useDatasetDispatch();
84
+ const [diseaseVars, setDiseaseVars] = useState([]);
85
+ const [sortedDiseaseVars, setSortedDiseaseVars] = useState([]);
86
+ const [params, setParams] = useState({
87
+ url: dataset.url,
88
+ col: dataset.varNamesCol,
89
+ diseaseDatasets: dataset.diseaseDatasets,
90
+ diseaseId: disease.id
91
+ });
92
+ useEffect(() => {
93
+ setParams(p => {
94
+ return {
95
+ ...p,
96
+ diseaseId: disease.id
97
+ };
98
+ });
99
+ }, [disease]);
100
+ const diseaseData = useFetch(ENDPOINT, params, {
101
+ enabled: !!params.diseaseId,
102
+ refetchOnMount: true
103
+ });
104
+ useEffect(() => {
105
+ if (!diseaseData.isPending && !diseaseData.serverError) {
106
+ setDiseaseVars(diseaseData.fetchedData);
107
+ }
108
+ }, [diseaseData.fetchedData, diseaseData.isPending, diseaseData.serverError]);
109
+ const varMeans = useVarMean(diseaseVars, !!diseaseVars?.length && dataset.varSort.disease.sort === VAR_SORT.MATRIX);
110
+ useEffect(() => {
111
+ if (dataset.varSort.disease.sort === VAR_SORT.MATRIX) {
112
+ if (!varMeans.isPending && !varMeans.serverError) {
113
+ setSortedDiseaseVars(_.orderBy(diseaseVars, o => {
114
+ return sortMeans(o, varMeans.fetchedData);
115
+ }, dataset.varSort.disease.sortOrder));
116
+ }
117
+ } else if (dataset.varSort.disease.sort === VAR_SORT.NAME) {
118
+ setSortedDiseaseVars(_.orderBy(diseaseVars, "name", dataset.varSort.disease.sortOrder));
119
+ } else {
120
+ setSortedDiseaseVars(diseaseVars);
121
+ }
122
+ }, [dataset.varSort.disease.sort, dataset.varSort.disease.sortOrder, diseaseVars, varMeans.fetchedData, varMeans.isPending, varMeans.serverError]);
123
+ const diseaseVarList = _.map(sortedDiseaseVars, v => {
124
+ return /*#__PURE__*/React.createElement(ListGroup.Item, {
125
+ key: v.gene_id
126
+ }, /*#__PURE__*/React.createElement("div", {
127
+ className: "d-flex justify-content-between align-items-center w-100"
128
+ }, v.name, /*#__PURE__*/React.createElement("div", {
129
+ className: "d-flex align-items-center gap-1"
130
+ }, /*#__PURE__*/React.createElement(Button, {
131
+ type: "button",
132
+ className: "m-0 p-0 px-1",
133
+ variant: "outline-secondary",
134
+ title: "Add to list",
135
+ onClick: () => {
136
+ handleSelect(dispatch, v);
137
+ }
138
+ }, /*#__PURE__*/React.createElement(FontAwesomeIcon, {
139
+ icon: faPlus
140
+ })))));
141
+ });
142
+ const isPending = diseaseData.isPending || varMeans.isPending && dataset.varSort.disease.sort === VAR_SORT.MATRIX;
143
+ return /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("h5", null, disease.disease_name), /*#__PURE__*/React.createElement("h6", null, "Implicated genes"), isPending ? /*#__PURE__*/React.createElement("p", null, "Loading...") : /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("div", {
144
+ className: "d-flex justify-content-end mb-2"
145
+ }, /*#__PURE__*/React.createElement(Button, {
146
+ size: "sm",
147
+ title: "Add all as a set",
148
+ onClick: () => {
149
+ addVarSet(dispatch, {
150
+ name: disease.disease_name,
151
+ vars: _.map(diseaseVars, v => {
152
+ return {
153
+ index: v.index,
154
+ name: v.name,
155
+ matrix_index: v.matrix_index
156
+ };
157
+ })
158
+ });
159
+ }
160
+ }, /*#__PURE__*/React.createElement(FontAwesomeIcon, {
161
+ icon: faPlus
162
+ }), " Add all as a set")), /*#__PURE__*/React.createElement(ListGroup, {
163
+ className: "overflow-scroll"
164
+ }, diseaseVarList)));
165
+ }
@@ -1,14 +1,18 @@
1
- import React, { useState, useEffect, useMemo, useDeferredValue } from "react";
1
+ import React, { useDeferredValue, useEffect, useMemo, useState } from "react";
2
+ import { faPlus } from "@fortawesome/free-solid-svg-icons";
3
+ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
2
4
  import _ from "lodash";
3
- import { Dropdown } from "react-bootstrap";
5
+ import { Button, ListGroup } from "react-bootstrap";
4
6
  import { useDatasetDispatch } from "../../context/DatasetContext";
5
7
  import { useDiseaseSearch, useVarSearch } from "../../utils/search";
6
8
  import { VirtualizedList } from "../../utils/VirtualizedList";
7
9
  export function VarSearchResults(_ref) {
8
10
  let {
9
11
  text,
10
- setShowSuggestions,
11
- handleSelect
12
+ handleSelect,
13
+ selectedResult,
14
+ setSelectedResult,
15
+ setResultsLength
12
16
  } = _ref;
13
17
  const [suggestions, setSuggestions] = useState([]);
14
18
  const dispatch = useDatasetDispatch();
@@ -33,51 +37,68 @@ export function VarSearchResults(_ref) {
33
37
  });
34
38
  } else {
35
39
  setSuggestions([]);
36
- setShowSuggestions(false);
37
40
  }
38
41
  };
39
42
  return _.debounce(setData, 300);
40
- }, [setParams, setShowSuggestions]);
43
+ }, [setParams]);
41
44
  useEffect(() => {
42
45
  updateParams(text);
43
46
  }, [text, updateParams]);
44
47
  useEffect(() => {
45
48
  if (!isPending && !serverError) {
46
49
  setSuggestions(fetchedData);
47
- setShowSuggestions(true);
50
+ setResultsLength(fetchedData?.length);
48
51
  }
49
- }, [fetchedData, isPending, serverError, setShowSuggestions]);
52
+ }, [fetchedData, isPending, serverError, setResultsLength]);
50
53
  const getDataAtIndex = index => deferredData[index];
51
- const ItemComponent = item => /*#__PURE__*/React.createElement(Dropdown.Item, {
52
- key: item.name,
53
- as: "button",
54
+ const ItemComponent = item => /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("div", {
55
+ className: "virtualized-list-wrapper"
56
+ }, /*#__PURE__*/React.createElement(ListGroup.Item, {
57
+ key: item,
58
+ onClick: () => {
59
+ setSelectedResult(item);
60
+ },
61
+ active: selectedResult?.index === item.index
62
+ }, /*#__PURE__*/React.createElement("div", {
63
+ className: "d-flex justify-content-between align-items-center w-100"
64
+ }, /*#__PURE__*/React.createElement("div", null, item.name), /*#__PURE__*/React.createElement("div", {
65
+ className: "d-flex align-items-center gap-1"
66
+ }, /*#__PURE__*/React.createElement(Button, {
67
+ type: "button",
68
+ className: "m-0 p-0 px-1",
69
+ variant: "outline-secondary",
70
+ title: "Add to list",
54
71
  disabled: isStale,
55
72
  onClick: () => {
56
73
  handleSelect(dispatch, item);
57
- _.delay(() => {
58
- setShowSuggestions(false);
59
- }, 150);
60
74
  }
61
- }, item.name);
62
- return /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement(Dropdown.Header, null, "Features"), /*#__PURE__*/React.createElement("div", {
75
+ }, /*#__PURE__*/React.createElement(FontAwesomeIcon, {
76
+ icon: faPlus
77
+ })))))));
78
+ return /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("div", {
63
79
  className: "search-results"
80
+ }, /*#__PURE__*/React.createElement(ListGroup, {
81
+ variant: "flush",
82
+ className: "cherita-list"
64
83
  }, deferredData?.length ? /*#__PURE__*/React.createElement(VirtualizedList, {
65
84
  getDataAtIndex: getDataAtIndex,
66
85
  count: deferredData.length,
67
86
  ItemComponent: ItemComponent,
68
87
  overscan: 500,
69
- estimateSize: 32,
70
- maxHeight: "25vh"
71
- }) : /*#__PURE__*/React.createElement(Dropdown.Item, {
88
+ estimateSize: 42,
89
+ maxHeight: "70vh"
90
+ }) : /*#__PURE__*/React.createElement(ListGroup.Item, {
72
91
  key: "empty",
73
92
  as: "button",
74
93
  disabled: true
75
- }, !serverError ? isStale || isPending ? "Loading..." : "No items found" : "Failed to fetch data")));
94
+ }, !text.length ? "Search features" : !serverError ? isStale || isPending ? "Loading..." : "No items found" : "Failed to fetch data"))));
76
95
  }
77
96
  export function DiseasesSearchResults(_ref2) {
78
97
  let {
79
98
  text,
80
- setShowSuggestions
99
+ selectedResult,
100
+ setSelectedResult,
101
+ setResultsLength
81
102
  } = _ref2;
82
103
  const [suggestions, setSuggestions] = useState([]);
83
104
  const dispatch = useDatasetDispatch();
@@ -112,37 +133,41 @@ export function DiseasesSearchResults(_ref2) {
112
133
  useEffect(() => {
113
134
  if (!isPending && !serverError) {
114
135
  setSuggestions(fetchedData);
115
- setShowSuggestions(true);
136
+ setResultsLength(fetchedData?.length);
116
137
  }
117
- }, [fetchedData, isPending, serverError, setShowSuggestions]);
138
+ }, [fetchedData, isPending, serverError, setResultsLength]);
118
139
  const getDataAtIndex = index => deferredData[index];
119
- const ItemComponent = item => /*#__PURE__*/React.createElement(Dropdown.Item, {
140
+ const ItemComponent = item => /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("div", {
141
+ className: "virtualized-list-wrapper"
142
+ }, /*#__PURE__*/React.createElement(ListGroup.Item, {
120
143
  key: item.name,
121
- as: "button",
122
- disabled: isStale,
123
144
  onClick: () => {
145
+ setSelectedResult(item);
124
146
  dispatch({
125
147
  type: "select.disease",
126
148
  id: item.disease_id,
127
149
  name: item.disease_name
128
150
  });
129
- _.delay(() => {
130
- setShowSuggestions(false);
131
- }, 150);
132
- }
133
- }, item.disease_name);
134
- return /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement(Dropdown.Header, null, "Diseases"), /*#__PURE__*/React.createElement("div", {
151
+ },
152
+ active: selectedResult?.id === item.id
153
+ }, /*#__PURE__*/React.createElement("div", {
154
+ className: "d-flex justify-content-between align-items-center w-100"
155
+ }, /*#__PURE__*/React.createElement("div", null, item.disease_name)))));
156
+ return /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("div", {
135
157
  className: "search-results"
158
+ }, /*#__PURE__*/React.createElement(ListGroup, {
159
+ variant: "flush",
160
+ className: "cherita-list"
136
161
  }, deferredData?.length ? /*#__PURE__*/React.createElement(VirtualizedList, {
137
162
  getDataAtIndex: getDataAtIndex,
138
163
  count: deferredData.length,
139
164
  ItemComponent: ItemComponent,
140
165
  overscan: 250,
141
166
  estimateSize: 32,
142
- maxHeight: "25vh"
143
- }) : /*#__PURE__*/React.createElement(Dropdown.Item, {
167
+ maxHeight: "70vh"
168
+ }) : /*#__PURE__*/React.createElement(ListGroup.Item, {
144
169
  key: "empty",
145
170
  as: "button",
146
171
  disabled: true
147
- }, !serverError ? isStale || isPending ? "Loading..." : "No items found" : "Failed to fetch data")));
172
+ }, !text.length ? "Search diseases" : !serverError ? isStale || isPending ? "Loading..." : "No items found" : "Failed to fetch data"))));
148
173
  }
@@ -8,7 +8,7 @@ import { COLOR_ENCODINGS, SELECTION_MODES } from "../../constants/constants";
8
8
  import { useDataset, useDatasetDispatch } from "../../context/DatasetContext";
9
9
  import { useFilteredData } from "../../context/FilterContext";
10
10
  import { Histogram } from "../../utils/Histogram";
11
- import { useFetch, useDebouncedFetch } from "../../utils/requests";
11
+ import { useDebouncedFetch, useFetch } from "../../utils/requests";
12
12
  import { VirtualizedList } from "../../utils/VirtualizedList";
13
13
  function VarHistogram(_ref) {
14
14
  let {
@@ -75,7 +75,7 @@ function VarDiseaseInfoItem(item) {
75
75
  }
76
76
  }))));
77
77
  }
78
- function VarDiseaseInfo(_ref2) {
78
+ export function VarDiseaseInfo(_ref2) {
79
79
  let {
80
80
  data
81
81
  } = _ref2;
@@ -255,4 +255,72 @@ export function VarItem(_ref5) {
255
255
  } else {
256
256
  return null;
257
257
  }
258
+ }
259
+ export function SearchResultItem(_ref6) {
260
+ let {
261
+ item,
262
+ isActive,
263
+ selectVar,
264
+ removeVar,
265
+ isDiseaseGene = false,
266
+ showSetColorEncoding = true,
267
+ showRemove = true
268
+ } = _ref6;
269
+ const ENDPOINT = "disease/gene";
270
+ const [openInfo, setOpenInfo] = useState(false);
271
+ const dataset = useDataset();
272
+ const params = {
273
+ geneName: item.name,
274
+ diseaseDatasets: dataset.diseaseDatasets
275
+ };
276
+ const isNotInData = item.matrix_index === -1;
277
+ const {
278
+ fetchedData,
279
+ isPending,
280
+ serverError
281
+ } = useFetch(ENDPOINT, params, {
282
+ refetchOnMount: false,
283
+ enabled: !!dataset.diseaseDatasets.length
284
+ });
285
+ const hasDiseaseInfo = !isPending && !serverError && !!fetchedData?.length;
286
+ return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("div", {
287
+ className: `d-flex justify-content-between ${hasDiseaseInfo ? "cursor-pointer" : ""}`,
288
+ onClick: () => {
289
+ setOpenInfo(o => !o);
290
+ }
291
+ }, /*#__PURE__*/React.createElement("div", {
292
+ className: "d-flex justify-content-between align-items-center w-100"
293
+ }, /*#__PURE__*/React.createElement("div", null, item.name), /*#__PURE__*/React.createElement("div", {
294
+ className: "d-flex align-items-center gap-1"
295
+ }, hasDiseaseInfo && /*#__PURE__*/React.createElement(MoreVert, null), showSetColorEncoding && /*#__PURE__*/React.createElement(Button, {
296
+ type: "button",
297
+ key: item.matrix_index,
298
+ variant: isActive ? "primary" : isNotInData ? "outline-secondary" : "outline-primary",
299
+ className: "m-0 p-0 px-1",
300
+ onClick: e => {
301
+ e.stopPropagation();
302
+ selectVar();
303
+ },
304
+ disabled: isNotInData,
305
+ title: isNotInData ? "Not present in data" : "Set as color encoding"
306
+ }, /*#__PURE__*/React.createElement(FontAwesomeIcon, {
307
+ icon: faDroplet
308
+ })), (!isDiseaseGene || !showRemove) && /*#__PURE__*/React.createElement(Button, {
309
+ type: "button",
310
+ className: "m-0 p-0 px-1",
311
+ variant: "outline-secondary",
312
+ title: "Remove from list",
313
+ onClick: e => {
314
+ e.stopPropagation();
315
+ removeVar();
316
+ }
317
+ }, /*#__PURE__*/React.createElement(FontAwesomeIcon, {
318
+ icon: faTrash
319
+ }))))), hasDiseaseInfo && /*#__PURE__*/React.createElement(Collapse, {
320
+ in: openInfo
321
+ }, /*#__PURE__*/React.createElement("div", {
322
+ className: "mt-2 var-disease-info-collapse"
323
+ }, /*#__PURE__*/React.createElement(VarDiseaseInfo, {
324
+ data: fetchedData
325
+ }))));
258
326
  }