@jbrowse/plugin-circular-view 2.3.1 → 2.3.3
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/CircularView/components/CircularView.js +31 -80
- package/dist/CircularView/components/CircularView.js.map +1 -1
- package/dist/CircularView/components/Controls.d.ts +6 -0
- package/dist/CircularView/components/Controls.js +54 -0
- package/dist/CircularView/components/Controls.js.map +1 -0
- package/dist/CircularView/components/ImportForm.js +7 -5
- package/dist/CircularView/components/ImportForm.js.map +1 -1
- package/dist/CircularView/models/CircularView.d.ts +20 -10
- package/dist/CircularView/models/CircularView.js +26 -23
- package/dist/CircularView/models/CircularView.js.map +1 -1
- package/esm/CircularView/components/CircularView.js +32 -81
- package/esm/CircularView/components/CircularView.js.map +1 -1
- package/esm/CircularView/components/Controls.d.ts +6 -0
- package/esm/CircularView/components/Controls.js +49 -0
- package/esm/CircularView/components/Controls.js.map +1 -0
- package/esm/CircularView/components/ImportForm.js +7 -5
- package/esm/CircularView/components/ImportForm.js.map +1 -1
- package/esm/CircularView/models/CircularView.d.ts +20 -10
- package/esm/CircularView/models/CircularView.js +26 -23
- package/esm/CircularView/models/CircularView.js.map +1 -1
- package/package.json +2 -2
- package/src/CircularView/components/CircularView.tsx +71 -178
- package/src/CircularView/components/Controls.tsx +106 -0
- package/src/CircularView/components/ImportForm.tsx +9 -6
- package/src/CircularView/models/CircularView.ts +442 -426
|
@@ -1,22 +1,12 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
2
|
import { observer } from 'mobx-react'
|
|
3
|
-
import {
|
|
4
|
-
import { ResizeHandle, ErrorMessage } from '@jbrowse/core/ui'
|
|
3
|
+
import { ResizeHandle } from '@jbrowse/core/ui'
|
|
5
4
|
import { assembleLocString } from '@jbrowse/core/util'
|
|
6
5
|
import { makeStyles } from 'tss-react/mui'
|
|
7
|
-
import { grey } from '@mui/material/colors'
|
|
8
|
-
|
|
9
|
-
// icons
|
|
10
|
-
import ZoomOutIcon from '@mui/icons-material/ZoomOut'
|
|
11
|
-
import ZoomInIcon from '@mui/icons-material/ZoomIn'
|
|
12
|
-
import RotateLeftIcon from '@mui/icons-material/RotateLeft'
|
|
13
|
-
import RotateRightIcon from '@mui/icons-material/RotateRight'
|
|
14
|
-
import LockOpenIcon from '@mui/icons-material/LockOpen'
|
|
15
|
-
import LockIcon from '@mui/icons-material/Lock'
|
|
16
|
-
import { TrackSelector as TrackSelectorIcon } from '@jbrowse/core/ui/Icons'
|
|
17
6
|
|
|
18
7
|
// locals
|
|
19
8
|
import Ruler from './Ruler'
|
|
9
|
+
import Controls from './Controls'
|
|
20
10
|
import ImportForm from './ImportForm'
|
|
21
11
|
import { CircularViewModel } from '../models/CircularView'
|
|
22
12
|
|
|
@@ -38,24 +28,6 @@ const useStyles = makeStyles()(theme => ({
|
|
|
38
28
|
boxSizing: 'content-box',
|
|
39
29
|
display: 'block',
|
|
40
30
|
},
|
|
41
|
-
iconButton: {
|
|
42
|
-
padding: '4px',
|
|
43
|
-
margin: '0 2px 0 2px',
|
|
44
|
-
},
|
|
45
|
-
controls: {
|
|
46
|
-
overflow: 'hidden',
|
|
47
|
-
whiteSpace: 'nowrap',
|
|
48
|
-
position: 'absolute',
|
|
49
|
-
background: grey[200],
|
|
50
|
-
boxSizing: 'border-box',
|
|
51
|
-
borderRight: '1px solid #a2a2a2',
|
|
52
|
-
borderBottom: '1px solid #a2a2a2',
|
|
53
|
-
left: 0,
|
|
54
|
-
top: 0,
|
|
55
|
-
},
|
|
56
|
-
importFormContainer: {
|
|
57
|
-
marginBottom: theme.spacing(4),
|
|
58
|
-
},
|
|
59
31
|
}))
|
|
60
32
|
|
|
61
33
|
const Slices = observer(({ model }: { model: CircularViewModel }) => {
|
|
@@ -85,163 +57,84 @@ const Slices = observer(({ model }: { model: CircularViewModel }) => {
|
|
|
85
57
|
)
|
|
86
58
|
})
|
|
87
59
|
|
|
88
|
-
const Controls = observer(function ({
|
|
89
|
-
model,
|
|
90
|
-
showingFigure,
|
|
91
|
-
}: {
|
|
92
|
-
model: CircularViewModel
|
|
93
|
-
showingFigure: boolean
|
|
94
|
-
}) {
|
|
95
|
-
const { classes } = useStyles()
|
|
96
|
-
return (
|
|
97
|
-
<div className={classes.controls}>
|
|
98
|
-
<IconButton
|
|
99
|
-
onClick={model.zoomOutButton}
|
|
100
|
-
className={classes.iconButton}
|
|
101
|
-
title={model.lockedFitToWindow ? 'unlock to zoom out' : 'zoom out'}
|
|
102
|
-
disabled={
|
|
103
|
-
!showingFigure || model.atMaxBpPerPx || model.lockedFitToWindow
|
|
104
|
-
}
|
|
105
|
-
color="secondary"
|
|
106
|
-
>
|
|
107
|
-
<ZoomOutIcon />
|
|
108
|
-
</IconButton>
|
|
109
|
-
|
|
110
|
-
<IconButton
|
|
111
|
-
onClick={model.zoomInButton}
|
|
112
|
-
className={classes.iconButton}
|
|
113
|
-
title="zoom in"
|
|
114
|
-
disabled={!showingFigure || model.atMinBpPerPx}
|
|
115
|
-
color="secondary"
|
|
116
|
-
>
|
|
117
|
-
<ZoomInIcon />
|
|
118
|
-
</IconButton>
|
|
119
|
-
|
|
120
|
-
<IconButton
|
|
121
|
-
onClick={model.rotateCounterClockwiseButton}
|
|
122
|
-
className={classes.iconButton}
|
|
123
|
-
title="rotate counter-clockwise"
|
|
124
|
-
disabled={!showingFigure}
|
|
125
|
-
color="secondary"
|
|
126
|
-
>
|
|
127
|
-
<RotateLeftIcon />
|
|
128
|
-
</IconButton>
|
|
129
|
-
|
|
130
|
-
<IconButton
|
|
131
|
-
onClick={model.rotateClockwiseButton}
|
|
132
|
-
className={classes.iconButton}
|
|
133
|
-
title="rotate clockwise"
|
|
134
|
-
disabled={!showingFigure}
|
|
135
|
-
color="secondary"
|
|
136
|
-
>
|
|
137
|
-
<RotateRightIcon />
|
|
138
|
-
</IconButton>
|
|
139
|
-
|
|
140
|
-
<IconButton
|
|
141
|
-
onClick={model.toggleFitToWindowLock}
|
|
142
|
-
className={classes.iconButton}
|
|
143
|
-
title={
|
|
144
|
-
model.lockedFitToWindow
|
|
145
|
-
? 'locked model to window size'
|
|
146
|
-
: 'unlocked model to zoom further'
|
|
147
|
-
}
|
|
148
|
-
disabled={model.tooSmallToLock}
|
|
149
|
-
color="secondary"
|
|
150
|
-
>
|
|
151
|
-
{model.lockedFitToWindow ? <LockIcon /> : <LockOpenIcon />}
|
|
152
|
-
</IconButton>
|
|
153
|
-
|
|
154
|
-
{model.hideTrackSelectorButton ? null : (
|
|
155
|
-
<IconButton
|
|
156
|
-
onClick={model.activateTrackSelector}
|
|
157
|
-
title="Open track selector"
|
|
158
|
-
data-testid="circular_track_select"
|
|
159
|
-
color="secondary"
|
|
160
|
-
>
|
|
161
|
-
<TrackSelectorIcon />
|
|
162
|
-
</IconButton>
|
|
163
|
-
)}
|
|
164
|
-
</div>
|
|
165
|
-
)
|
|
166
|
-
})
|
|
167
|
-
|
|
168
60
|
const CircularView = observer(({ model }: { model: CircularViewModel }) => {
|
|
169
|
-
const { classes } = useStyles()
|
|
170
61
|
const initialized =
|
|
171
62
|
!!model.displayedRegions.length &&
|
|
172
63
|
!!model.figureWidth &&
|
|
173
|
-
!!model.figureHeight
|
|
64
|
+
!!model.figureHeight &&
|
|
65
|
+
model.initialized
|
|
174
66
|
|
|
175
67
|
const showImportForm = !initialized && !model.disableImportForm
|
|
176
68
|
const showFigure = initialized && !showImportForm
|
|
177
69
|
|
|
178
70
|
return (
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
71
|
+
<>
|
|
72
|
+
{showImportForm || model.error ? (
|
|
73
|
+
<ImportForm model={model} />
|
|
74
|
+
) : showFigure ? (
|
|
75
|
+
<CircularViewLoaded model={model} />
|
|
76
|
+
) : null}
|
|
77
|
+
</>
|
|
78
|
+
)
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
const CircularViewLoaded = observer(function ({
|
|
82
|
+
model,
|
|
83
|
+
}: {
|
|
84
|
+
model: CircularViewModel
|
|
85
|
+
}) {
|
|
86
|
+
const {
|
|
87
|
+
width,
|
|
88
|
+
height,
|
|
89
|
+
id,
|
|
90
|
+
offsetRadians,
|
|
91
|
+
centerXY,
|
|
92
|
+
figureWidth,
|
|
93
|
+
figureHeight,
|
|
94
|
+
hideVerticalResizeHandle,
|
|
95
|
+
} = model
|
|
96
|
+
const { classes } = useStyles()
|
|
97
|
+
return (
|
|
98
|
+
<div className={classes.root} style={{ width, height }} data-testid={id}>
|
|
99
|
+
<div className={classes.scroller} style={{ width, height }}>
|
|
100
|
+
<div
|
|
101
|
+
style={{
|
|
102
|
+
transform: [`rotate(${offsetRadians}rad)`].join(' '),
|
|
103
|
+
transition: 'transform 0.5s',
|
|
104
|
+
transformOrigin: centerXY.map(x => `${x}px`).join(' '),
|
|
105
|
+
}}
|
|
106
|
+
>
|
|
107
|
+
<svg
|
|
108
|
+
style={{
|
|
109
|
+
position: 'absolute',
|
|
110
|
+
left: 0,
|
|
111
|
+
top: 0,
|
|
112
|
+
}}
|
|
113
|
+
className={classes.sliceRoot}
|
|
114
|
+
width={figureWidth + 'px'}
|
|
115
|
+
height={figureHeight + 'px'}
|
|
116
|
+
version="1.1"
|
|
117
|
+
>
|
|
118
|
+
<g transform={`translate(${centerXY})`}>
|
|
119
|
+
<Slices model={model} />
|
|
120
|
+
</g>
|
|
121
|
+
</svg>
|
|
122
|
+
</div>
|
|
123
|
+
</div>
|
|
124
|
+
<Controls model={model} />
|
|
125
|
+
{hideVerticalResizeHandle ? null : (
|
|
126
|
+
<ResizeHandle
|
|
127
|
+
onDrag={model.resizeHeight}
|
|
128
|
+
style={{
|
|
129
|
+
height: dragHandleHeight,
|
|
130
|
+
position: 'absolute',
|
|
131
|
+
bottom: 0,
|
|
132
|
+
left: 0,
|
|
133
|
+
background: '#ccc',
|
|
134
|
+
boxSizing: 'border-box',
|
|
135
|
+
borderTop: '1px solid #fafafa',
|
|
136
|
+
}}
|
|
137
|
+
/>
|
|
245
138
|
)}
|
|
246
139
|
</div>
|
|
247
140
|
)
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { observer } from 'mobx-react'
|
|
3
|
+
import { IconButton } from '@mui/material'
|
|
4
|
+
import { makeStyles } from 'tss-react/mui'
|
|
5
|
+
import { grey } from '@mui/material/colors'
|
|
6
|
+
|
|
7
|
+
// icons
|
|
8
|
+
import ZoomOutIcon from '@mui/icons-material/ZoomOut'
|
|
9
|
+
import ZoomInIcon from '@mui/icons-material/ZoomIn'
|
|
10
|
+
import RotateLeftIcon from '@mui/icons-material/RotateLeft'
|
|
11
|
+
import RotateRightIcon from '@mui/icons-material/RotateRight'
|
|
12
|
+
import LockOpenIcon from '@mui/icons-material/LockOpen'
|
|
13
|
+
import LockIcon from '@mui/icons-material/Lock'
|
|
14
|
+
import { TrackSelector as TrackSelectorIcon } from '@jbrowse/core/ui/Icons'
|
|
15
|
+
|
|
16
|
+
// locals
|
|
17
|
+
import { CircularViewModel } from '../models/CircularView'
|
|
18
|
+
|
|
19
|
+
const useStyles = makeStyles()({
|
|
20
|
+
iconButton: {
|
|
21
|
+
padding: '4px',
|
|
22
|
+
margin: '0 2px 0 2px',
|
|
23
|
+
},
|
|
24
|
+
controls: {
|
|
25
|
+
overflow: 'hidden',
|
|
26
|
+
whiteSpace: 'nowrap',
|
|
27
|
+
position: 'absolute',
|
|
28
|
+
background: grey[200],
|
|
29
|
+
boxSizing: 'border-box',
|
|
30
|
+
borderRight: '1px solid #a2a2a2',
|
|
31
|
+
borderBottom: '1px solid #a2a2a2',
|
|
32
|
+
left: 0,
|
|
33
|
+
top: 0,
|
|
34
|
+
},
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
const Controls = observer(function ({ model }: { model: CircularViewModel }) {
|
|
38
|
+
const { classes } = useStyles()
|
|
39
|
+
return (
|
|
40
|
+
<div className={classes.controls}>
|
|
41
|
+
<IconButton
|
|
42
|
+
onClick={model.zoomOutButton}
|
|
43
|
+
className={classes.iconButton}
|
|
44
|
+
title={model.lockedFitToWindow ? 'unlock to zoom out' : 'zoom out'}
|
|
45
|
+
disabled={model.atMaxBpPerPx || model.lockedFitToWindow}
|
|
46
|
+
color="secondary"
|
|
47
|
+
>
|
|
48
|
+
<ZoomOutIcon />
|
|
49
|
+
</IconButton>
|
|
50
|
+
|
|
51
|
+
<IconButton
|
|
52
|
+
onClick={model.zoomInButton}
|
|
53
|
+
className={classes.iconButton}
|
|
54
|
+
title="zoom in"
|
|
55
|
+
disabled={model.atMinBpPerPx}
|
|
56
|
+
color="secondary"
|
|
57
|
+
>
|
|
58
|
+
<ZoomInIcon />
|
|
59
|
+
</IconButton>
|
|
60
|
+
|
|
61
|
+
<IconButton
|
|
62
|
+
onClick={model.rotateCounterClockwiseButton}
|
|
63
|
+
className={classes.iconButton}
|
|
64
|
+
title="rotate counter-clockwise"
|
|
65
|
+
color="secondary"
|
|
66
|
+
>
|
|
67
|
+
<RotateLeftIcon />
|
|
68
|
+
</IconButton>
|
|
69
|
+
|
|
70
|
+
<IconButton
|
|
71
|
+
onClick={model.rotateClockwiseButton}
|
|
72
|
+
className={classes.iconButton}
|
|
73
|
+
title="rotate clockwise"
|
|
74
|
+
color="secondary"
|
|
75
|
+
>
|
|
76
|
+
<RotateRightIcon />
|
|
77
|
+
</IconButton>
|
|
78
|
+
|
|
79
|
+
<IconButton
|
|
80
|
+
onClick={model.toggleFitToWindowLock}
|
|
81
|
+
className={classes.iconButton}
|
|
82
|
+
title={
|
|
83
|
+
model.lockedFitToWindow
|
|
84
|
+
? 'locked model to window size'
|
|
85
|
+
: 'unlocked model to zoom further'
|
|
86
|
+
}
|
|
87
|
+
disabled={model.tooSmallToLock}
|
|
88
|
+
color="secondary"
|
|
89
|
+
>
|
|
90
|
+
{model.lockedFitToWindow ? <LockIcon /> : <LockOpenIcon />}
|
|
91
|
+
</IconButton>
|
|
92
|
+
|
|
93
|
+
{model.hideTrackSelectorButton ? null : (
|
|
94
|
+
<IconButton
|
|
95
|
+
onClick={model.activateTrackSelector}
|
|
96
|
+
title="Open track selector"
|
|
97
|
+
data-testid="circular_track_select"
|
|
98
|
+
color="secondary"
|
|
99
|
+
>
|
|
100
|
+
<TrackSelectorIcon />
|
|
101
|
+
</IconButton>
|
|
102
|
+
)}
|
|
103
|
+
</div>
|
|
104
|
+
)
|
|
105
|
+
})
|
|
106
|
+
export default Controls
|
|
@@ -7,7 +7,7 @@ import { ErrorMessage, AssemblySelector } from '@jbrowse/core/ui'
|
|
|
7
7
|
|
|
8
8
|
const useStyles = makeStyles()(theme => ({
|
|
9
9
|
importFormContainer: {
|
|
10
|
-
|
|
10
|
+
padding: theme.spacing(6),
|
|
11
11
|
},
|
|
12
12
|
}))
|
|
13
13
|
|
|
@@ -15,10 +15,9 @@ const useStyles = makeStyles()(theme => ({
|
|
|
15
15
|
const ImportForm = observer(({ model }: { model: any }) => {
|
|
16
16
|
const { classes } = useStyles()
|
|
17
17
|
const session = getSession(model)
|
|
18
|
-
const { error
|
|
18
|
+
const { error } = model
|
|
19
19
|
const { assemblyNames, assemblyManager } = session
|
|
20
20
|
const [selectedAsm, setSelectedAsm] = useState(assemblyNames[0])
|
|
21
|
-
const [error, setError] = useState<Error | undefined>(modelError)
|
|
22
21
|
const assembly = assemblyManager.get(selectedAsm)
|
|
23
22
|
const assemblyError = assemblyNames.length
|
|
24
23
|
? assembly?.error
|
|
@@ -39,7 +38,7 @@ const ImportForm = observer(({ model }: { model: any }) => {
|
|
|
39
38
|
<Grid item>
|
|
40
39
|
<AssemblySelector
|
|
41
40
|
onChange={val => {
|
|
42
|
-
setError(undefined)
|
|
41
|
+
model.setError(undefined)
|
|
43
42
|
setSelectedAsm(val)
|
|
44
43
|
}}
|
|
45
44
|
session={session}
|
|
@@ -50,11 +49,15 @@ const ImportForm = observer(({ model }: { model: any }) => {
|
|
|
50
49
|
<Grid item>
|
|
51
50
|
<Button
|
|
52
51
|
disabled={!regions?.length}
|
|
53
|
-
onClick={() =>
|
|
52
|
+
onClick={() => {
|
|
53
|
+
model.setError(undefined)
|
|
54
|
+
model.setDisplayedRegions(regions)
|
|
55
|
+
}}
|
|
54
56
|
variant="contained"
|
|
55
57
|
color="primary"
|
|
56
58
|
>
|
|
57
|
-
{
|
|
59
|
+
{/* if there's an error, it's not actively loading so just display open */}
|
|
60
|
+
{regions.length || err ? 'Open' : 'Loading...'}
|
|
58
61
|
</Button>
|
|
59
62
|
</Grid>
|
|
60
63
|
</Grid>
|