@jbrowse/plugin-linear-genome-view 1.5.8 → 1.6.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/models/BaseLinearDisplayModel.d.ts +35 -17
- package/dist/LinearBareDisplay/model.d.ts +28 -4
- package/dist/LinearBasicDisplay/model.d.ts +28 -4
- package/dist/LinearGenomeView/components/Header.d.ts +2 -1
- package/dist/LinearGenomeView/components/RefNameAutocomplete.d.ts +2 -1
- package/dist/LinearGenomeView/components/ScaleBar.d.ts +8 -8
- package/dist/LinearGenomeView/components/SearchBox.d.ts +8 -0
- package/dist/LinearGenomeView/index.d.ts +7 -4
- package/dist/index.d.ts +85 -14
- package/dist/plugin-linear-genome-view.cjs.development.js +738 -389
- 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 +749 -401
- package/dist/plugin-linear-genome-view.esm.js.map +1 -1
- package/package.json +2 -2
- package/src/BaseLinearDisplay/models/BaseLinearDisplayModel.tsx +308 -88
- package/src/BaseLinearDisplay/models/baseLinearDisplayConfigSchema.ts +10 -3
- package/src/LinearBasicDisplay/configSchema.ts +0 -6
- package/src/LinearGenomeView/components/Header.tsx +38 -120
- package/src/LinearGenomeView/components/ImportForm.tsx +1 -1
- package/src/LinearGenomeView/components/LinearGenomeView.test.js +6 -4
- package/src/LinearGenomeView/components/RefNameAutocomplete.tsx +9 -5
- package/src/LinearGenomeView/components/SearchBox.tsx +111 -0
- package/src/LinearGenomeView/components/TrackLabel.tsx +10 -6
- package/src/LinearGenomeView/components/__snapshots__/LinearGenomeView.test.js.snap +10 -11
- package/src/LinearGenomeView/index.tsx +25 -53
- package/src/index.ts +61 -2
|
@@ -1,16 +1,13 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
2
|
import { observer } from 'mobx-react'
|
|
3
|
-
import { getSession } from '@jbrowse/core/util'
|
|
4
3
|
import {
|
|
5
4
|
Button,
|
|
6
5
|
FormGroup,
|
|
7
6
|
Typography,
|
|
8
7
|
makeStyles,
|
|
9
|
-
useTheme,
|
|
10
8
|
alpha,
|
|
11
9
|
} from '@material-ui/core'
|
|
12
|
-
import
|
|
13
|
-
import { SearchType } from '@jbrowse/core/data_adapters/BaseAdapter'
|
|
10
|
+
import SearchBox from './SearchBox'
|
|
14
11
|
|
|
15
12
|
// icons
|
|
16
13
|
import { TrackSelector as TrackSelectorIcon } from '@jbrowse/core/ui/Icons'
|
|
@@ -18,15 +15,16 @@ import ArrowForwardIcon from '@material-ui/icons/ArrowForward'
|
|
|
18
15
|
import ArrowBackIcon from '@material-ui/icons/ArrowBack'
|
|
19
16
|
|
|
20
17
|
// locals
|
|
21
|
-
import {
|
|
22
|
-
|
|
18
|
+
import {
|
|
19
|
+
LinearGenomeViewModel,
|
|
20
|
+
SPACING,
|
|
21
|
+
WIDGET_HEIGHT,
|
|
22
|
+
HEADER_BAR_HEIGHT,
|
|
23
|
+
} from '..'
|
|
23
24
|
import OverviewScaleBar from './OverviewScaleBar'
|
|
24
25
|
import ZoomControls from './ZoomControls'
|
|
25
|
-
import { dedupe } from './util'
|
|
26
|
-
|
|
27
|
-
const WIDGET_HEIGHT = 32
|
|
28
|
-
const SPACING = 7
|
|
29
26
|
|
|
27
|
+
type LGV = LinearGenomeViewModel
|
|
30
28
|
const useStyles = makeStyles(theme => ({
|
|
31
29
|
headerBar: {
|
|
32
30
|
height: HEADER_BAR_HEIGHT,
|
|
@@ -39,10 +37,7 @@ const useStyles = makeStyles(theme => ({
|
|
|
39
37
|
spacer: {
|
|
40
38
|
flexGrow: 1,
|
|
41
39
|
},
|
|
42
|
-
|
|
43
|
-
headerRefName: {
|
|
44
|
-
minWidth: 100,
|
|
45
|
-
},
|
|
40
|
+
|
|
46
41
|
panButton: {
|
|
47
42
|
background: alpha(theme.palette.background.paper, 0.8),
|
|
48
43
|
height: WIDGET_HEIGHT,
|
|
@@ -63,7 +58,7 @@ const useStyles = makeStyles(theme => ({
|
|
|
63
58
|
},
|
|
64
59
|
}))
|
|
65
60
|
|
|
66
|
-
const
|
|
61
|
+
const HeaderButtons = observer(({ model }: { model: LGV }) => {
|
|
67
62
|
const classes = useStyles()
|
|
68
63
|
return (
|
|
69
64
|
<Button
|
|
@@ -78,7 +73,7 @@ const Controls = observer(({ model }: { model: LinearGenomeViewModel }) => {
|
|
|
78
73
|
)
|
|
79
74
|
})
|
|
80
75
|
|
|
81
|
-
function PanControls({ model }: { model:
|
|
76
|
+
function PanControls({ model }: { model: LGV }) {
|
|
82
77
|
const classes = useStyles()
|
|
83
78
|
return (
|
|
84
79
|
<>
|
|
@@ -100,118 +95,41 @@ function PanControls({ model }: { model: LinearGenomeViewModel }) {
|
|
|
100
95
|
)
|
|
101
96
|
}
|
|
102
97
|
|
|
103
|
-
const RegionWidth = observer(({ model }: { model:
|
|
98
|
+
const RegionWidth = observer(({ model }: { model: LGV }) => {
|
|
104
99
|
const classes = useStyles()
|
|
105
100
|
const { coarseTotalBp } = model
|
|
106
101
|
return (
|
|
107
102
|
<Typography variant="body2" color="textSecondary" className={classes.bp}>
|
|
108
|
-
{
|
|
103
|
+
{Math.round(coarseTotalBp).toLocaleString('en-US')} bp
|
|
109
104
|
</Typography>
|
|
110
105
|
)
|
|
111
106
|
})
|
|
112
107
|
|
|
113
|
-
const
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
const textSearchResults = await textSearchManager?.search(
|
|
131
|
-
{
|
|
132
|
-
queryString: query,
|
|
133
|
-
searchType,
|
|
134
|
-
},
|
|
135
|
-
searchScope,
|
|
136
|
-
rankSearchResults,
|
|
137
|
-
)
|
|
138
|
-
|
|
139
|
-
const refNameResults = assembly?.allRefNames
|
|
140
|
-
?.filter(refName => refName.startsWith(query))
|
|
141
|
-
.map(r => new BaseResult({ label: r }))
|
|
142
|
-
.slice(0, 10)
|
|
143
|
-
|
|
144
|
-
return dedupe(
|
|
145
|
-
[...(refNameResults || []), ...(textSearchResults || [])],
|
|
146
|
-
elt => elt.getId(),
|
|
147
|
-
)
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
async function handleSelectedRegion(option: BaseResult) {
|
|
151
|
-
let trackId = option.getTrackId()
|
|
152
|
-
let location = option.getLocation()
|
|
153
|
-
const label = option.getLabel()
|
|
154
|
-
try {
|
|
155
|
-
if (assembly?.allRefNames?.includes(location)) {
|
|
156
|
-
model.navToLocString(location)
|
|
157
|
-
} else {
|
|
158
|
-
const results = await fetchResults(label, 'exact')
|
|
159
|
-
if (results && results.length > 1) {
|
|
160
|
-
model.setSearchResults(results, label.toLowerCase())
|
|
161
|
-
return
|
|
162
|
-
} else if (results?.length === 1) {
|
|
163
|
-
location = results[0].getLocation()
|
|
164
|
-
trackId = results[0].getTrackId()
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
model.navToLocString(location, assemblyName)
|
|
168
|
-
if (trackId) {
|
|
169
|
-
model.showTrack(trackId)
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
} catch (e) {
|
|
173
|
-
console.error(e)
|
|
174
|
-
session.notify(`${e}`, 'warning')
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
const controls = (
|
|
179
|
-
<div className={classes.headerBar}>
|
|
180
|
-
<Controls model={model} />
|
|
181
|
-
<div className={classes.spacer} />
|
|
182
|
-
<FormGroup row className={classes.headerForm}>
|
|
183
|
-
<PanControls model={model} />
|
|
184
|
-
<RefNameAutocomplete
|
|
185
|
-
onSelect={handleSelectedRegion}
|
|
186
|
-
assemblyName={assemblyName}
|
|
187
|
-
fetchResults={fetchResults}
|
|
188
|
-
model={model}
|
|
189
|
-
TextFieldProps={{
|
|
190
|
-
variant: 'outlined',
|
|
191
|
-
className: classes.headerRefName,
|
|
192
|
-
style: { margin: SPACING, minWidth: '175px' },
|
|
193
|
-
InputProps: {
|
|
194
|
-
style: {
|
|
195
|
-
padding: 0,
|
|
196
|
-
height: WIDGET_HEIGHT,
|
|
197
|
-
background: alpha(theme.palette.background.paper, 0.8),
|
|
198
|
-
},
|
|
199
|
-
},
|
|
200
|
-
}}
|
|
201
|
-
/>
|
|
202
|
-
</FormGroup>
|
|
203
|
-
<RegionWidth model={model} />
|
|
204
|
-
<ZoomControls model={model} />
|
|
205
|
-
<div className={classes.spacer} />
|
|
206
|
-
</div>
|
|
207
|
-
)
|
|
208
|
-
|
|
209
|
-
if (model.hideHeaderOverview) {
|
|
210
|
-
return controls
|
|
211
|
-
}
|
|
108
|
+
const Controls = ({ model }: { model: LGV }) => {
|
|
109
|
+
const classes = useStyles()
|
|
110
|
+
return (
|
|
111
|
+
<div className={classes.headerBar}>
|
|
112
|
+
<HeaderButtons model={model} />
|
|
113
|
+
<div className={classes.spacer} />
|
|
114
|
+
<FormGroup row className={classes.headerForm}>
|
|
115
|
+
<PanControls model={model} />
|
|
116
|
+
<SearchBox model={model} />
|
|
117
|
+
</FormGroup>
|
|
118
|
+
<RegionWidth model={model} />
|
|
119
|
+
<ZoomControls model={model} />
|
|
120
|
+
<div className={classes.spacer} />
|
|
121
|
+
</div>
|
|
122
|
+
)
|
|
123
|
+
}
|
|
212
124
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
125
|
+
const LinearGenomeViewHeader = observer(({ model }: { model: LGV }) => {
|
|
126
|
+
return model.hideHeaderOverview ? (
|
|
127
|
+
<Controls model={model} />
|
|
128
|
+
) : (
|
|
129
|
+
<OverviewScaleBar model={model}>
|
|
130
|
+
<Controls model={model} />
|
|
131
|
+
</OverviewScaleBar>
|
|
132
|
+
)
|
|
133
|
+
})
|
|
216
134
|
|
|
217
135
|
export default LinearGenomeViewHeader
|
|
@@ -9,9 +9,9 @@ import {
|
|
|
9
9
|
makeStyles,
|
|
10
10
|
} from '@material-ui/core'
|
|
11
11
|
import { SearchType } from '@jbrowse/core/data_adapters/BaseAdapter'
|
|
12
|
+
import ErrorMessage from '@jbrowse/core/ui/ErrorMessage'
|
|
12
13
|
import BaseResult from '@jbrowse/core/TextSearch/BaseResults'
|
|
13
14
|
import AssemblySelector from '@jbrowse/core/ui/AssemblySelector'
|
|
14
|
-
import ErrorMessage from '@jbrowse/core/ui/ErrorMessage'
|
|
15
15
|
import CloseIcon from '@material-ui/icons/Close'
|
|
16
16
|
|
|
17
17
|
// locals
|
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
2
|
import { fireEvent, render, waitFor } from '@testing-library/react'
|
|
3
|
+
import '@testing-library/jest-dom/extend-expect'
|
|
3
4
|
import { createTestSession } from '@jbrowse/web/src/rootModel'
|
|
4
|
-
import sizeMe from 'react-sizeme'
|
|
5
5
|
import 'requestidlecallback-polyfill'
|
|
6
6
|
import LinearGenomeView from './LinearGenomeView'
|
|
7
7
|
|
|
8
|
-
sizeMe.noPlaceholders = true
|
|
9
|
-
|
|
10
8
|
const assemblyConf = {
|
|
11
9
|
name: 'volMyt1',
|
|
12
10
|
sequence: {
|
|
@@ -142,9 +140,13 @@ describe('<LinearGenomeView />', () => {
|
|
|
142
140
|
})
|
|
143
141
|
const model = session.views[0]
|
|
144
142
|
model.setWidth(800)
|
|
145
|
-
const { container, findByText } = render(
|
|
143
|
+
const { container, findByText, findAllByTestId } = render(
|
|
144
|
+
<LinearGenomeView model={model} />,
|
|
145
|
+
)
|
|
146
146
|
await findByText('Foo Track')
|
|
147
147
|
await findByText('798 bp')
|
|
148
|
+
await findAllByTestId('svgfeatures')
|
|
149
|
+
|
|
148
150
|
expect(container.firstChild).toMatchSnapshot()
|
|
149
151
|
})
|
|
150
152
|
})
|
|
@@ -66,6 +66,7 @@ const MyPopper = function (
|
|
|
66
66
|
|
|
67
67
|
function RefNameAutocomplete({
|
|
68
68
|
model,
|
|
69
|
+
showHelp = true,
|
|
69
70
|
onSelect,
|
|
70
71
|
assemblyName,
|
|
71
72
|
style,
|
|
@@ -81,6 +82,7 @@ function RefNameAutocomplete({
|
|
|
81
82
|
fetchResults: (query: string) => Promise<BaseResult[]>
|
|
82
83
|
style?: React.CSSProperties
|
|
83
84
|
minWidth?: number
|
|
85
|
+
showHelp?: boolean
|
|
84
86
|
TextFieldProps?: TFP
|
|
85
87
|
}) {
|
|
86
88
|
const session = getSession(model)
|
|
@@ -238,11 +240,13 @@ function RefNameAutocomplete({
|
|
|
238
240
|
) : (
|
|
239
241
|
<InputAdornment position="end" style={{ marginRight: 7 }}>
|
|
240
242
|
<SearchIcon />
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
243
|
+
{showHelp ? (
|
|
244
|
+
<IconButton
|
|
245
|
+
onClick={() => setHelpDialogDisplayed(true)}
|
|
246
|
+
>
|
|
247
|
+
<HelpIcon />
|
|
248
|
+
</IconButton>
|
|
249
|
+
) : null}
|
|
246
250
|
</InputAdornment>
|
|
247
251
|
)}
|
|
248
252
|
{params.InputProps.endAdornment}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { observer } from 'mobx-react'
|
|
3
|
+
import { makeStyles, useTheme, alpha } from '@material-ui/core'
|
|
4
|
+
import BaseResult from '@jbrowse/core/TextSearch/BaseResults'
|
|
5
|
+
import { getSession } from '@jbrowse/core/util'
|
|
6
|
+
import { SearchType } from '@jbrowse/core/data_adapters/BaseAdapter'
|
|
7
|
+
|
|
8
|
+
// locals
|
|
9
|
+
import RefNameAutocomplete from './RefNameAutocomplete'
|
|
10
|
+
import { dedupe } from './util'
|
|
11
|
+
import { LinearGenomeViewModel, SPACING, WIDGET_HEIGHT } from '..'
|
|
12
|
+
|
|
13
|
+
const useStyles = makeStyles(() => ({
|
|
14
|
+
headerRefName: {
|
|
15
|
+
minWidth: 100,
|
|
16
|
+
},
|
|
17
|
+
}))
|
|
18
|
+
|
|
19
|
+
function SearchBox({
|
|
20
|
+
model,
|
|
21
|
+
showHelp,
|
|
22
|
+
}: {
|
|
23
|
+
showHelp?: boolean
|
|
24
|
+
model: LinearGenomeViewModel
|
|
25
|
+
}) {
|
|
26
|
+
const classes = useStyles()
|
|
27
|
+
const theme = useTheme()
|
|
28
|
+
const session = getSession(model)
|
|
29
|
+
|
|
30
|
+
const { textSearchManager, assemblyManager } = session
|
|
31
|
+
const { assemblyNames, rankSearchResults } = model
|
|
32
|
+
const assemblyName = assemblyNames[0]
|
|
33
|
+
const assembly = assemblyManager.get(assemblyName)
|
|
34
|
+
const searchScope = model.searchScope(assemblyName)
|
|
35
|
+
|
|
36
|
+
async function fetchResults(query: string, searchType?: SearchType) {
|
|
37
|
+
if (!textSearchManager) {
|
|
38
|
+
console.warn('No text search manager')
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const textSearchResults = await textSearchManager?.search(
|
|
42
|
+
{
|
|
43
|
+
queryString: query,
|
|
44
|
+
searchType,
|
|
45
|
+
},
|
|
46
|
+
searchScope,
|
|
47
|
+
rankSearchResults,
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
const refNameResults = assembly?.allRefNames
|
|
51
|
+
?.filter(refName => refName.startsWith(query))
|
|
52
|
+
.map(r => new BaseResult({ label: r }))
|
|
53
|
+
.slice(0, 10)
|
|
54
|
+
|
|
55
|
+
return dedupe(
|
|
56
|
+
[...(refNameResults || []), ...(textSearchResults || [])],
|
|
57
|
+
elt => elt.getId(),
|
|
58
|
+
)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
async function handleSelectedRegion(option: BaseResult) {
|
|
62
|
+
let trackId = option.getTrackId()
|
|
63
|
+
let location = option.getLocation()
|
|
64
|
+
const label = option.getLabel()
|
|
65
|
+
try {
|
|
66
|
+
if (assembly?.allRefNames?.includes(location)) {
|
|
67
|
+
model.navToLocString(location)
|
|
68
|
+
} else {
|
|
69
|
+
const results = await fetchResults(label, 'exact')
|
|
70
|
+
if (results && results.length > 1) {
|
|
71
|
+
model.setSearchResults(results, label.toLowerCase())
|
|
72
|
+
return
|
|
73
|
+
} else if (results?.length === 1) {
|
|
74
|
+
location = results[0].getLocation()
|
|
75
|
+
trackId = results[0].getTrackId()
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
model.navToLocString(location, assemblyName)
|
|
79
|
+
if (trackId) {
|
|
80
|
+
model.showTrack(trackId)
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
} catch (e) {
|
|
84
|
+
console.error(e)
|
|
85
|
+
session.notify(`${e}`, 'warning')
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return (
|
|
89
|
+
<RefNameAutocomplete
|
|
90
|
+
showHelp={showHelp}
|
|
91
|
+
onSelect={handleSelectedRegion}
|
|
92
|
+
assemblyName={assemblyName}
|
|
93
|
+
fetchResults={fetchResults}
|
|
94
|
+
model={model}
|
|
95
|
+
TextFieldProps={{
|
|
96
|
+
variant: 'outlined',
|
|
97
|
+
className: classes.headerRefName,
|
|
98
|
+
style: { margin: SPACING, minWidth: '175px' },
|
|
99
|
+
InputProps: {
|
|
100
|
+
style: {
|
|
101
|
+
padding: 0,
|
|
102
|
+
height: WIDGET_HEIGHT,
|
|
103
|
+
background: alpha(theme.palette.background.paper, 0.8),
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
}}
|
|
107
|
+
/>
|
|
108
|
+
)
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export default observer(SearchBox)
|
|
@@ -1,12 +1,17 @@
|
|
|
1
|
+
import React from 'react'
|
|
1
2
|
import { getConf, readConfObject } from '@jbrowse/core/configuration'
|
|
2
3
|
import { Menu } from '@jbrowse/core/ui'
|
|
3
4
|
import { getSession, getContainingView } from '@jbrowse/core/util'
|
|
4
5
|
import { BaseTrackModel } from '@jbrowse/core/pluggableElementTypes/models'
|
|
5
|
-
import
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
import {
|
|
7
|
+
IconButton,
|
|
8
|
+
Paper,
|
|
9
|
+
Typography,
|
|
10
|
+
alpha,
|
|
11
|
+
makeStyles,
|
|
12
|
+
} from '@material-ui/core'
|
|
13
|
+
|
|
14
|
+
// icons
|
|
10
15
|
import MoreVertIcon from '@material-ui/icons/MoreVert'
|
|
11
16
|
import DragIcon from '@material-ui/icons/DragIndicator'
|
|
12
17
|
import CloseIcon from '@material-ui/icons/Close'
|
|
@@ -14,7 +19,6 @@ import CloseIcon from '@material-ui/icons/Close'
|
|
|
14
19
|
import clsx from 'clsx'
|
|
15
20
|
import { observer } from 'mobx-react'
|
|
16
21
|
import { Instance } from 'mobx-state-tree'
|
|
17
|
-
import React from 'react'
|
|
18
22
|
import { LinearGenomeViewStateModel } from '..'
|
|
19
23
|
|
|
20
24
|
const useStyles = makeStyles(theme => ({
|
|
@@ -255,7 +255,8 @@ exports[`<LinearGenomeView /> renders one track, one region 1`] = `
|
|
|
255
255
|
<p
|
|
256
256
|
class="MuiTypography-root makeStyles-bp MuiTypography-body2 MuiTypography-colorTextSecondary"
|
|
257
257
|
>
|
|
258
|
-
100
|
|
258
|
+
100
|
|
259
|
+
bp
|
|
259
260
|
</p>
|
|
260
261
|
<div
|
|
261
262
|
class="makeStyles-container"
|
|
@@ -555,15 +556,8 @@ exports[`<LinearGenomeView /> renders one track, one region 1`] = `
|
|
|
555
556
|
style="width: 100px;"
|
|
556
557
|
>
|
|
557
558
|
<div
|
|
558
|
-
style="
|
|
559
|
-
|
|
560
|
-
<svg
|
|
561
|
-
class="SvgFeatureRendering"
|
|
562
|
-
height="100"
|
|
563
|
-
style="display: block;"
|
|
564
|
-
width="100"
|
|
565
|
-
/>
|
|
566
|
-
</div>
|
|
559
|
+
style="display: flex;"
|
|
560
|
+
/>
|
|
567
561
|
</div>
|
|
568
562
|
<div
|
|
569
563
|
class="makeStyles-boundaryPaddingBlock"
|
|
@@ -858,7 +852,8 @@ exports[`<LinearGenomeView /> renders two tracks, two regions 1`] = `
|
|
|
858
852
|
<p
|
|
859
853
|
class="MuiTypography-root makeStyles-bp MuiTypography-body2 MuiTypography-colorTextSecondary"
|
|
860
854
|
>
|
|
861
|
-
798
|
|
855
|
+
798
|
|
856
|
+
bp
|
|
862
857
|
</p>
|
|
863
858
|
<div
|
|
864
859
|
class="makeStyles-container"
|
|
@@ -1457,6 +1452,7 @@ exports[`<LinearGenomeView /> renders two tracks, two regions 1`] = `
|
|
|
1457
1452
|
>
|
|
1458
1453
|
<svg
|
|
1459
1454
|
class="SvgFeatureRendering"
|
|
1455
|
+
data-testid="svgfeatures"
|
|
1460
1456
|
height="100"
|
|
1461
1457
|
style="display: block;"
|
|
1462
1458
|
width="100"
|
|
@@ -1476,6 +1472,7 @@ exports[`<LinearGenomeView /> renders two tracks, two regions 1`] = `
|
|
|
1476
1472
|
>
|
|
1477
1473
|
<svg
|
|
1478
1474
|
class="SvgFeatureRendering"
|
|
1475
|
+
data-testid="svgfeatures"
|
|
1479
1476
|
height="100"
|
|
1480
1477
|
style="display: block;"
|
|
1481
1478
|
width="1000"
|
|
@@ -1609,6 +1606,7 @@ exports[`<LinearGenomeView /> renders two tracks, two regions 1`] = `
|
|
|
1609
1606
|
>
|
|
1610
1607
|
<svg
|
|
1611
1608
|
class="SvgFeatureRendering"
|
|
1609
|
+
data-testid="svgfeatures"
|
|
1612
1610
|
height="100"
|
|
1613
1611
|
style="display: block;"
|
|
1614
1612
|
width="100"
|
|
@@ -1628,6 +1626,7 @@ exports[`<LinearGenomeView /> renders two tracks, two regions 1`] = `
|
|
|
1628
1626
|
>
|
|
1629
1627
|
<svg
|
|
1630
1628
|
class="SvgFeatureRendering"
|
|
1629
|
+
data-testid="svgfeatures"
|
|
1631
1630
|
height="100"
|
|
1632
1631
|
style="display: block;"
|
|
1633
1632
|
width="1000"
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
measureText,
|
|
15
15
|
parseLocString,
|
|
16
16
|
springAnimate,
|
|
17
|
+
viewBpToPx,
|
|
17
18
|
} from '@jbrowse/core/util'
|
|
18
19
|
import BaseResult from '@jbrowse/core/TextSearch/BaseResults'
|
|
19
20
|
import { BlockSet, BaseBlock } from '@jbrowse/core/util/blockTypes'
|
|
@@ -50,6 +51,7 @@ import MenuOpenIcon from '@material-ui/icons/MenuOpen'
|
|
|
50
51
|
// locals
|
|
51
52
|
import { renderToSvg } from './components/LinearGenomeViewSvg'
|
|
52
53
|
import RefNameAutocomplete from './components/RefNameAutocomplete'
|
|
54
|
+
import SearchBox from './components/SearchBox'
|
|
53
55
|
import ExportSvgDlg from './components/ExportSvgDialog'
|
|
54
56
|
import ReturnToImportFormDlg from './components/ReturnToImportFormDialog'
|
|
55
57
|
|
|
@@ -99,6 +101,8 @@ export const HEADER_OVERVIEW_HEIGHT = 20
|
|
|
99
101
|
export const SCALE_BAR_HEIGHT = 17
|
|
100
102
|
export const RESIZE_HANDLE_HEIGHT = 3
|
|
101
103
|
export const INTER_REGION_PADDING_WIDTH = 2
|
|
104
|
+
export const WIDGET_HEIGHT = 32
|
|
105
|
+
export const SPACING = 7
|
|
102
106
|
|
|
103
107
|
export function stateModelFactory(pluginManager: PluginManager) {
|
|
104
108
|
return types
|
|
@@ -122,9 +126,18 @@ export function stateModelFactory(pluginManager: PluginManager) {
|
|
|
122
126
|
types.enumeration(['hierarchical']),
|
|
123
127
|
'hierarchical',
|
|
124
128
|
),
|
|
125
|
-
trackLabels:
|
|
126
|
-
|
|
127
|
-
|
|
129
|
+
trackLabels: types.optional(
|
|
130
|
+
types.string,
|
|
131
|
+
() => localStorage.getItem('lgv-trackLabels') || 'overlapping',
|
|
132
|
+
),
|
|
133
|
+
showCenterLine: types.optional(types.boolean, () => {
|
|
134
|
+
const setting = localStorage.getItem('lgv-showCenterLine')
|
|
135
|
+
return setting !== undefined ? !!setting : false
|
|
136
|
+
}),
|
|
137
|
+
showCytobandsSetting: types.optional(types.boolean, () => {
|
|
138
|
+
const setting = localStorage.getItem('lgv-showCytobands')
|
|
139
|
+
return setting !== undefined ? !!setting : true
|
|
140
|
+
}),
|
|
128
141
|
}),
|
|
129
142
|
)
|
|
130
143
|
.volatile(() => ({
|
|
@@ -213,11 +226,7 @@ export function stateModelFactory(pluginManager: PluginManager) {
|
|
|
213
226
|
)
|
|
214
227
|
},
|
|
215
228
|
get totalBp() {
|
|
216
|
-
|
|
217
|
-
self.displayedRegions.forEach(region => {
|
|
218
|
-
totalbp += region.end - region.start
|
|
219
|
-
})
|
|
220
|
-
return totalbp
|
|
229
|
+
return self.displayedRegions.reduce((a, b) => a + b.end - b.start, 0)
|
|
221
230
|
},
|
|
222
231
|
|
|
223
232
|
get maxBpPerPx() {
|
|
@@ -288,47 +297,7 @@ export function stateModelFactory(pluginManager: PluginManager) {
|
|
|
288
297
|
coord: number
|
|
289
298
|
regionNumber?: number
|
|
290
299
|
}) {
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
const interRegionPaddingBp = self.interRegionPaddingWidth * self.bpPerPx
|
|
294
|
-
const minimumBlockBp = self.minimumBlockWidth * self.bpPerPx
|
|
295
|
-
const index = self.displayedRegions.findIndex((region, idx) => {
|
|
296
|
-
const len = region.end - region.start
|
|
297
|
-
if (
|
|
298
|
-
refName === region.refName &&
|
|
299
|
-
coord >= region.start &&
|
|
300
|
-
coord <= region.end
|
|
301
|
-
) {
|
|
302
|
-
if (regionNumber ? regionNumber === idx : true) {
|
|
303
|
-
offsetBp += region.reversed
|
|
304
|
-
? region.end - coord
|
|
305
|
-
: coord - region.start
|
|
306
|
-
return true
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
// add the interRegionPaddingWidth if the boundary is in the screen
|
|
311
|
-
// e.g. offset>=0 && offset<width
|
|
312
|
-
if (
|
|
313
|
-
len > minimumBlockBp &&
|
|
314
|
-
offsetBp / self.bpPerPx >= 0 &&
|
|
315
|
-
offsetBp / self.bpPerPx < self.width
|
|
316
|
-
) {
|
|
317
|
-
offsetBp += len + interRegionPaddingBp
|
|
318
|
-
} else {
|
|
319
|
-
offsetBp += len
|
|
320
|
-
}
|
|
321
|
-
return false
|
|
322
|
-
})
|
|
323
|
-
const foundRegion = self.displayedRegions[index]
|
|
324
|
-
if (foundRegion) {
|
|
325
|
-
return {
|
|
326
|
-
index,
|
|
327
|
-
offsetPx: Math.round(offsetBp / self.bpPerPx),
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
return undefined
|
|
300
|
+
return viewBpToPx({ refName, coord, regionNumber, self })
|
|
332
301
|
},
|
|
333
302
|
/**
|
|
334
303
|
*
|
|
@@ -474,6 +443,7 @@ export function stateModelFactory(pluginManager: PluginManager) {
|
|
|
474
443
|
.actions(self => ({
|
|
475
444
|
setShowCytobands(flag: boolean) {
|
|
476
445
|
self.showCytobandsSetting = flag
|
|
446
|
+
localStorage.setItem('lgv-showCytobands', `${+flag}`)
|
|
477
447
|
},
|
|
478
448
|
setWidth(newWidth: number) {
|
|
479
449
|
self.volatileWidth = newWidth
|
|
@@ -566,11 +536,11 @@ export function stateModelFactory(pluginManager: PluginManager) {
|
|
|
566
536
|
trackId,
|
|
567
537
|
)
|
|
568
538
|
if (!configuration) {
|
|
569
|
-
throw new Error(`Could not resolve identifier`)
|
|
539
|
+
throw new Error(`Could not resolve identifier "${trackId}"`)
|
|
570
540
|
}
|
|
571
541
|
const trackType = pluginManager.getTrackType(configuration?.type)
|
|
572
542
|
if (!trackType) {
|
|
573
|
-
throw new Error(`
|
|
543
|
+
throw new Error(`Unknown track type ${configuration.type}`)
|
|
574
544
|
}
|
|
575
545
|
const viewType = pluginManager.getViewType(self.type)
|
|
576
546
|
const supportedDisplays = viewType.displayTypes.map(
|
|
@@ -581,7 +551,7 @@ export function stateModelFactory(pluginManager: PluginManager) {
|
|
|
581
551
|
)
|
|
582
552
|
if (!displayConf) {
|
|
583
553
|
throw new Error(
|
|
584
|
-
`
|
|
554
|
+
`Could not find a compatible display for view type ${self.type}`,
|
|
585
555
|
)
|
|
586
556
|
}
|
|
587
557
|
|
|
@@ -666,10 +636,12 @@ export function stateModelFactory(pluginManager: PluginManager) {
|
|
|
666
636
|
|
|
667
637
|
setTrackLabels(setting: 'overlapping' | 'offset' | 'hidden') {
|
|
668
638
|
self.trackLabels = setting
|
|
639
|
+
localStorage.setItem('lgv-trackLabels', setting)
|
|
669
640
|
},
|
|
670
641
|
|
|
671
642
|
toggleCenterLine() {
|
|
672
643
|
self.showCenterLine = !self.showCenterLine
|
|
644
|
+
localStorage.setItem('lgv-showCenterLine', `${+self.showCenterLine}`)
|
|
673
645
|
},
|
|
674
646
|
|
|
675
647
|
setDisplayedRegions(regions: Region[]) {
|
|
@@ -1456,7 +1428,7 @@ export function stateModelFactory(pluginManager: PluginManager) {
|
|
|
1456
1428
|
}))
|
|
1457
1429
|
}
|
|
1458
1430
|
|
|
1459
|
-
export { renderToSvg, RefNameAutocomplete }
|
|
1431
|
+
export { renderToSvg, RefNameAutocomplete, SearchBox }
|
|
1460
1432
|
export type LinearGenomeViewStateModel = ReturnType<typeof stateModelFactory>
|
|
1461
1433
|
export type LinearGenomeViewModel = Instance<LinearGenomeViewStateModel>
|
|
1462
1434
|
export { default as ReactComponent } from './components/LinearGenomeView'
|