react-msaview 2.0.0 → 2.1.1

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.
Files changed (149) hide show
  1. package/bundle/index.js +35 -33
  2. package/dist/UniprotTrack.js +6 -5
  3. package/dist/UniprotTrack.js.map +1 -1
  4. package/dist/colorSchemes.d.ts +3 -9
  5. package/dist/colorSchemes.js +8 -6
  6. package/dist/colorSchemes.js.map +1 -1
  7. package/dist/components/BoxTrack.d.ts +4 -3
  8. package/dist/components/BoxTrack.js +6 -137
  9. package/dist/components/BoxTrack.js.map +1 -1
  10. package/dist/components/BoxTrackBlock.d.ts +8 -0
  11. package/dist/components/BoxTrackBlock.js +136 -0
  12. package/dist/components/BoxTrackBlock.js.map +1 -0
  13. package/dist/components/Header.d.ts +2 -1
  14. package/dist/components/Header.js +29 -27
  15. package/dist/components/Header.js.map +1 -1
  16. package/dist/components/ImportForm.d.ts +2 -1
  17. package/dist/components/MSABlock.d.ts +8 -0
  18. package/dist/components/MSABlock.js +103 -0
  19. package/dist/components/MSABlock.js.map +1 -0
  20. package/dist/components/MSACanvas.d.ts +2 -1
  21. package/dist/components/MSACanvas.js +3 -101
  22. package/dist/components/MSACanvas.js.map +1 -1
  23. package/dist/components/MSAMouseoverCanvas.d.ts +6 -0
  24. package/dist/components/MSAMouseoverCanvas.js +52 -0
  25. package/dist/components/MSAMouseoverCanvas.js.map +1 -0
  26. package/dist/components/MSAView.d.ts +2 -1
  27. package/dist/components/MSAView.js +10 -36
  28. package/dist/components/MSAView.js.map +1 -1
  29. package/dist/components/MultiAlignmentSelector.d.ts +6 -0
  30. package/dist/components/MultiAlignmentSelector.js +13 -0
  31. package/dist/components/MultiAlignmentSelector.js.map +1 -0
  32. package/dist/components/ResizeHandles.d.ts +3 -2
  33. package/dist/components/Rubberband.d.ts +1 -1
  34. package/dist/components/Rubberband.js +0 -1
  35. package/dist/components/Rubberband.js.map +1 -1
  36. package/dist/components/Ruler.d.ts +2 -1
  37. package/dist/components/Ruler.js +2 -2
  38. package/dist/components/Ruler.js.map +1 -1
  39. package/dist/components/TextTrack.d.ts +2 -1
  40. package/dist/components/Track.d.ts +3 -2
  41. package/dist/components/Track.js +4 -3
  42. package/dist/components/Track.js.map +1 -1
  43. package/dist/components/TreeBranchMenu.d.ts +14 -0
  44. package/dist/components/TreeBranchMenu.js +26 -0
  45. package/dist/components/TreeBranchMenu.js.map +1 -0
  46. package/dist/components/TreeCanvas.d.ts +2 -1
  47. package/dist/components/TreeCanvas.js +2 -320
  48. package/dist/components/TreeCanvas.js.map +1 -1
  49. package/dist/components/TreeCanvasBlock.d.ts +7 -0
  50. package/dist/components/TreeCanvasBlock.js +252 -0
  51. package/dist/components/TreeCanvasBlock.js.map +1 -0
  52. package/dist/components/TreeMenu.d.ts +12 -0
  53. package/dist/components/TreeMenu.js +56 -0
  54. package/dist/components/TreeMenu.js.map +1 -0
  55. package/dist/components/TreeRuler.d.ts +2 -1
  56. package/dist/components/VerticalGuide.d.ts +2 -1
  57. package/dist/components/ZoomControls.d.ts +6 -0
  58. package/dist/components/ZoomControls.js +58 -0
  59. package/dist/components/ZoomControls.js.map +1 -0
  60. package/dist/components/dialogs/AboutDlg.d.ts +4 -0
  61. package/dist/components/{AboutDlg.js → dialogs/AboutDlg.js} +3 -3
  62. package/dist/components/dialogs/AboutDlg.js.map +1 -0
  63. package/dist/components/{AddTrackDlg.d.ts → dialogs/AddTrackDlg.d.ts} +3 -2
  64. package/dist/components/dialogs/AddTrackDlg.js.map +1 -0
  65. package/dist/components/{AnnotationDlg.d.ts → dialogs/AnnotationDlg.d.ts} +3 -2
  66. package/dist/components/dialogs/AnnotationDlg.js.map +1 -0
  67. package/dist/components/dialogs/DetailsDlg.d.ts +7 -0
  68. package/dist/components/{DetailsDlg.js → dialogs/DetailsDlg.js} +3 -2
  69. package/dist/components/dialogs/DetailsDlg.js.map +1 -0
  70. package/dist/components/{MoreInfoDlg.d.ts → dialogs/MoreInfoDlg.d.ts} +2 -1
  71. package/dist/components/dialogs/MoreInfoDlg.js.map +1 -0
  72. package/dist/components/dialogs/SettingsDlg.d.ts +7 -0
  73. package/dist/components/{SettingsDlg.js → dialogs/SettingsDlg.js} +4 -3
  74. package/dist/components/dialogs/SettingsDlg.js.map +1 -0
  75. package/dist/components/{TrackInfoDlg.d.ts → dialogs/TrackInfoDlg.d.ts} +2 -1
  76. package/dist/components/dialogs/TrackInfoDlg.js.map +1 -0
  77. package/dist/components/dialogs/TracklistDlg.d.ts +7 -0
  78. package/dist/components/{TracklistDlg.js → dialogs/TracklistDlg.js} +2 -2
  79. package/dist/components/dialogs/TracklistDlg.js.map +1 -0
  80. package/dist/components/util.js +0 -1
  81. package/dist/components/util.js.map +1 -1
  82. package/dist/model.d.ts +36 -39
  83. package/dist/model.js +25 -22
  84. package/dist/model.js.map +1 -1
  85. package/dist/parseNewick.js +2 -2
  86. package/dist/parseNewick.js.map +1 -1
  87. package/dist/parsers/FastaMSA.d.ts +1 -3
  88. package/dist/parsers/FastaMSA.js +2 -2
  89. package/dist/parsers/FastaMSA.js.map +1 -1
  90. package/dist/parsers/StockholmMSA.d.ts +3 -5
  91. package/dist/parsers/StockholmMSA.js +3 -3
  92. package/dist/parsers/StockholmMSA.js.map +1 -1
  93. package/dist/util.d.ts +5 -7
  94. package/dist/util.js +4 -2
  95. package/dist/util.js.map +1 -1
  96. package/dist/version.d.ts +1 -1
  97. package/dist/version.js +1 -1
  98. package/dist/version.js.map +1 -1
  99. package/package.json +9 -9
  100. package/src/UniprotTrack.ts +6 -7
  101. package/src/colorSchemes.ts +12 -9
  102. package/src/components/BoxTrack.tsx +6 -198
  103. package/src/components/BoxTrackBlock.tsx +198 -0
  104. package/src/components/Header.tsx +49 -60
  105. package/src/components/MSABlock.tsx +164 -0
  106. package/src/components/MSACanvas.tsx +3 -158
  107. package/src/components/MSAMouseoverCanvas.tsx +87 -0
  108. package/src/components/MSAView.tsx +19 -63
  109. package/src/components/MultiAlignmentSelector.tsx +33 -0
  110. package/src/components/Rubberband.tsx +0 -1
  111. package/src/components/Ruler.tsx +2 -1
  112. package/src/components/Track.tsx +9 -6
  113. package/src/components/TreeBranchMenu.tsx +67 -0
  114. package/src/components/TreeCanvas.tsx +2 -507
  115. package/src/components/TreeCanvasBlock.tsx +359 -0
  116. package/src/components/TreeMenu.tsx +105 -0
  117. package/src/components/ZoomControls.tsx +78 -0
  118. package/src/components/{AboutDlg.tsx → dialogs/AboutDlg.tsx} +3 -9
  119. package/src/components/{AddTrackDlg.tsx → dialogs/AddTrackDlg.tsx} +1 -1
  120. package/src/components/{AnnotationDlg.tsx → dialogs/AnnotationDlg.tsx} +2 -2
  121. package/src/components/{DetailsDlg.tsx → dialogs/DetailsDlg.tsx} +5 -5
  122. package/src/components/{SettingsDlg.tsx → dialogs/SettingsDlg.tsx} +6 -6
  123. package/src/components/{TracklistDlg.tsx → dialogs/TracklistDlg.tsx} +2 -4
  124. package/src/components/util.ts +0 -1
  125. package/src/declare.d.ts +0 -1
  126. package/src/model.ts +32 -29
  127. package/src/parseNewick.ts +2 -3
  128. package/src/parsers/FastaMSA.ts +3 -3
  129. package/src/parsers/StockholmMSA.ts +5 -5
  130. package/src/util.ts +8 -5
  131. package/src/version.ts +1 -1
  132. package/dist/components/AboutDlg.d.ts +0 -4
  133. package/dist/components/AboutDlg.js.map +0 -1
  134. package/dist/components/AddTrackDlg.js.map +0 -1
  135. package/dist/components/AnnotationDlg.js.map +0 -1
  136. package/dist/components/DetailsDlg.d.ts +0 -7
  137. package/dist/components/DetailsDlg.js.map +0 -1
  138. package/dist/components/MoreInfoDlg.js.map +0 -1
  139. package/dist/components/SettingsDlg.d.ts +0 -7
  140. package/dist/components/SettingsDlg.js.map +0 -1
  141. package/dist/components/TrackInfoDlg.js.map +0 -1
  142. package/dist/components/TracklistDlg.d.ts +0 -7
  143. package/dist/components/TracklistDlg.js.map +0 -1
  144. /package/dist/components/{AddTrackDlg.js → dialogs/AddTrackDlg.js} +0 -0
  145. /package/dist/components/{AnnotationDlg.js → dialogs/AnnotationDlg.js} +0 -0
  146. /package/dist/components/{MoreInfoDlg.js → dialogs/MoreInfoDlg.js} +0 -0
  147. /package/dist/components/{TrackInfoDlg.js → dialogs/TrackInfoDlg.js} +0 -0
  148. /package/src/components/{MoreInfoDlg.tsx → dialogs/MoreInfoDlg.tsx} +0 -0
  149. /package/src/components/{TrackInfoDlg.tsx → dialogs/TrackInfoDlg.tsx} +0 -0
@@ -0,0 +1,359 @@
1
+ import React, { useEffect, useRef, useState } from 'react'
2
+ import { observer } from 'mobx-react'
3
+ import RBush from 'rbush'
4
+
5
+ // locals
6
+ import { MsaViewModel } from '../model'
7
+ import TreeMenu from './TreeMenu'
8
+ import TreeBranchMenu from './TreeBranchMenu'
9
+
10
+ const extendBounds = 5
11
+ const radius = 3.5
12
+ const d = radius * 2
13
+
14
+ const padding = 600
15
+
16
+ interface TooltipData {
17
+ name: string
18
+ id: string
19
+ x: number
20
+ y: number
21
+ }
22
+
23
+ interface ClickEntry {
24
+ name: string
25
+ id: string
26
+ branch?: boolean
27
+ minX: number
28
+ maxX: number
29
+ minY: number
30
+ maxY: number
31
+ }
32
+
33
+ const TreeCanvasBlock = observer(function ({
34
+ model,
35
+ offsetY,
36
+ }: {
37
+ model: MsaViewModel
38
+ offsetY: number
39
+ }) {
40
+ const ref = useRef<HTMLCanvasElement>(null)
41
+ const clickMap = useRef(new RBush<ClickEntry>())
42
+ const mouseoverRef = useRef<HTMLCanvasElement>(null)
43
+ const [branchMenu, setBranchMenu] = useState<TooltipData>()
44
+ const [toggleNodeMenu, setToggleNodeMenu] = useState<TooltipData>()
45
+ const [hoverElt, setHoverElt] = useState<ClickEntry>()
46
+
47
+ const {
48
+ hierarchy,
49
+ rowHeight,
50
+ scrollY,
51
+ treeWidth,
52
+ showBranchLen,
53
+ collapsed,
54
+ margin,
55
+ labelsAlignRight,
56
+ noTree,
57
+ blockSize,
58
+ drawNodeBubbles,
59
+ drawTree,
60
+ treeAreaWidth,
61
+ structures,
62
+ highResScaleFactor,
63
+ } = model
64
+
65
+ useEffect(() => {
66
+ clickMap.current.clear()
67
+
68
+ if (!ref.current) {
69
+ return
70
+ }
71
+ const ctx = ref.current.getContext('2d')
72
+ if (!ctx) {
73
+ return
74
+ }
75
+
76
+ ctx.resetTransform()
77
+ ctx.scale(highResScaleFactor, highResScaleFactor)
78
+ ctx.clearRect(0, 0, treeWidth + padding, blockSize)
79
+ ctx.translate(margin.left, -offsetY)
80
+
81
+ const font = ctx.font
82
+ ctx.font = font.replace(/\d+px/, `${Math.max(8, rowHeight - 8)}px`)
83
+
84
+ if (!noTree && drawTree) {
85
+ for (const { source, target } of hierarchy.links()) {
86
+ const y = showBranchLen ? 'len' : 'y'
87
+ // @ts-expect-error
88
+ const { x: sy, [y]: sx } = source
89
+ // @ts-expect-error
90
+ const { x: ty, [y]: tx } = target
91
+
92
+ const y1 = Math.min(sy, ty)
93
+ const y2 = Math.max(sy, ty)
94
+ // 1d line intersection to check if line crosses block at all, this is
95
+ // an optimization that allows us to skip drawing most tree links
96
+ // outside the block
97
+ if (offsetY + blockSize >= y1 && y2 >= offsetY) {
98
+ ctx.beginPath()
99
+ ctx.moveTo(sx, sy)
100
+ ctx.lineTo(sx, ty)
101
+ ctx.lineTo(tx, ty)
102
+ ctx.stroke()
103
+ }
104
+ }
105
+
106
+ if (drawNodeBubbles) {
107
+ for (const node of hierarchy.descendants()) {
108
+ const val = showBranchLen ? 'len' : 'y'
109
+ const {
110
+ // @ts-expect-error
111
+ x: y,
112
+ // @ts-expect-error
113
+ [val]: x,
114
+ data,
115
+ } = node
116
+ const { id = '', name = '' } = data
117
+
118
+ if (
119
+ y > offsetY - extendBounds &&
120
+ y < offsetY + blockSize + extendBounds
121
+ ) {
122
+ ctx.strokeStyle = 'black'
123
+ ctx.fillStyle = collapsed.includes(id) ? 'black' : 'white'
124
+ ctx.beginPath()
125
+ ctx.arc(x, y, radius, 0, 2 * Math.PI)
126
+ ctx.fill()
127
+ ctx.stroke()
128
+
129
+ clickMap.current.insert({
130
+ minX: x - radius,
131
+ maxX: x - radius + d,
132
+ minY: y - radius,
133
+ maxY: y - radius + d,
134
+ branch: true,
135
+ id,
136
+ name,
137
+ })
138
+ }
139
+ }
140
+ }
141
+ }
142
+
143
+ if (rowHeight >= 5) {
144
+ if (labelsAlignRight) {
145
+ ctx.textAlign = 'right'
146
+ ctx.setLineDash([1, 3])
147
+ } else {
148
+ ctx.textAlign = 'start'
149
+ }
150
+ for (const node of hierarchy.leaves()) {
151
+ const {
152
+ // @ts-expect-error
153
+ x: y,
154
+ // @ts-expect-error
155
+ y: x,
156
+ data: { name, id },
157
+ // @ts-expect-error
158
+ len,
159
+ } = node
160
+
161
+ if (
162
+ y > offsetY - extendBounds &&
163
+ y < offsetY + blockSize + extendBounds
164
+ ) {
165
+ // note: +rowHeight/4 matches with -rowHeight/4 in msa
166
+ const yp = y + rowHeight / 4
167
+ const xp = showBranchLen ? len : x
168
+
169
+ const { width } = ctx.measureText(name)
170
+ const height = ctx.measureText('M').width // use an 'em' for height
171
+
172
+ const hasStructure = structures[name]
173
+ ctx.fillStyle = hasStructure ? 'blue' : 'black'
174
+
175
+ if (!drawTree && !labelsAlignRight) {
176
+ ctx.fillText(name, 0, yp)
177
+ clickMap.current.insert({
178
+ minX: 0,
179
+ maxX: width,
180
+ minY: yp - height,
181
+ maxY: yp,
182
+ name,
183
+ id,
184
+ })
185
+ } else if (labelsAlignRight) {
186
+ const smallPadding = 2
187
+ const offset = treeAreaWidth - smallPadding - margin.left
188
+ if (drawTree && !noTree) {
189
+ const { width } = ctx.measureText(name)
190
+ ctx.moveTo(xp + radius + 2, y)
191
+ ctx.lineTo(offset - smallPadding - width, y)
192
+ ctx.stroke()
193
+ }
194
+ ctx.fillText(name, offset, yp)
195
+ clickMap.current.insert({
196
+ minX: treeAreaWidth - margin.left - width,
197
+ maxX: treeAreaWidth - margin.left,
198
+ minY: yp - height,
199
+ maxY: yp,
200
+ name,
201
+ id,
202
+ })
203
+ } else {
204
+ ctx.fillText(name, xp + d, yp)
205
+ clickMap.current.insert({
206
+ minX: xp + d,
207
+ maxX: xp + d + width,
208
+ minY: yp - height,
209
+ maxY: yp,
210
+ name,
211
+ id,
212
+ })
213
+ }
214
+ }
215
+ }
216
+ ctx.setLineDash([])
217
+ }
218
+ }, [
219
+ collapsed,
220
+ rowHeight,
221
+ margin.left,
222
+ hierarchy,
223
+ offsetY,
224
+ treeWidth,
225
+ showBranchLen,
226
+ noTree,
227
+ blockSize,
228
+ drawNodeBubbles,
229
+ drawTree,
230
+ labelsAlignRight,
231
+ treeAreaWidth,
232
+ structures,
233
+ highResScaleFactor,
234
+ ])
235
+
236
+ useEffect(() => {
237
+ const canvas = mouseoverRef.current
238
+ if (!canvas) {
239
+ return
240
+ }
241
+ const ctx = canvas.getContext('2d')
242
+ if (!ctx) {
243
+ return
244
+ }
245
+
246
+ ctx.resetTransform()
247
+ ctx.clearRect(0, 0, treeWidth + padding, blockSize)
248
+ ctx.translate(margin.left, -offsetY)
249
+
250
+ if (hoverElt) {
251
+ const { minX, maxX, minY, maxY } = hoverElt
252
+
253
+ ctx.fillStyle = 'rgba(0,0,0,0.1)'
254
+ ctx.fillRect(minX, minY, maxX - minX, maxY - minY)
255
+ }
256
+ }, [hoverElt, margin.left, offsetY, blockSize, treeWidth])
257
+
258
+ function hoverBranchClickMap(event: React.MouseEvent) {
259
+ const x = event.nativeEvent.offsetX - margin.left
260
+ const y = event.nativeEvent.offsetY
261
+
262
+ const [entry] = clickMap.current.search({
263
+ minX: x,
264
+ maxX: x + 1,
265
+ minY: y + offsetY,
266
+ maxY: y + 1 + offsetY,
267
+ })
268
+
269
+ return entry && entry.branch
270
+ ? { ...entry, x: event.clientX, y: event.clientY }
271
+ : undefined
272
+ }
273
+
274
+ function hoverNameClickMap(event: React.MouseEvent) {
275
+ const x = event.nativeEvent.offsetX - margin.left
276
+ const y = event.nativeEvent.offsetY
277
+ const [entry] = clickMap.current.search({
278
+ minX: x,
279
+ maxX: x + 1,
280
+ minY: y + offsetY,
281
+ maxY: y + 1 + offsetY,
282
+ })
283
+
284
+ return entry && !entry.branch
285
+ ? { ...entry, x: event.clientX, y: event.clientY }
286
+ : undefined
287
+ }
288
+
289
+ return (
290
+ <>
291
+ {branchMenu?.id ? (
292
+ <TreeBranchMenu
293
+ node={branchMenu}
294
+ model={model}
295
+ onClose={() => setBranchMenu(undefined)}
296
+ />
297
+ ) : null}
298
+
299
+ {toggleNodeMenu?.id ? (
300
+ <TreeMenu
301
+ node={toggleNodeMenu}
302
+ model={model}
303
+ onClose={() => setToggleNodeMenu(undefined)}
304
+ />
305
+ ) : null}
306
+
307
+ <canvas
308
+ width={(treeWidth + padding) * highResScaleFactor}
309
+ height={blockSize * highResScaleFactor}
310
+ style={{
311
+ width: treeWidth + padding,
312
+ height: blockSize,
313
+ top: scrollY + offsetY,
314
+ left: 0,
315
+ position: 'absolute',
316
+ }}
317
+ onMouseMove={event => {
318
+ if (!ref.current) {
319
+ return
320
+ }
321
+
322
+ const ret = hoverNameClickMap(event) || hoverBranchClickMap(event)
323
+ ref.current.style.cursor = ret ? 'pointer' : 'default'
324
+ setHoverElt(hoverNameClickMap(event))
325
+ }}
326
+ onClick={event => {
327
+ const { clientX: x, clientY: y } = event
328
+
329
+ const data = hoverBranchClickMap(event)
330
+ if (data?.id) {
331
+ setBranchMenu({ ...data, x, y })
332
+ }
333
+
334
+ const data2 = hoverNameClickMap(event)
335
+ if (data2?.id) {
336
+ setToggleNodeMenu({ ...data2, x, y })
337
+ }
338
+ }}
339
+ ref={ref}
340
+ />
341
+ <canvas
342
+ style={{
343
+ width: treeWidth + padding,
344
+ height: blockSize,
345
+ top: scrollY + offsetY,
346
+ left: 0,
347
+ position: 'absolute',
348
+ pointerEvents: 'none',
349
+ zIndex: 100,
350
+ }}
351
+ width={treeWidth + padding}
352
+ height={blockSize}
353
+ ref={mouseoverRef}
354
+ />
355
+ </>
356
+ )
357
+ })
358
+
359
+ export default TreeCanvasBlock
@@ -0,0 +1,105 @@
1
+ import React, { lazy } from 'react'
2
+ import { Menu, MenuItem } from '@mui/material'
3
+ import { observer } from 'mobx-react'
4
+
5
+ // locals
6
+ import { MsaViewModel } from '../model'
7
+
8
+ const MoreInfoDlg = lazy(() => import('./dialogs/MoreInfoDlg'))
9
+
10
+ const TreeMenu = observer(function ({
11
+ node,
12
+ onClose,
13
+ model,
14
+ }: {
15
+ node: { x: number; y: number; name: string }
16
+ model: MsaViewModel
17
+ onClose: () => void
18
+ }) {
19
+ const { structures } = model
20
+ const nodeDetails = node ? model.getRowData(node.name) : undefined
21
+
22
+ return (
23
+ <>
24
+ <Menu
25
+ anchorReference="anchorPosition"
26
+ anchorPosition={{
27
+ top: node.y,
28
+ left: node.x,
29
+ }}
30
+ transitionDuration={0}
31
+ keepMounted
32
+ open={Boolean(node)}
33
+ onClose={onClose}
34
+ >
35
+ <MenuItem dense disabled>
36
+ {node.name}
37
+ </MenuItem>
38
+
39
+ <MenuItem
40
+ dense
41
+ onClick={() => {
42
+ model.setDialogComponent(MoreInfoDlg, {
43
+ info: model.getRowData(node.name),
44
+ })
45
+ onClose()
46
+ }}
47
+ >
48
+ More info...
49
+ </MenuItem>
50
+
51
+ {structures[node.name]?.map(entry => {
52
+ return !model.selectedStructures.some(n => n.id === node.name) ? (
53
+ <MenuItem
54
+ key={JSON.stringify(entry)}
55
+ dense
56
+ onClick={() => {
57
+ model.addStructureToSelection({
58
+ structure: entry,
59
+ id: node.name,
60
+ })
61
+ onClose()
62
+ }}
63
+ >
64
+ Add PDB to selection ({entry.pdb})
65
+ </MenuItem>
66
+ ) : (
67
+ <MenuItem
68
+ key={JSON.stringify(entry)}
69
+ dense
70
+ onClick={() => {
71
+ model.removeStructureFromSelection({
72
+ structure: entry,
73
+ id: node.name,
74
+ })
75
+ onClose()
76
+ }}
77
+ >
78
+ Remove PDB from selection ({entry.pdb})
79
+ </MenuItem>
80
+ )
81
+ })}
82
+
83
+ {// @ts-expect-error
84
+ nodeDetails?.data.accession?.map(accession => (
85
+ <MenuItem
86
+ dense
87
+ key={accession}
88
+ onClick={() => {
89
+ model.addUniprotTrack({
90
+ // @ts-expect-error
91
+ name: nodeDetails?.data.name,
92
+ accession,
93
+ })
94
+ onClose()
95
+ }}
96
+ >
97
+ Open UniProt track ({accession})
98
+ </MenuItem>
99
+ ))}
100
+ </Menu>
101
+ </>
102
+ )
103
+ })
104
+
105
+ export default TreeMenu
@@ -0,0 +1,78 @@
1
+ import React from 'react'
2
+ import { IconButton } from '@mui/material'
3
+ import { observer } from 'mobx-react'
4
+ import CascadingMenuButton from '@jbrowse/core/ui/CascadingMenuButton'
5
+
6
+ // locals
7
+ import { MsaViewModel } from '../model'
8
+
9
+ // icons
10
+ import MoreVert from '@mui/icons-material/MoreVert'
11
+ import ZoomIn from '@mui/icons-material/ZoomIn'
12
+ import ZoomOut from '@mui/icons-material/ZoomOut'
13
+
14
+ const ZoomControls = observer(function ZoomControls({
15
+ model,
16
+ }: {
17
+ model: MsaViewModel
18
+ }) {
19
+ const { colWidth, rowHeight } = model
20
+ return (
21
+ <>
22
+ <IconButton
23
+ onClick={() => {
24
+ model.setColWidth(Math.ceil(colWidth * 1.5))
25
+ model.setRowHeight(Math.ceil(rowHeight * 1.5))
26
+ }}
27
+ >
28
+ <ZoomIn />
29
+ </IconButton>
30
+ <IconButton
31
+ onClick={() => {
32
+ model.setColWidth(Math.max(1, Math.floor(colWidth * 0.75)))
33
+ model.setRowHeight(Math.max(1.5, Math.floor(rowHeight * 0.75)))
34
+ }}
35
+ >
36
+ <ZoomOut />
37
+ </IconButton>
38
+ <CascadingMenuButton
39
+ menuItems={[
40
+ {
41
+ label: 'Decrease row height',
42
+ onClick: () => {
43
+ model.setRowHeight(Math.max(1.5, rowHeight * 0.75))
44
+ },
45
+ },
46
+ {
47
+ label: 'Increase row height',
48
+ onClick: () => {
49
+ model.setRowHeight(rowHeight * 1.5)
50
+ },
51
+ },
52
+ {
53
+ label: 'Decrease col width',
54
+ onClick: () => {
55
+ model.setColWidth(Math.max(1, colWidth * 0.75))
56
+ },
57
+ },
58
+ {
59
+ label: 'Increase col width',
60
+ onClick: () => {
61
+ model.setColWidth(colWidth * 1.5)
62
+ },
63
+ },
64
+ {
65
+ label: 'Reset zoom to default',
66
+ onClick: () => {
67
+ model.setColWidth(16)
68
+ model.setRowHeight(20)
69
+ },
70
+ },
71
+ ]}
72
+ >
73
+ <MoreVert />
74
+ </CascadingMenuButton>
75
+ </>
76
+ )
77
+ })
78
+ export default ZoomControls
@@ -1,17 +1,11 @@
1
1
  import React from 'react'
2
2
  import { Dialog } from '@jbrowse/core/ui'
3
3
  import { DialogContent, Typography, Link } from '@mui/material'
4
- import { version } from '../version'
4
+ import { version } from '../../version'
5
5
 
6
- export default function AboutDialog({
7
- onClose,
8
- open,
9
- }: {
10
- onClose: () => void
11
- open: boolean
12
- }) {
6
+ export default function AboutDialog({ onClose }: { onClose: () => void }) {
13
7
  return (
14
- <Dialog onClose={() => onClose()} open={open} title="About this plugin">
8
+ <Dialog onClose={() => onClose()} open title="About this plugin">
15
9
  <DialogContent>
16
10
  <Typography>
17
11
  MSAView {version} (
@@ -10,7 +10,7 @@ import {
10
10
  import { Dialog, FileSelector } from '@jbrowse/core/ui'
11
11
  import { FileLocation } from '@jbrowse/core/util/types'
12
12
  import { observer } from 'mobx-react'
13
- import { MsaViewModel } from '../model'
13
+ import { MsaViewModel } from '../../model'
14
14
 
15
15
  export default observer(function ({
16
16
  model,
@@ -14,10 +14,10 @@ import { Dialog } from '@jbrowse/core/ui'
14
14
  import DeleteIcon from '@mui/icons-material/Delete'
15
15
 
16
16
  // locals
17
- import { MsaViewModel } from '../model'
17
+ import { MsaViewModel } from '../../model'
18
18
 
19
19
  const specialFromEntries = (val: string[][]) => {
20
- const ret = {} as { [key: string]: string[] }
20
+ const ret = {} as Record<string, string[]>
21
21
  val.forEach(([key, val]) => {
22
22
  if (!ret[key]) {
23
23
  ret[key] = [] as string[]
@@ -5,24 +5,24 @@ import { Dialog } from '@jbrowse/core/ui'
5
5
  import { Attributes } from '@jbrowse/core/BaseFeatureWidget/BaseFeatureDetail'
6
6
 
7
7
  // locals
8
- import { MsaViewModel } from '../model'
8
+ import { MsaViewModel } from '../../model'
9
9
 
10
- export default observer(function ({
10
+ const DetailsDialog = observer(function ({
11
11
  model,
12
12
  onClose,
13
- open,
14
13
  }: {
15
14
  model: MsaViewModel
16
15
  onClose: () => void
17
- open: boolean
18
16
  }) {
19
17
  const { header } = model
20
18
 
21
19
  return (
22
- <Dialog onClose={() => onClose()} open={open} title="Metadata">
20
+ <Dialog onClose={() => onClose()} open title="Metadata">
23
21
  <DialogContent>
24
22
  <Attributes attributes={header} />
25
23
  </DialogContent>
26
24
  </Dialog>
27
25
  )
28
26
  })
27
+
28
+ export default DetailsDialog
@@ -12,8 +12,8 @@ import {
12
12
  TextField,
13
13
  } from '@mui/material'
14
14
 
15
- import { MsaViewModel } from '../model'
16
- import colorSchemes from '../colorSchemes'
15
+ import { MsaViewModel } from '../../model'
16
+ import colorSchemes from '../../colorSchemes'
17
17
 
18
18
  const useStyles = makeStyles()(theme => ({
19
19
  field: {
@@ -21,14 +21,12 @@ const useStyles = makeStyles()(theme => ({
21
21
  },
22
22
  }))
23
23
 
24
- export default observer(function ({
24
+ const SettingsDialog = observer(function ({
25
25
  model,
26
26
  onClose,
27
- open,
28
27
  }: {
29
28
  model: MsaViewModel
30
29
  onClose: () => void
31
- open: boolean
32
30
  }) {
33
31
  const { classes } = useStyles()
34
32
  const { colorSchemeName, noTree } = model
@@ -44,7 +42,7 @@ export default observer(function ({
44
42
  const treeWidthError = error(treeWidth)
45
43
 
46
44
  return (
47
- <Dialog onClose={() => onClose()} open={open} title="Settings">
45
+ <Dialog open onClose={() => onClose()} title="Settings">
48
46
  <DialogContent>
49
47
  <FormControlLabel
50
48
  control={
@@ -152,3 +150,5 @@ export default observer(function ({
152
150
  </Dialog>
153
151
  )
154
152
  })
153
+
154
+ export default SettingsDialog
@@ -12,21 +12,19 @@ import {
12
12
  import { observer } from 'mobx-react'
13
13
 
14
14
  // locals
15
- import { MsaViewModel } from '../model'
15
+ import { MsaViewModel } from '../../model'
16
16
 
17
17
  export default observer(function ({
18
18
  model,
19
19
  onClose,
20
- open,
21
20
  }: {
22
21
  model: MsaViewModel
23
22
  onClose: () => void
24
- open: boolean
25
23
  }) {
26
24
  const { tracks } = model
27
25
 
28
26
  return (
29
- <Dialog onClose={() => onClose()} open={open} title="Add track">
27
+ <Dialog onClose={() => onClose()} open title="Add track">
30
28
  <DialogContent>
31
29
  <Typography>
32
30
  Open relevant per-alignment tracks e.g. protein domains
@@ -55,7 +55,6 @@ export function makeTicks(
55
55
  }
56
56
 
57
57
  if (bpPerPx < 0) {
58
- // eslint-disable-next-line @typescript-eslint/no-extra-semi
59
58
  ;[minBase, maxBase] = [maxBase, minBase]
60
59
  }
61
60
 
package/src/declare.d.ts CHANGED
@@ -1,2 +1 @@
1
1
  declare module 'stockholm-js'
2
- declare module 'normalize-wheel'