react-msaview 1.3.1 → 2.0.0

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 (200) hide show
  1. package/bundle/index.js +283 -97265
  2. package/dist/StructureModel.d.ts +9 -0
  3. package/dist/StructureModel.js +11 -0
  4. package/dist/StructureModel.js.map +1 -0
  5. package/dist/UniprotTrack.d.ts +27 -0
  6. package/dist/UniprotTrack.js +52 -0
  7. package/dist/UniprotTrack.js.map +1 -0
  8. package/dist/colorSchemes.d.ts +2 -2
  9. package/dist/colorSchemes.js +24 -29
  10. package/dist/colorSchemes.js.map +1 -0
  11. package/dist/components/AboutDlg.d.ts +0 -1
  12. package/dist/components/AboutDlg.js +38 -48
  13. package/dist/components/AboutDlg.js.map +1 -0
  14. package/dist/components/AddTrackDlg.d.ts +0 -1
  15. package/dist/components/AddTrackDlg.js +13 -13
  16. package/dist/components/AddTrackDlg.js.map +1 -0
  17. package/dist/components/AnnotationDlg.d.ts +0 -1
  18. package/dist/components/AnnotationDlg.js +36 -48
  19. package/dist/components/AnnotationDlg.js.map +1 -0
  20. package/dist/components/BoxTrack.d.ts +3 -4
  21. package/dist/components/BoxTrack.js +51 -48
  22. package/dist/components/BoxTrack.js.map +1 -0
  23. package/dist/components/DetailsDlg.d.ts +0 -1
  24. package/dist/components/DetailsDlg.js +7 -7
  25. package/dist/components/DetailsDlg.js.map +1 -0
  26. package/dist/components/Header.d.ts +0 -1
  27. package/dist/components/Header.js +39 -34
  28. package/dist/components/Header.js.map +1 -0
  29. package/dist/components/ImportForm.d.ts +0 -1
  30. package/dist/components/ImportForm.js +59 -71
  31. package/dist/components/ImportForm.js.map +1 -0
  32. package/dist/components/MSACanvas.d.ts +0 -1
  33. package/dist/components/MSACanvas.js +71 -74
  34. package/dist/components/MSACanvas.js.map +1 -0
  35. package/dist/components/MSAView.d.ts +0 -1
  36. package/dist/components/MSAView.js +19 -38
  37. package/dist/components/MSAView.js.map +1 -0
  38. package/dist/components/MoreInfoDlg.d.ts +2 -3
  39. package/dist/components/MoreInfoDlg.js +5 -5
  40. package/dist/components/MoreInfoDlg.js.map +1 -0
  41. package/dist/components/ResizeHandles.d.ts +2 -3
  42. package/dist/components/ResizeHandles.js +31 -32
  43. package/dist/components/ResizeHandles.js.map +1 -0
  44. package/dist/components/Rubberband.d.ts +2 -1
  45. package/dist/components/Rubberband.js +42 -64
  46. package/dist/components/Rubberband.js.map +1 -0
  47. package/dist/components/Ruler.d.ts +0 -15
  48. package/dist/components/Ruler.js +18 -87
  49. package/dist/components/Ruler.js.map +1 -0
  50. package/dist/components/SettingsDlg.d.ts +0 -1
  51. package/dist/components/SettingsDlg.js +29 -22
  52. package/dist/components/SettingsDlg.js.map +1 -0
  53. package/dist/components/TextTrack.d.ts +3 -4
  54. package/dist/components/TextTrack.js +23 -24
  55. package/dist/components/TextTrack.js.map +1 -0
  56. package/dist/components/Track.d.ts +2 -3
  57. package/dist/components/Track.js +38 -38
  58. package/dist/components/Track.js.map +1 -0
  59. package/dist/components/TrackInfoDlg.d.ts +5 -3
  60. package/dist/components/TrackInfoDlg.js +12 -13
  61. package/dist/components/TrackInfoDlg.js.map +1 -0
  62. package/dist/components/TracklistDlg.d.ts +0 -1
  63. package/dist/components/TracklistDlg.js +9 -9
  64. package/dist/components/TracklistDlg.js.map +1 -0
  65. package/dist/components/TreeCanvas.d.ts +0 -1
  66. package/dist/components/TreeCanvas.js +135 -148
  67. package/dist/components/TreeCanvas.js.map +1 -0
  68. package/dist/components/TreeRuler.d.ts +0 -1
  69. package/dist/components/TreeRuler.js +3 -3
  70. package/dist/components/TreeRuler.js.map +1 -0
  71. package/dist/components/VerticalGuide.d.ts +6 -0
  72. package/dist/components/VerticalGuide.js +30 -0
  73. package/dist/components/VerticalGuide.js.map +1 -0
  74. package/dist/components/data/seq2.d.ts +3 -3
  75. package/dist/components/data/seq2.js +33 -3
  76. package/dist/components/data/seq2.js.map +1 -0
  77. package/{bundle/components/Ruler.d.ts → dist/components/util.d.ts} +1 -6
  78. package/dist/components/util.js +69 -0
  79. package/dist/components/util.js.map +1 -0
  80. package/dist/index.d.ts +2 -4
  81. package/dist/index.js +3 -3
  82. package/dist/index.js.map +1 -0
  83. package/dist/layout.js +14 -20
  84. package/dist/layout.js.map +1 -0
  85. package/dist/model.d.ts +94 -74
  86. package/dist/model.js +232 -473
  87. package/dist/model.js.map +1 -0
  88. package/dist/parseNewick.d.ts +1 -5
  89. package/dist/parseNewick.js +10 -7
  90. package/dist/parseNewick.js.map +1 -0
  91. package/dist/parsers/ClustalMSA.d.ts +6 -18
  92. package/dist/parsers/ClustalMSA.js +55 -64
  93. package/dist/parsers/ClustalMSA.js.map +1 -0
  94. package/dist/parsers/FastaMSA.d.ts +4 -9
  95. package/dist/parsers/FastaMSA.js +55 -64
  96. package/dist/parsers/FastaMSA.js.map +1 -0
  97. package/dist/parsers/StockholmMSA.d.ts +8 -13
  98. package/dist/parsers/StockholmMSA.js +78 -107
  99. package/dist/parsers/StockholmMSA.js.map +1 -0
  100. package/dist/util.d.ts +33 -4
  101. package/dist/util.js +76 -24
  102. package/dist/util.js.map +1 -0
  103. package/dist/version.d.ts +1 -0
  104. package/dist/version.js +2 -0
  105. package/dist/version.js.map +1 -0
  106. package/package.json +30 -30
  107. package/src/StructureModel.ts +11 -0
  108. package/src/UniprotTrack.ts +60 -0
  109. package/src/colorSchemes.ts +520 -0
  110. package/src/components/AboutDlg.tsx +64 -0
  111. package/src/components/AddTrackDlg.tsx +74 -0
  112. package/src/components/AnnotationDlg.tsx +144 -0
  113. package/src/components/BoxTrack.tsx +225 -0
  114. package/src/components/DetailsDlg.tsx +28 -0
  115. package/src/components/Header.tsx +117 -0
  116. package/src/components/ImportForm.tsx +192 -0
  117. package/src/components/MSACanvas.tsx +297 -0
  118. package/src/components/MSAView.tsx +132 -0
  119. package/src/components/MoreInfoDlg.tsx +21 -0
  120. package/src/components/ResizeHandles.tsx +137 -0
  121. package/src/components/Rubberband.tsx +271 -0
  122. package/src/components/Ruler.tsx +122 -0
  123. package/src/components/SettingsDlg.tsx +154 -0
  124. package/src/components/TextTrack.tsx +120 -0
  125. package/src/components/Track.tsx +150 -0
  126. package/src/components/TrackInfoDlg.tsx +59 -0
  127. package/src/components/TracklistDlg.tsx +61 -0
  128. package/src/components/TreeCanvas.tsx +633 -0
  129. package/src/components/TreeRuler.tsx +12 -0
  130. package/src/components/VerticalGuide.tsx +50 -0
  131. package/src/components/data/seq2.ts +35 -0
  132. package/src/components/util.ts +94 -0
  133. package/src/declare.d.ts +2 -0
  134. package/src/index.ts +2 -0
  135. package/src/layout.ts +83 -0
  136. package/src/model.ts +790 -0
  137. package/{bundle/parseNewick.d.ts → src/parseNewick.ts} +36 -5
  138. package/src/parsers/ClustalMSA.ts +79 -0
  139. package/src/parsers/FastaMSA.ts +82 -0
  140. package/src/parsers/StockholmMSA.ts +137 -0
  141. package/src/util.ts +142 -0
  142. package/src/version.ts +1 -0
  143. package/bundle/colorSchemes.d.ts +0 -16
  144. package/bundle/colorSchemes.js +0 -455
  145. package/bundle/components/AboutDlg.d.ts +0 -5
  146. package/bundle/components/AboutDlg.js +0 -47
  147. package/bundle/components/AddTrackDlg.d.ts +0 -8
  148. package/bundle/components/AddTrackDlg.js +0 -26
  149. package/bundle/components/AnnotationDlg.d.ts +0 -11
  150. package/bundle/components/AnnotationDlg.js +0 -77
  151. package/bundle/components/BoxTrack.d.ts +0 -7
  152. package/bundle/components/BoxTrack.js +0 -143
  153. package/bundle/components/DetailsDlg.d.ts +0 -8
  154. package/bundle/components/DetailsDlg.js +0 -12
  155. package/bundle/components/Header.d.ts +0 -6
  156. package/bundle/components/Header.js +0 -63
  157. package/bundle/components/ImportForm.d.ts +0 -6
  158. package/bundle/components/ImportForm.js +0 -89
  159. package/bundle/components/MSACanvas.d.ts +0 -6
  160. package/bundle/components/MSACanvas.js +0 -210
  161. package/bundle/components/MSAView.d.ts +0 -6
  162. package/bundle/components/MSAView.js +0 -88
  163. package/bundle/components/MoreInfoDlg.d.ts +0 -6
  164. package/bundle/components/MoreInfoDlg.js +0 -11
  165. package/bundle/components/ResizeHandles.d.ts +0 -8
  166. package/bundle/components/ResizeHandles.js +0 -110
  167. package/bundle/components/Rubberband.d.ts +0 -7
  168. package/bundle/components/Rubberband.js +0 -196
  169. package/bundle/components/Ruler.js +0 -121
  170. package/bundle/components/SettingsDlg.d.ts +0 -8
  171. package/bundle/components/SettingsDlg.js +0 -40
  172. package/bundle/components/TextTrack.d.ts +0 -7
  173. package/bundle/components/TextTrack.js +0 -72
  174. package/bundle/components/Track.d.ts +0 -11
  175. package/bundle/components/Track.js +0 -81
  176. package/bundle/components/TrackInfoDlg.d.ts +0 -6
  177. package/bundle/components/TrackInfoDlg.js +0 -33
  178. package/bundle/components/TracklistDlg.d.ts +0 -8
  179. package/bundle/components/TracklistDlg.js +0 -18
  180. package/bundle/components/TreeCanvas.d.ts +0 -6
  181. package/bundle/components/TreeCanvas.js +0 -431
  182. package/bundle/components/TreeRuler.d.ts +0 -6
  183. package/bundle/components/TreeRuler.js +0 -8
  184. package/bundle/components/data/seq2.d.ts +0 -3
  185. package/bundle/components/data/seq2.js +0 -3
  186. package/bundle/index.d.ts +0 -4
  187. package/bundle/layout.d.ts +0 -23
  188. package/bundle/layout.js +0 -53
  189. package/bundle/model.d.ts +0 -364
  190. package/bundle/model.js +0 -894
  191. package/bundle/parseNewick.js +0 -94
  192. package/bundle/parsers/ClustalMSA.d.ts +0 -39
  193. package/bundle/parsers/ClustalMSA.js +0 -77
  194. package/bundle/parsers/FastaMSA.d.ts +0 -26
  195. package/bundle/parsers/FastaMSA.js +0 -78
  196. package/bundle/parsers/StockholmMSA.d.ts +0 -75
  197. package/bundle/parsers/StockholmMSA.js +0 -142
  198. package/bundle/util.d.ts +0 -17
  199. package/bundle/util.js +0 -33
  200. package/dist/components/package.json +0 -62
@@ -0,0 +1,297 @@
1
+ import React, { useEffect, useState, useRef, useMemo } from 'react'
2
+ import { Typography, CircularProgress, useTheme } from '@mui/material'
3
+ import { observer } from 'mobx-react'
4
+ import normalizeWheel from 'normalize-wheel'
5
+
6
+ // locals
7
+ import { MsaViewModel } from '../model'
8
+ import { colorContrast } from '../util'
9
+ import { getClustalXColor, getPercentIdentityColor } from '../colorSchemes'
10
+
11
+ const MSABlock = observer(function ({
12
+ model,
13
+ offsetX,
14
+ offsetY,
15
+ }: {
16
+ model: MsaViewModel
17
+ offsetX: number
18
+ offsetY: number
19
+ }) {
20
+ const {
21
+ MSA,
22
+ colWidth,
23
+ bgColor,
24
+ columns,
25
+ rowHeight,
26
+ scrollY,
27
+ scrollX,
28
+ hierarchy,
29
+ colorScheme,
30
+ colorSchemeName,
31
+ blockSize,
32
+ highResScaleFactor,
33
+ colStats,
34
+ } = model
35
+ const theme = useTheme()
36
+
37
+ const contrastScheme = useMemo(
38
+ () => colorContrast(colorScheme, theme),
39
+ [colorScheme, theme],
40
+ )
41
+
42
+ const ref = useRef<HTMLCanvasElement>(null)
43
+ useEffect(() => {
44
+ if (!ref.current) {
45
+ return
46
+ }
47
+
48
+ const ctx = ref.current.getContext('2d')
49
+ if (!ctx) {
50
+ return
51
+ }
52
+
53
+ ctx.resetTransform()
54
+ ctx.scale(highResScaleFactor, highResScaleFactor)
55
+ ctx.clearRect(0, 0, blockSize, blockSize)
56
+ ctx.translate(-offsetX, rowHeight / 2 - offsetY)
57
+ ctx.textAlign = 'center'
58
+ ctx.font = ctx.font.replace(/\d+px/, `${Math.max(8, rowHeight - 8)}px`)
59
+
60
+ const leaves = hierarchy.leaves()
61
+ const b = blockSize
62
+
63
+ // slice vertical rows, e.g. tree leaves, avoid negative slice
64
+ const yStart = Math.max(0, Math.floor((offsetY - rowHeight) / rowHeight))
65
+ const yEnd = Math.max(0, Math.ceil((offsetY + b + rowHeight) / rowHeight))
66
+
67
+ // slice horizontal visible letters, avoid negative slice
68
+ const xStart = Math.max(0, Math.floor(offsetX / colWidth))
69
+ const xEnd = Math.max(0, Math.ceil((offsetX + b) / colWidth))
70
+ const visibleLeaves = leaves.slice(yStart, yEnd)
71
+ visibleLeaves.forEach(node => {
72
+ const {
73
+ // @ts-expect-error
74
+ x: y,
75
+ data: { name },
76
+ } = node
77
+
78
+ const str = columns[name]?.slice(xStart, xEnd)
79
+ for (let i = 0; i < str?.length; i++) {
80
+ const letter = str[i]
81
+ const color =
82
+ colorSchemeName === 'clustalx_protein_dynamic'
83
+ ? getClustalXColor(colStats[xStart + i], model, name, xStart + i)
84
+ : colorSchemeName === 'percent_identity_dynamic'
85
+ ? getPercentIdentityColor(
86
+ colStats[xStart + i],
87
+ model,
88
+ name,
89
+ xStart + i,
90
+ )
91
+ : colorScheme[letter.toUpperCase()]
92
+ if (bgColor) {
93
+ const x = i * colWidth + offsetX - (offsetX % colWidth)
94
+ ctx.fillStyle = color || 'white'
95
+ ctx.fillRect(x, y - rowHeight, colWidth, rowHeight)
96
+ }
97
+ }
98
+ })
99
+
100
+ if (rowHeight >= 10 && colWidth >= rowHeight / 2) {
101
+ visibleLeaves.forEach(node => {
102
+ const {
103
+ // @ts-expect-error
104
+ x: y,
105
+ data: { name },
106
+ } = node
107
+
108
+ const str = columns[name]?.slice(xStart, xEnd)
109
+ for (let i = 0; i < str?.length; i++) {
110
+ const letter = str[i]
111
+ const color = colorScheme[letter.toUpperCase()]
112
+ const contrast = contrastScheme[letter.toUpperCase()] || 'black'
113
+ const x = i * colWidth + offsetX - (offsetX % colWidth)
114
+
115
+ // note: -rowHeight/4 matches +rowHeight/4 in tree
116
+ ctx.fillStyle = bgColor ? contrast : color || 'black'
117
+ ctx.fillText(letter, x + colWidth / 2, y - rowHeight / 4)
118
+ }
119
+ })
120
+ }
121
+ // eslint-disable-next-line react-hooks/exhaustive-deps
122
+ }, [
123
+ MSA,
124
+ highResScaleFactor,
125
+ columns,
126
+ colorScheme,
127
+ contrastScheme,
128
+ bgColor,
129
+ rowHeight,
130
+ colWidth,
131
+ hierarchy,
132
+ offsetX,
133
+ offsetY,
134
+ blockSize,
135
+ ])
136
+ return (
137
+ <canvas
138
+ ref={ref}
139
+ onMouseMove={event => {
140
+ if (!ref.current) {
141
+ return
142
+ }
143
+ const { left, top } = ref.current.getBoundingClientRect()
144
+ const mouseX = event.clientX - left
145
+ const mouseY = event.clientY - top
146
+ model.setMousePos(
147
+ Math.floor((mouseX + offsetX) / colWidth) + 1,
148
+ Math.floor((mouseY + offsetY) / rowHeight),
149
+ )
150
+ }}
151
+ onMouseLeave={() => model.setMousePos()}
152
+ width={blockSize * highResScaleFactor}
153
+ height={blockSize * highResScaleFactor}
154
+ style={{
155
+ position: 'absolute',
156
+ top: scrollY + offsetY,
157
+ left: scrollX + offsetX,
158
+ width: blockSize,
159
+ height: blockSize,
160
+ }}
161
+ />
162
+ )
163
+ })
164
+
165
+ const MSACanvas = observer(function ({ model }: { model: MsaViewModel }) {
166
+ const { MSA, msaFilehandle, height, msaAreaWidth, blocks2d } = model
167
+ const ref = useRef<HTMLDivElement>(null)
168
+ // wheel
169
+ const scheduled = useRef(false)
170
+ const deltaX = useRef(0)
171
+ const deltaY = useRef(0)
172
+ // mouse click-and-drag scrolling
173
+ const prevX = useRef<number>(0)
174
+ const prevY = useRef<number>(0)
175
+ const [mouseDragging, setMouseDragging] = useState(false)
176
+ useEffect(() => {
177
+ const curr = ref.current
178
+ if (!curr) {
179
+ return
180
+ }
181
+ function onWheel(origEvent: WheelEvent) {
182
+ const event = normalizeWheel(origEvent)
183
+ deltaX.current += event.pixelX
184
+ deltaY.current += event.pixelY
185
+
186
+ if (!scheduled.current) {
187
+ scheduled.current = true
188
+ requestAnimationFrame(() => {
189
+ model.doScrollX(-deltaX.current)
190
+ model.doScrollY(-deltaY.current)
191
+ deltaX.current = 0
192
+ deltaY.current = 0
193
+ scheduled.current = false
194
+ })
195
+ }
196
+ origEvent.preventDefault()
197
+ }
198
+ curr.addEventListener('wheel', onWheel)
199
+ return () => {
200
+ curr.removeEventListener('wheel', onWheel)
201
+ }
202
+ }, [model])
203
+
204
+ useEffect(() => {
205
+ let cleanup = () => {}
206
+
207
+ function globalMouseMove(event: MouseEvent) {
208
+ event.preventDefault()
209
+ const currX = event.clientX
210
+ const currY = event.clientY
211
+ const distanceX = currX - prevX.current
212
+ const distanceY = currY - prevY.current
213
+ if (distanceX || distanceY) {
214
+ // use rAF to make it so multiple event handlers aren't fired per-frame
215
+ // see https://calendar.perfplanet.com/2013/the-runtime-performance-checklist/
216
+ if (!scheduled.current) {
217
+ scheduled.current = true
218
+ window.requestAnimationFrame(() => {
219
+ model.doScrollX(distanceX)
220
+ model.doScrollY(distanceY)
221
+ scheduled.current = false
222
+ prevX.current = event.clientX
223
+ prevY.current = event.clientY
224
+ })
225
+ }
226
+ }
227
+ }
228
+
229
+ function globalMouseUp() {
230
+ prevX.current = 0
231
+ if (mouseDragging) {
232
+ setMouseDragging(false)
233
+ }
234
+ }
235
+
236
+ if (mouseDragging) {
237
+ window.addEventListener('mousemove', globalMouseMove, true)
238
+ window.addEventListener('mouseup', globalMouseUp, true)
239
+ cleanup = () => {
240
+ window.removeEventListener('mousemove', globalMouseMove, true)
241
+ window.removeEventListener('mouseup', globalMouseUp, true)
242
+ }
243
+ }
244
+ return cleanup
245
+ }, [model, mouseDragging])
246
+
247
+ return (
248
+ <div
249
+ ref={ref}
250
+ onMouseDown={event => {
251
+ // check if clicking a draggable element or a resize handle
252
+ const target = event.target as HTMLElement
253
+ if (target.draggable || target.dataset.resizer) {
254
+ return
255
+ }
256
+
257
+ // otherwise do click and drag scroll
258
+ if (event.button === 0) {
259
+ prevX.current = event.clientX
260
+ prevY.current = event.clientY
261
+ setMouseDragging(true)
262
+ }
263
+ }}
264
+ onMouseUp={event => {
265
+ event.preventDefault()
266
+ setMouseDragging(false)
267
+ }}
268
+ onMouseLeave={event => {
269
+ event.preventDefault()
270
+ }}
271
+ style={{
272
+ position: 'relative',
273
+ height,
274
+ width: msaAreaWidth,
275
+ overflow: 'hidden',
276
+ }}
277
+ >
278
+ {!MSA && !msaFilehandle ? null : !MSA ? (
279
+ <div style={{ position: 'absolute', left: '50%', top: '50%' }}>
280
+ <CircularProgress />
281
+ <Typography>Loading...</Typography>
282
+ </div>
283
+ ) : (
284
+ blocks2d.map(([bx, by]) => (
285
+ <MSABlock
286
+ key={`${bx}_${by}`}
287
+ model={model}
288
+ offsetX={bx}
289
+ offsetY={by}
290
+ />
291
+ ))
292
+ )}
293
+ </div>
294
+ )
295
+ })
296
+
297
+ export default MSACanvas
@@ -0,0 +1,132 @@
1
+ import React, { useRef, useEffect } from 'react'
2
+
3
+ import { observer } from 'mobx-react'
4
+ import { Typography } from '@mui/material'
5
+
6
+ // locals
7
+ import ImportForm from './ImportForm'
8
+ import Rubberband from './Rubberband'
9
+ import TreeCanvas from './TreeCanvas'
10
+ import MSACanvas from './MSACanvas'
11
+ import Ruler from './Ruler'
12
+ import TreeRuler from './TreeRuler'
13
+ import Header from './Header'
14
+ import Track from './Track'
15
+ import AnnotationDialog from './AnnotationDlg'
16
+
17
+ import { HorizontalResizeHandle, VerticalResizeHandle } from './ResizeHandles'
18
+ import { MsaViewModel } from '../model'
19
+
20
+ const MouseoverCanvas = observer(function ({ model }: { model: MsaViewModel }) {
21
+ const ref = useRef<HTMLCanvasElement>(null)
22
+ const {
23
+ height,
24
+ width,
25
+ treeAreaWidth,
26
+ resizeHandleWidth,
27
+ scrollX,
28
+ mouseCol,
29
+ colWidth,
30
+ } = model
31
+
32
+ useEffect(() => {
33
+ if (!ref.current) {
34
+ return
35
+ }
36
+
37
+ const ctx = ref.current.getContext('2d')
38
+ if (!ctx) {
39
+ return
40
+ }
41
+
42
+ ctx.resetTransform()
43
+ ctx.clearRect(0, 0, width, height)
44
+
45
+ if (mouseCol !== undefined) {
46
+ const x =
47
+ (mouseCol - 1) * colWidth + scrollX + treeAreaWidth + resizeHandleWidth
48
+ ctx.fillStyle = 'rgba(100,100,100,0.5)'
49
+ ctx.fillRect(x, 0, colWidth, height)
50
+ }
51
+ }, [mouseCol, colWidth, scrollX, height, resizeHandleWidth, treeAreaWidth, width])
52
+
53
+ return (
54
+ <canvas
55
+ ref={ref}
56
+ width={width}
57
+ height={height}
58
+ style={{
59
+ position: 'absolute',
60
+ top: 0,
61
+ left: 0,
62
+ width,
63
+ height,
64
+ zIndex: 1000,
65
+ pointerEvents: 'none',
66
+ }}
67
+ />
68
+ )
69
+ })
70
+ export default observer(function ({ model }: { model: MsaViewModel }) {
71
+ const { done, initialized, treeAreaWidth, height, turnedOnTracks } = model
72
+
73
+ return (
74
+ <div>
75
+ {!initialized ? (
76
+ <ImportForm model={model} />
77
+ ) : !done ? (
78
+ <Typography variant="h4">Loading...</Typography>
79
+ ) : (
80
+ <div>
81
+ <div style={{ height, overflow: 'hidden' }}>
82
+ <Header model={model} />
83
+ <div>
84
+ <div style={{ position: 'relative' }}>
85
+ <div style={{ display: 'flex' }}>
86
+ <div style={{ flexShrink: 0, width: treeAreaWidth }}>
87
+ <TreeRuler model={model} />
88
+ </div>
89
+
90
+ <Rubberband
91
+ model={model}
92
+ ControlComponent={<Ruler model={model} />}
93
+ />
94
+ </div>
95
+ {turnedOnTracks?.map(track => (
96
+ <Track key={track.model.id} model={model} track={track} />
97
+ ))}
98
+
99
+ <div style={{ display: 'flex' }}>
100
+ <div style={{ flexShrink: 0, width: treeAreaWidth }}>
101
+ <TreeCanvas model={model} />
102
+ </div>
103
+ <VerticalResizeHandle model={model} />
104
+ <MSACanvas model={model} />
105
+ <MouseoverCanvas model={model} />
106
+ </div>
107
+ </div>
108
+ </div>
109
+ </div>
110
+ <HorizontalResizeHandle model={model} />
111
+ </div>
112
+ )}
113
+
114
+ {model.DialogComponent ? (
115
+ <model.DialogComponent
116
+ {...(model.DialogProps || {})}
117
+ onClose={() => {
118
+ model.setDialogComponent(undefined, undefined)
119
+ }}
120
+ />
121
+ ) : null}
122
+
123
+ {model.annotPos ? (
124
+ <AnnotationDialog
125
+ data={model.annotPos}
126
+ model={model}
127
+ onClose={() => model.clearAnnotPos()}
128
+ />
129
+ ) : null}
130
+ </div>
131
+ )
132
+ })
@@ -0,0 +1,21 @@
1
+ import React from 'react'
2
+ import { Dialog } from '@jbrowse/core/ui'
3
+ import { DialogContent } from '@mui/material'
4
+ import { observer } from 'mobx-react'
5
+ import { Attributes } from '@jbrowse/core/BaseFeatureWidget/BaseFeatureDetail'
6
+
7
+ export default observer(function ({
8
+ info,
9
+ onClose,
10
+ }: {
11
+ info: Record<string, unknown>
12
+ onClose: () => void
13
+ }) {
14
+ return (
15
+ <Dialog onClose={() => onClose()} open title="Metadata">
16
+ <DialogContent>
17
+ <Attributes attributes={info} />
18
+ </DialogContent>
19
+ </Dialog>
20
+ )
21
+ })
@@ -0,0 +1,137 @@
1
+ import React, { useEffect, useRef, useState } from 'react'
2
+ import { observer } from 'mobx-react'
3
+
4
+ // locals
5
+ import { MsaViewModel } from '../model'
6
+
7
+ export const VerticalResizeHandle = observer(function ({
8
+ model,
9
+ }: {
10
+ model: MsaViewModel
11
+ }) {
12
+ const { resizeHandleWidth } = model
13
+ const [mouseDragging, setMouseDragging] = useState(false)
14
+ const scheduled = useRef(false)
15
+ const prevX = useRef(0)
16
+
17
+ useEffect(() => {
18
+ function globalMouseMove(event: MouseEvent) {
19
+ event.preventDefault()
20
+ const currX = event.clientX
21
+ if (prevX.current === 0) {
22
+ prevX.current = event.clientX
23
+ } else {
24
+ const distance = currX - prevX.current
25
+ if (distance) {
26
+ // use rAF to make it so multiple event handlers aren't fired per-frame
27
+ // see https://calendar.perfplanet.com/2013/the-runtime-performance-checklist/
28
+ if (!scheduled.current) {
29
+ scheduled.current = true
30
+ window.requestAnimationFrame(() => {
31
+ model.setTreeAreaWidth(model.treeAreaWidth + distance)
32
+ scheduled.current = false
33
+ prevX.current = event.clientX
34
+ })
35
+ }
36
+ }
37
+ }
38
+ }
39
+
40
+ function globalMouseUp() {
41
+ prevX.current = 0
42
+ if (mouseDragging) {
43
+ setMouseDragging(false)
44
+ }
45
+ }
46
+ if (mouseDragging) {
47
+ document.addEventListener('mousemove', globalMouseMove, true)
48
+ document.addEventListener('mouseup', globalMouseUp, true)
49
+ return () => {
50
+ document.removeEventListener('mousemove', globalMouseMove, true)
51
+ document.removeEventListener('mouseup', globalMouseUp, true)
52
+ }
53
+ }
54
+ return () => {}
55
+ }, [mouseDragging, model])
56
+
57
+ return (
58
+ <div>
59
+ <div
60
+ onMouseDown={() => setMouseDragging(true)}
61
+ style={{
62
+ cursor: 'ew-resize',
63
+ height: '100%',
64
+ width: resizeHandleWidth,
65
+ background: `rgba(200,200,200)`,
66
+ position: 'relative',
67
+ }}
68
+ />
69
+ </div>
70
+ )
71
+ })
72
+
73
+ export const HorizontalResizeHandle = observer(function ({
74
+ model,
75
+ }: {
76
+ model: MsaViewModel
77
+ }) {
78
+ const { resizeHandleWidth } = model
79
+ const [mouseDragging, setMouseDragging] = useState(false)
80
+ const scheduled = useRef(false)
81
+ const prevY = useRef(0)
82
+
83
+ useEffect(() => {
84
+ function globalMouseMove(event: MouseEvent) {
85
+ event.preventDefault()
86
+ const currY = event.clientY
87
+ if (prevY.current === 0) {
88
+ prevY.current = event.clientY
89
+ } else {
90
+ const distance = currY - prevY.current
91
+ if (distance) {
92
+ // use rAF to make it so multiple event handlers aren't fired per-frame
93
+ // see https://calendar.perfplanet.com/2013/the-runtime-performance-checklist/
94
+ if (!scheduled.current) {
95
+ scheduled.current = true
96
+ window.requestAnimationFrame(() => {
97
+ model.setHeight(model.height + distance)
98
+ scheduled.current = false
99
+ prevY.current = event.clientY
100
+ })
101
+ }
102
+ }
103
+ }
104
+ }
105
+
106
+ function globalMouseUp() {
107
+ prevY.current = 0
108
+ if (mouseDragging) {
109
+ setMouseDragging(false)
110
+ }
111
+ }
112
+ if (mouseDragging) {
113
+ document.addEventListener('mousemove', globalMouseMove, true)
114
+ document.addEventListener('mouseup', globalMouseUp, true)
115
+ return () => {
116
+ document.removeEventListener('mousemove', globalMouseMove, true)
117
+ document.removeEventListener('mouseup', globalMouseUp, true)
118
+ }
119
+ }
120
+ return () => {}
121
+ }, [mouseDragging, model])
122
+
123
+ return (
124
+ <div>
125
+ <div
126
+ onMouseDown={() => setMouseDragging(true)}
127
+ style={{
128
+ cursor: 'ns-resize',
129
+ width: '100%',
130
+ height: resizeHandleWidth,
131
+ background: `rgba(200,200,200)`,
132
+ position: 'relative',
133
+ }}
134
+ />
135
+ </div>
136
+ )
137
+ })