@jbrowse/plugin-linear-genome-view 1.4.1 → 1.5.2
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/BaseLinearDisplay/components/Block.d.ts +7 -10
- package/dist/BaseLinearDisplay/models/BaseLinearDisplayModel.d.ts +16 -9
- package/dist/BaseLinearDisplay/models/serverSideRenderedBlock.d.ts +2 -2
- package/dist/LinearBareDisplay/model.d.ts +8 -8
- package/dist/LinearBasicDisplay/model.d.ts +11 -8
- package/dist/LinearGenomeView/components/HelpDialog.d.ts +5 -0
- package/dist/LinearGenomeView/components/LinearGenomeView.d.ts +3 -5
- package/dist/LinearGenomeView/components/LinearGenomeViewSvg.d.ts +4 -0
- package/dist/LinearGenomeView/components/OverviewRubberBand.d.ts +2 -3
- package/dist/LinearGenomeView/components/OverviewScaleBar.d.ts +116 -2
- package/dist/LinearGenomeView/components/RefNameAutocomplete.d.ts +3 -11
- package/dist/LinearGenomeView/components/ScaleBar.d.ts +36 -2
- package/dist/LinearGenomeView/components/util.d.ts +2 -0
- package/dist/LinearGenomeView/index.d.ts +22 -4
- package/dist/index.d.ts +26 -26
- package/dist/plugin-linear-genome-view.cjs.development.js +3178 -2884
- package/dist/plugin-linear-genome-view.cjs.development.js.map +1 -1
- package/dist/plugin-linear-genome-view.cjs.production.min.js +1 -1
- package/dist/plugin-linear-genome-view.cjs.production.min.js.map +1 -1
- package/dist/plugin-linear-genome-view.esm.js +3191 -2898
- package/dist/plugin-linear-genome-view.esm.js.map +1 -1
- package/package.json +2 -2
- package/src/BaseLinearDisplay/components/BaseLinearDisplay.tsx +3 -0
- package/src/BaseLinearDisplay/components/Block.tsx +20 -33
- package/src/BaseLinearDisplay/models/BaseLinearDisplayModel.tsx +3 -7
- package/src/BaseLinearDisplay/models/serverSideRenderedBlock.ts +15 -13
- package/src/LinearBasicDisplay/model.ts +25 -3
- package/src/LinearGenomeView/components/ExportSvgDialog.tsx +6 -6
- package/src/LinearGenomeView/components/Header.tsx +56 -78
- package/src/LinearGenomeView/components/HelpDialog.tsx +81 -0
- package/src/LinearGenomeView/components/ImportForm.tsx +139 -158
- package/src/LinearGenomeView/components/LinearGenomeView.test.js +6 -6
- package/src/LinearGenomeView/components/LinearGenomeView.tsx +30 -245
- package/src/LinearGenomeView/components/LinearGenomeViewSvg.tsx +317 -0
- package/src/LinearGenomeView/components/OverviewRubberBand.tsx +74 -34
- package/src/LinearGenomeView/components/OverviewScaleBar.tsx +326 -177
- package/src/LinearGenomeView/components/RefNameAutocomplete.tsx +152 -157
- package/src/LinearGenomeView/components/SearchResultsDialog.tsx +12 -34
- package/src/LinearGenomeView/components/SequenceDialog.tsx +10 -9
- package/src/LinearGenomeView/components/__snapshots__/LinearGenomeView.test.js.snap +127 -254
- package/src/LinearGenomeView/components/util.ts +10 -0
- package/src/LinearGenomeView/index.tsx +69 -27
- package/src/index.ts +3 -1
|
@@ -1,71 +1,40 @@
|
|
|
1
|
-
|
|
2
|
-
* Based on:
|
|
3
|
-
* https://material-ui.com/components/autocomplete/#Virtualize.tsx
|
|
4
|
-
* Asynchronous Requests for autocomplete:
|
|
5
|
-
* https://material-ui.com/components/autocomplete/
|
|
6
|
-
*/
|
|
7
|
-
import React, { useMemo, useEffect, useState } from 'react'
|
|
1
|
+
import React, { lazy, useMemo, useEffect, useState } from 'react'
|
|
8
2
|
import { observer } from 'mobx-react'
|
|
9
|
-
|
|
10
|
-
// jbrowse core
|
|
11
3
|
import { getSession, useDebounce, measureText } from '@jbrowse/core/util'
|
|
12
4
|
import BaseResult, {
|
|
13
5
|
RefSequenceResult,
|
|
14
6
|
} from '@jbrowse/core/TextSearch/BaseResults'
|
|
15
|
-
|
|
16
|
-
// material ui
|
|
17
7
|
import {
|
|
18
8
|
CircularProgress,
|
|
9
|
+
IconButton,
|
|
19
10
|
InputAdornment,
|
|
20
11
|
Popper,
|
|
12
|
+
PopperProps,
|
|
21
13
|
TextField,
|
|
22
14
|
TextFieldProps as TFP,
|
|
23
|
-
PopperProps,
|
|
24
15
|
Typography,
|
|
25
16
|
} from '@material-ui/core'
|
|
17
|
+
|
|
18
|
+
// icons
|
|
26
19
|
import SearchIcon from '@material-ui/icons/Search'
|
|
27
20
|
import Autocomplete from '@material-ui/lab/Autocomplete'
|
|
21
|
+
import HelpIcon from '@material-ui/icons/Help'
|
|
28
22
|
|
|
29
23
|
// locals
|
|
30
24
|
import { LinearGenomeViewModel } from '..'
|
|
25
|
+
import { dedupe } from './util'
|
|
26
|
+
|
|
27
|
+
// lazy
|
|
28
|
+
const HelpDialog = lazy(() => import('./HelpDialog'))
|
|
31
29
|
|
|
32
|
-
/**
|
|
33
|
-
* Option interface used to format results to display in dropdown
|
|
34
|
-
* of the materila ui interface
|
|
35
|
-
*/
|
|
36
30
|
export interface Option {
|
|
37
31
|
group?: string
|
|
38
32
|
result: BaseResult
|
|
39
33
|
}
|
|
40
34
|
|
|
41
|
-
async function fetchResults(
|
|
42
|
-
self: LinearGenomeViewModel,
|
|
43
|
-
query: string,
|
|
44
|
-
assemblyName: string,
|
|
45
|
-
) {
|
|
46
|
-
const { textSearchManager } = getSession(self)
|
|
47
|
-
const { rankSearchResults } = self
|
|
48
|
-
const searchScope = self.searchScope(assemblyName)
|
|
49
|
-
return textSearchManager
|
|
50
|
-
?.search(
|
|
51
|
-
{
|
|
52
|
-
queryString: query,
|
|
53
|
-
searchType: 'prefix',
|
|
54
|
-
},
|
|
55
|
-
searchScope,
|
|
56
|
-
rankSearchResults,
|
|
57
|
-
)
|
|
58
|
-
.then(results =>
|
|
59
|
-
results.filter(
|
|
60
|
-
(elem, index, self) =>
|
|
61
|
-
index === self.findIndex(t => t.label === elem.label),
|
|
62
|
-
),
|
|
63
|
-
)
|
|
64
|
-
}
|
|
65
|
-
|
|
66
35
|
// the logic of this method is to only apply a filter to RefSequenceResults
|
|
67
|
-
// because they do not have a matchedObject. the trix search results
|
|
68
|
-
//
|
|
36
|
+
// because they do not have a matchedObject. the trix search results already
|
|
37
|
+
// filter so don't need re-filtering
|
|
69
38
|
function filterOptions(options: Option[], searchQuery: string) {
|
|
70
39
|
return options.filter(option => {
|
|
71
40
|
const { result } = option
|
|
@@ -100,22 +69,28 @@ function RefNameAutocomplete({
|
|
|
100
69
|
onSelect,
|
|
101
70
|
assemblyName,
|
|
102
71
|
style,
|
|
72
|
+
fetchResults,
|
|
103
73
|
value,
|
|
74
|
+
minWidth = 200,
|
|
104
75
|
TextFieldProps = {},
|
|
105
76
|
}: {
|
|
106
77
|
model: LinearGenomeViewModel
|
|
107
78
|
onSelect: (region: BaseResult) => void
|
|
108
79
|
assemblyName?: string
|
|
109
80
|
value?: string
|
|
81
|
+
fetchResults: (query: string) => Promise<BaseResult[]>
|
|
110
82
|
style?: React.CSSProperties
|
|
83
|
+
minWidth?: number
|
|
111
84
|
TextFieldProps?: TFP
|
|
112
85
|
}) {
|
|
113
86
|
const session = getSession(model)
|
|
114
87
|
const { assemblyManager } = session
|
|
115
88
|
const [open, setOpen] = useState(false)
|
|
116
89
|
const [loaded, setLoaded] = useState(true)
|
|
90
|
+
const [isHelpDialogDisplayed, setHelpDialogDisplayed] = useState(false)
|
|
117
91
|
const [currentSearch, setCurrentSearch] = useState('')
|
|
118
|
-
const [
|
|
92
|
+
const [inputValue, setInputValue] = useState('')
|
|
93
|
+
const [searchOptions, setSearchOptions] = useState<Option[]>()
|
|
119
94
|
const debouncedSearch = useDebounce(currentSearch, 300)
|
|
120
95
|
const { coarseVisibleLocStrings, hasDisplayedRegions } = model
|
|
121
96
|
const assembly = assemblyName ? assemblyManager.get(assemblyName) : undefined
|
|
@@ -145,11 +120,15 @@ function RefNameAutocomplete({
|
|
|
145
120
|
}
|
|
146
121
|
|
|
147
122
|
setLoaded(false)
|
|
148
|
-
const results = await fetchResults(
|
|
149
|
-
if (
|
|
150
|
-
setSearchOptions(
|
|
123
|
+
const results = await fetchResults(debouncedSearch)
|
|
124
|
+
if (active) {
|
|
125
|
+
setSearchOptions(
|
|
126
|
+
dedupe(results, r => r.getDisplayString()).map(result => ({
|
|
127
|
+
result,
|
|
128
|
+
})),
|
|
129
|
+
)
|
|
130
|
+
setLoaded(true)
|
|
151
131
|
}
|
|
152
|
-
setLoaded(true)
|
|
153
132
|
} catch (e) {
|
|
154
133
|
console.error(e)
|
|
155
134
|
if (active) {
|
|
@@ -161,123 +140,139 @@ function RefNameAutocomplete({
|
|
|
161
140
|
return () => {
|
|
162
141
|
active = false
|
|
163
142
|
}
|
|
164
|
-
}, [assemblyName, debouncedSearch, session, model])
|
|
143
|
+
}, [assemblyName, fetchResults, debouncedSearch, session, model])
|
|
165
144
|
|
|
166
145
|
const inputBoxVal = coarseVisibleLocStrings || value || ''
|
|
167
146
|
|
|
168
|
-
// heuristic, text width + icon width
|
|
169
|
-
|
|
147
|
+
// heuristic, text width + icon width
|
|
148
|
+
// + 45 accomodates help icon and search icon
|
|
149
|
+
const width = Math.min(
|
|
150
|
+
Math.max(measureText(inputBoxVal, 16) + 45, minWidth),
|
|
151
|
+
550,
|
|
152
|
+
)
|
|
170
153
|
|
|
171
154
|
// notes on implementation:
|
|
172
|
-
// selectOnFocus helps highlight the field when clicked
|
|
173
|
-
// blurOnSelect helps it so that when the user-re-clicks on the textfield,
|
|
174
|
-
// that selectOnFocus re-activates
|
|
155
|
+
// The selectOnFocus setting helps highlight the field when clicked
|
|
175
156
|
return (
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
setOpen(
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
const newResult = new BaseResult({
|
|
208
|
-
label: selectedOption,
|
|
209
|
-
})
|
|
210
|
-
onSelect(newResult)
|
|
211
|
-
} else {
|
|
212
|
-
const { result } = selectedOption
|
|
213
|
-
onSelect(result)
|
|
214
|
-
}
|
|
215
|
-
}}
|
|
216
|
-
options={searchOptions.length === 0 ? options : searchOptions}
|
|
217
|
-
getOptionDisabled={option => option?.group === 'limitOption'}
|
|
218
|
-
filterOptions={(options, params) => {
|
|
219
|
-
const filtered = filterOptions(
|
|
220
|
-
options,
|
|
221
|
-
params.inputValue.toLocaleLowerCase(),
|
|
222
|
-
)
|
|
223
|
-
return [
|
|
224
|
-
...filtered.slice(0, 100),
|
|
225
|
-
...(filtered.length > 100
|
|
226
|
-
? [
|
|
227
|
-
{
|
|
228
|
-
group: 'limitOption',
|
|
229
|
-
result: new BaseResult({
|
|
230
|
-
label: 'keep typing for more results',
|
|
231
|
-
}),
|
|
232
|
-
},
|
|
233
|
-
]
|
|
234
|
-
: []),
|
|
235
|
-
]
|
|
236
|
-
}}
|
|
237
|
-
renderInput={params => {
|
|
238
|
-
const { helperText, InputProps = {} } = TextFieldProps
|
|
239
|
-
return (
|
|
240
|
-
<TextField
|
|
241
|
-
{...params}
|
|
242
|
-
{...TextFieldProps}
|
|
243
|
-
helperText={helperText}
|
|
244
|
-
InputProps={{
|
|
245
|
-
...params.InputProps,
|
|
246
|
-
...InputProps,
|
|
157
|
+
<>
|
|
158
|
+
<Autocomplete
|
|
159
|
+
id={`refNameAutocomplete-${model.id}`}
|
|
160
|
+
data-testid="autocomplete"
|
|
161
|
+
disableListWrap
|
|
162
|
+
disableClearable
|
|
163
|
+
PopperComponent={MyPopper}
|
|
164
|
+
disabled={!assemblyName}
|
|
165
|
+
freeSolo
|
|
166
|
+
includeInputInList
|
|
167
|
+
selectOnFocus
|
|
168
|
+
style={{ ...style, width }}
|
|
169
|
+
value={inputBoxVal}
|
|
170
|
+
loading={!loaded}
|
|
171
|
+
inputValue={inputValue}
|
|
172
|
+
onInputChange={(event, newInputValue) => setInputValue(newInputValue)}
|
|
173
|
+
loadingText="loading results"
|
|
174
|
+
open={open}
|
|
175
|
+
onOpen={() => setOpen(true)}
|
|
176
|
+
onClose={() => {
|
|
177
|
+
setOpen(false)
|
|
178
|
+
setLoaded(true)
|
|
179
|
+
if (hasDisplayedRegions) {
|
|
180
|
+
setCurrentSearch('')
|
|
181
|
+
setSearchOptions(undefined)
|
|
182
|
+
}
|
|
183
|
+
}}
|
|
184
|
+
onChange={(_event, selectedOption) => {
|
|
185
|
+
if (!selectedOption || !assemblyName) {
|
|
186
|
+
return
|
|
187
|
+
}
|
|
247
188
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
189
|
+
if (typeof selectedOption === 'string') {
|
|
190
|
+
// handles string inputs on keyPress enter
|
|
191
|
+
onSelect(new BaseResult({ label: selectedOption }))
|
|
192
|
+
} else {
|
|
193
|
+
onSelect(selectedOption.result)
|
|
194
|
+
}
|
|
195
|
+
setInputValue(inputBoxVal)
|
|
196
|
+
}}
|
|
197
|
+
options={!searchOptions?.length ? options : searchOptions}
|
|
198
|
+
getOptionDisabled={option => option?.group === 'limitOption'}
|
|
199
|
+
filterOptions={(options, params) => {
|
|
200
|
+
const filtered = filterOptions(
|
|
201
|
+
options,
|
|
202
|
+
params.inputValue.toLocaleLowerCase(),
|
|
203
|
+
)
|
|
204
|
+
return [
|
|
205
|
+
...filtered.slice(0, 100),
|
|
206
|
+
...(filtered.length > 100
|
|
207
|
+
? [
|
|
208
|
+
{
|
|
209
|
+
group: 'limitOption',
|
|
210
|
+
result: new BaseResult({
|
|
211
|
+
label: 'keep typing for more results',
|
|
212
|
+
}),
|
|
213
|
+
},
|
|
214
|
+
]
|
|
215
|
+
: []),
|
|
216
|
+
]
|
|
217
|
+
}}
|
|
218
|
+
renderInput={params => {
|
|
219
|
+
const { helperText, InputProps = {} } = TextFieldProps
|
|
220
|
+
return (
|
|
221
|
+
<TextField
|
|
222
|
+
onBlur={() => {
|
|
223
|
+
// this is used to restore a refName or the non-user-typed input
|
|
224
|
+
// to the box on blurring
|
|
225
|
+
setInputValue(inputBoxVal)
|
|
226
|
+
}}
|
|
227
|
+
{...params}
|
|
228
|
+
{...TextFieldProps}
|
|
229
|
+
helperText={helperText}
|
|
230
|
+
InputProps={{
|
|
231
|
+
...params.InputProps,
|
|
232
|
+
...InputProps,
|
|
274
233
|
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
234
|
+
endAdornment: (
|
|
235
|
+
<>
|
|
236
|
+
{regions.length === 0 ? (
|
|
237
|
+
<CircularProgress color="inherit" size={20} />
|
|
238
|
+
) : (
|
|
239
|
+
<InputAdornment position="end" style={{ marginRight: 7 }}>
|
|
240
|
+
<SearchIcon />
|
|
241
|
+
<IconButton
|
|
242
|
+
onClick={() => setHelpDialogDisplayed(true)}
|
|
243
|
+
>
|
|
244
|
+
<HelpIcon />
|
|
245
|
+
</IconButton>
|
|
246
|
+
</InputAdornment>
|
|
247
|
+
)}
|
|
248
|
+
{params.InputProps.endAdornment}
|
|
249
|
+
</>
|
|
250
|
+
),
|
|
251
|
+
}}
|
|
252
|
+
placeholder="Search for location"
|
|
253
|
+
onChange={e => {
|
|
254
|
+
setCurrentSearch(e.target.value)
|
|
255
|
+
}}
|
|
256
|
+
/>
|
|
257
|
+
)
|
|
258
|
+
}}
|
|
259
|
+
renderOption={option => {
|
|
260
|
+
const { result } = option
|
|
261
|
+
const component = result.getRenderingComponent()
|
|
262
|
+
if (component && React.isValidElement(component)) {
|
|
263
|
+
return component
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
return <Typography noWrap>{result.getDisplayString()}</Typography>
|
|
267
|
+
}}
|
|
268
|
+
getOptionLabel={option =>
|
|
269
|
+
(typeof option === 'string' ? option : option.result.getLabel()) || ''
|
|
270
|
+
}
|
|
271
|
+
/>
|
|
272
|
+
{isHelpDialogDisplayed ? (
|
|
273
|
+
<HelpDialog handleClose={() => setHelpDialogDisplayed(false)} />
|
|
274
|
+
) : null}
|
|
275
|
+
</>
|
|
281
276
|
)
|
|
282
277
|
}
|
|
283
278
|
|
|
@@ -6,7 +6,6 @@ import {
|
|
|
6
6
|
Dialog,
|
|
7
7
|
DialogActions,
|
|
8
8
|
DialogContent,
|
|
9
|
-
DialogContentText,
|
|
10
9
|
DialogTitle,
|
|
11
10
|
Divider,
|
|
12
11
|
IconButton,
|
|
@@ -18,10 +17,9 @@ import {
|
|
|
18
17
|
TableRow,
|
|
19
18
|
Typography,
|
|
20
19
|
Paper,
|
|
20
|
+
makeStyles,
|
|
21
21
|
} from '@material-ui/core'
|
|
22
22
|
import CloseIcon from '@material-ui/icons/Close'
|
|
23
|
-
import { makeStyles } from '@material-ui/core/styles'
|
|
24
|
-
import BaseResult from '@jbrowse/core/TextSearch/BaseResults'
|
|
25
23
|
import { LinearGenomeViewModel } from '../..'
|
|
26
24
|
|
|
27
25
|
export const useStyles = makeStyles(theme => ({
|
|
@@ -101,8 +99,8 @@ export default function SearchResultsDialog({
|
|
|
101
99
|
|
|
102
100
|
return (
|
|
103
101
|
<Dialog open maxWidth="xl" onClose={handleClose}>
|
|
104
|
-
<DialogTitle
|
|
105
|
-
Search
|
|
102
|
+
<DialogTitle>
|
|
103
|
+
Search results
|
|
106
104
|
{handleClose ? (
|
|
107
105
|
<IconButton
|
|
108
106
|
data-testid="close-resultsDialog"
|
|
@@ -117,18 +115,15 @@ export default function SearchResultsDialog({
|
|
|
117
115
|
</DialogTitle>
|
|
118
116
|
<Divider />
|
|
119
117
|
<DialogContent>
|
|
120
|
-
{model.searchResults?.length
|
|
121
|
-
model.searchResults === undefined ? (
|
|
118
|
+
{!model.searchResults?.length ? (
|
|
122
119
|
<Typography>
|
|
123
|
-
|
|
124
|
-
<b>{model.searchQuery}</b>
|
|
120
|
+
No results found for <b>{model.searchQuery}</b>
|
|
125
121
|
</Typography>
|
|
126
122
|
) : (
|
|
127
123
|
<>
|
|
128
|
-
<
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
</DialogContentText>
|
|
124
|
+
<Typography>
|
|
125
|
+
Showing results for <b>{model.searchQuery}</b>
|
|
126
|
+
</Typography>
|
|
132
127
|
<TableContainer component={Paper}>
|
|
133
128
|
<Table>
|
|
134
129
|
<TableHead>
|
|
@@ -140,8 +135,8 @@ export default function SearchResultsDialog({
|
|
|
140
135
|
</TableRow>
|
|
141
136
|
</TableHead>
|
|
142
137
|
<TableBody>
|
|
143
|
-
{model.searchResults.map(
|
|
144
|
-
<TableRow key={`${result.
|
|
138
|
+
{model.searchResults.map(result => (
|
|
139
|
+
<TableRow key={`${result.getId()}`}>
|
|
145
140
|
<TableCell component="th" scope="row">
|
|
146
141
|
{result.getLabel()}
|
|
147
142
|
</TableCell>
|
|
@@ -151,18 +146,6 @@ export default function SearchResultsDialog({
|
|
|
151
146
|
<TableCell align="right">
|
|
152
147
|
{getTrackName(result.getTrackId()) || 'N/A'}
|
|
153
148
|
</TableCell>
|
|
154
|
-
<TableCell align="right">
|
|
155
|
-
<Button
|
|
156
|
-
onClick={() => {
|
|
157
|
-
handleClick(result.getLocation())
|
|
158
|
-
handleClose()
|
|
159
|
-
}}
|
|
160
|
-
color="primary"
|
|
161
|
-
variant="contained"
|
|
162
|
-
>
|
|
163
|
-
Go to location
|
|
164
|
-
</Button>
|
|
165
|
-
</TableCell>
|
|
166
149
|
<TableCell align="right">
|
|
167
150
|
<Button
|
|
168
151
|
onClick={() => {
|
|
@@ -177,7 +160,7 @@ export default function SearchResultsDialog({
|
|
|
177
160
|
color="primary"
|
|
178
161
|
variant="contained"
|
|
179
162
|
>
|
|
180
|
-
|
|
163
|
+
Go
|
|
181
164
|
</Button>
|
|
182
165
|
</TableCell>
|
|
183
166
|
</TableRow>
|
|
@@ -190,12 +173,7 @@ export default function SearchResultsDialog({
|
|
|
190
173
|
</DialogContent>
|
|
191
174
|
<Divider />
|
|
192
175
|
<DialogActions>
|
|
193
|
-
<Button
|
|
194
|
-
onClick={() => {
|
|
195
|
-
handleClose()
|
|
196
|
-
}}
|
|
197
|
-
color="primary"
|
|
198
|
-
>
|
|
176
|
+
<Button onClick={() => handleClose()} color="primary">
|
|
199
177
|
Cancel
|
|
200
178
|
</Button>
|
|
201
179
|
</DialogActions>
|
|
@@ -17,14 +17,18 @@ import {
|
|
|
17
17
|
import { observer } from 'mobx-react'
|
|
18
18
|
import { saveAs } from 'file-saver'
|
|
19
19
|
import { Region } from '@jbrowse/core/util/types'
|
|
20
|
-
import {
|
|
20
|
+
import { getConf } from '@jbrowse/core/configuration'
|
|
21
21
|
import copy from 'copy-to-clipboard'
|
|
22
|
-
import { ContentCopy as ContentCopyIcon } from '@jbrowse/core/ui/Icons'
|
|
23
|
-
import CloseIcon from '@material-ui/icons/Close'
|
|
24
|
-
import GetAppIcon from '@material-ui/icons/GetApp'
|
|
25
22
|
import { getSession } from '@jbrowse/core/util'
|
|
26
23
|
import { Feature } from '@jbrowse/core/util/simpleFeature'
|
|
27
24
|
import { formatSeqFasta } from '@jbrowse/core/util/formatFastaStrings'
|
|
25
|
+
|
|
26
|
+
// icons
|
|
27
|
+
import { ContentCopy as ContentCopyIcon } from '@jbrowse/core/ui/Icons'
|
|
28
|
+
import CloseIcon from '@material-ui/icons/Close'
|
|
29
|
+
import GetAppIcon from '@material-ui/icons/GetApp'
|
|
30
|
+
|
|
31
|
+
// locals
|
|
28
32
|
import { LinearGenomeViewModel } from '..'
|
|
29
33
|
|
|
30
34
|
const useStyles = makeStyles(theme => ({
|
|
@@ -71,10 +75,7 @@ async function fetchSequence(
|
|
|
71
75
|
if (!assembly) {
|
|
72
76
|
throw new Error(`assembly ${assemblyName} not found`)
|
|
73
77
|
}
|
|
74
|
-
const adapterConfig =
|
|
75
|
-
'sequence',
|
|
76
|
-
'adapter',
|
|
77
|
-
])
|
|
78
|
+
const adapterConfig = getConf(assembly, ['sequence', 'adapter'])
|
|
78
79
|
|
|
79
80
|
const sessionId = 'getSequence'
|
|
80
81
|
const chunks = (await Promise.all(
|
|
@@ -101,7 +102,7 @@ function SequenceDialog({
|
|
|
101
102
|
}) {
|
|
102
103
|
const classes = useStyles()
|
|
103
104
|
const session = getSession(model)
|
|
104
|
-
const [error, setError] = useState<
|
|
105
|
+
const [error, setError] = useState<unknown>()
|
|
105
106
|
const [sequence, setSequence] = useState<string>()
|
|
106
107
|
const loading = Boolean(sequence === undefined)
|
|
107
108
|
const { leftOffset, rightOffset } = model
|