@jbrowse/plugin-circular-view 2.3.1 → 2.3.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.
@@ -1,22 +1,12 @@
1
1
  import React from 'react'
2
2
  import { observer } from 'mobx-react'
3
- import { IconButton } from '@mui/material'
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
- <div
180
- className={classes.root}
181
- style={{
182
- width: model.width,
183
- height: model.height,
184
- }}
185
- data-testid={model.id}
186
- >
187
- {model.error ? (
188
- <ErrorMessage error={model.error} />
189
- ) : (
190
- <>
191
- {showImportForm ? <ImportForm model={model} /> : null}
192
- <>
193
- {!showFigure ? null : (
194
- <div
195
- className={classes.scroller}
196
- style={{
197
- width: model.width,
198
- height: model.height,
199
- }}
200
- >
201
- <div
202
- style={{
203
- transform: [`rotate(${model.offsetRadians}rad)`].join(' '),
204
- transition: 'transform 0.5s',
205
- transformOrigin: model.centerXY
206
- .map(x => `${x}px`)
207
- .join(' '),
208
- }}
209
- >
210
- <svg
211
- style={{
212
- position: 'absolute',
213
- left: 0,
214
- top: 0,
215
- }}
216
- className={classes.sliceRoot}
217
- width={`${model.figureWidth}px`}
218
- height={`${model.figureHeight}px`}
219
- version="1.1"
220
- >
221
- <g transform={`translate(${model.centerXY})`}>
222
- <Slices model={model} />
223
- </g>
224
- </svg>
225
- </div>
226
- </div>
227
- )}
228
- <Controls model={model} showingFigure={showFigure} />
229
- {model.hideVerticalResizeHandle ? null : (
230
- <ResizeHandle
231
- onDrag={model.resizeHeight}
232
- style={{
233
- height: dragHandleHeight,
234
- position: 'absolute',
235
- bottom: 0,
236
- left: 0,
237
- background: '#ccc',
238
- boxSizing: 'border-box',
239
- borderTop: '1px solid #fafafa',
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
- marginBottom: theme.spacing(4),
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: modelError } = model
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={() => model.setDisplayedRegions(regions)}
52
+ onClick={() => {
53
+ model.setError(undefined)
54
+ model.setDisplayedRegions(regions)
55
+ }}
54
56
  variant="contained"
55
57
  color="primary"
56
58
  >
57
- {regions.length ? 'Open' : 'Loading&hellip;'}
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>