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,144 @@
1
+ import React, { useState } from 'react'
2
+ import { observer } from 'mobx-react'
3
+ import {
4
+ Button,
5
+ DialogActions,
6
+ DialogContent,
7
+ IconButton,
8
+ TextField,
9
+ Typography,
10
+ } from '@mui/material'
11
+ import { Dialog } from '@jbrowse/core/ui'
12
+
13
+ // icons
14
+ import DeleteIcon from '@mui/icons-material/Delete'
15
+
16
+ // locals
17
+ import { MsaViewModel } from '../model'
18
+
19
+ const specialFromEntries = (val: string[][]) => {
20
+ const ret = {} as { [key: string]: string[] }
21
+ val.forEach(([key, val]) => {
22
+ if (!ret[key]) {
23
+ ret[key] = [] as string[]
24
+ }
25
+ ret[key].push(val)
26
+ })
27
+ return ret
28
+ }
29
+
30
+ const Row = observer(function ({
31
+ name,
32
+ value,
33
+ setValue,
34
+ setName,
35
+ onDelete,
36
+ }: {
37
+ name: string
38
+ value: string
39
+ setValue: (arg: string) => void
40
+ setName: (arg: string) => void
41
+ onDelete: () => void
42
+ }) {
43
+ return (
44
+ <div>
45
+ <IconButton onClick={onDelete} style={{ margin: 10 }}>
46
+ <DeleteIcon />
47
+ </IconButton>
48
+ <TextField
49
+ value={name}
50
+ onChange={event => setName(event.target.value)}
51
+ label="Key"
52
+ />
53
+ <TextField
54
+ value={value}
55
+ onChange={event => setValue(event.target.value)}
56
+ label="Value"
57
+ />
58
+ </div>
59
+ )
60
+ })
61
+
62
+ export default observer(
63
+ ({
64
+ onClose,
65
+ data,
66
+ model,
67
+ }: {
68
+ model: MsaViewModel
69
+ onClose: () => void
70
+ data: { left: number; right: number }
71
+ }) => {
72
+ const { blanks } = model
73
+ const { left: l, right: r } = data
74
+ const [rows, setRows] = useState([
75
+ ['Name', ''],
76
+ ['ID', ''],
77
+ ['Note', ''],
78
+ ])
79
+ return (
80
+ <Dialog
81
+ onClose={() => onClose()}
82
+ open
83
+ title="Create new region annotation"
84
+ >
85
+ <DialogContent>
86
+ <Typography>
87
+ Do you want to add an annotation to the MSA at {l}..{r}{' '}
88
+ {blanks.length
89
+ ? ` (gapped ${model.getPos(l)}..${model.getPos(r)}`
90
+ : ''}
91
+ </Typography>
92
+ {rows.map(([key, val], index) => (
93
+ <Row
94
+ key={index}
95
+ name={key}
96
+ value={val}
97
+ setValue={newValue => {
98
+ const newRows = [...rows]
99
+ newRows[index][1] = newValue
100
+ setRows(newRows)
101
+ }}
102
+ setName={newName => {
103
+ const newRows = [...rows]
104
+ newRows[index][0] = newName
105
+ setRows(newRows)
106
+ }}
107
+ onDelete={() => {
108
+ rows.splice(index, 1)
109
+ setRows([...rows])
110
+ }}
111
+ />
112
+ ))}
113
+ <Button
114
+ onClick={() => {
115
+ setRows([...rows, ['', '']])
116
+ }}
117
+ >
118
+ Add row
119
+ </Button>
120
+
121
+ <DialogActions>
122
+ <Button
123
+ onClick={() => {
124
+ model.addAnnotation(l, r, specialFromEntries(rows))
125
+ onClose()
126
+ }}
127
+ variant="contained"
128
+ color="primary"
129
+ >
130
+ Submit
131
+ </Button>
132
+ <Button
133
+ variant="contained"
134
+ color="secondary"
135
+ onClick={() => onClose()}
136
+ >
137
+ Cancel
138
+ </Button>
139
+ </DialogActions>
140
+ </DialogContent>
141
+ </Dialog>
142
+ )
143
+ },
144
+ )
@@ -0,0 +1,225 @@
1
+ import React, { useRef, useMemo, useEffect } from 'react'
2
+ import { observer } from 'mobx-react'
3
+ import { getSnapshot, isStateTreeNode } from 'mobx-state-tree'
4
+
5
+ // locals
6
+ import { IBoxTrack, MsaViewModel } from '../model'
7
+ import Layout from '../layout'
8
+
9
+ interface Feat {
10
+ start: number
11
+ end: number
12
+ }
13
+ const AnnotationBlock = observer(function ({
14
+ track,
15
+ model,
16
+ offsetX,
17
+ }: {
18
+ track: IBoxTrack
19
+ model: MsaViewModel
20
+ offsetX: number
21
+ }) {
22
+ const {
23
+ blockSize,
24
+ colWidth,
25
+ blanks,
26
+ rowHeight,
27
+ highResScaleFactor,
28
+ scrollX,
29
+ } = model
30
+ const {
31
+ model: { height, features, associatedRowName },
32
+ } = track
33
+
34
+ const feats: Feat[] = isStateTreeNode(features)
35
+ ? // @ts-expect-error
36
+ getSnapshot(features)
37
+ : features
38
+
39
+ const layout = useMemo(() => {
40
+ const temp = new Layout()
41
+
42
+ feats?.forEach((feature, index) => {
43
+ const { start, end } = feature
44
+ if (associatedRowName) {
45
+ const s = model.rowSpecificBpToPx(associatedRowName, start - 1)
46
+ const e = model.rowSpecificBpToPx(associatedRowName, end)
47
+ temp.addRect(`${index}`, s, e, rowHeight, feature)
48
+ } else {
49
+ const s = model.globalBpToPx(start - 1)
50
+ const e = model.globalBpToPx(end)
51
+ temp.addRect(`${index}`, s, e, rowHeight, feature)
52
+ }
53
+ })
54
+ return temp
55
+
56
+ // might convert to autorun based drawing
57
+ // eslint-disable-next-line react-hooks/exhaustive-deps
58
+ }, [rowHeight, feats, associatedRowName, model, blanks])
59
+
60
+ const ref = useRef<HTMLCanvasElement>(null)
61
+ const labelRef = useRef<HTMLCanvasElement>(null)
62
+ const mouseoverRef = useRef<HTMLCanvasElement>(null)
63
+
64
+ useEffect(() => {
65
+ if (!ref.current) {
66
+ return
67
+ }
68
+
69
+ const ctx = ref.current.getContext('2d')
70
+ if (!ctx) {
71
+ return
72
+ }
73
+
74
+ ctx.resetTransform()
75
+ ctx.scale(highResScaleFactor, highResScaleFactor)
76
+ ctx.clearRect(0, 0, blockSize, height)
77
+ ctx.translate(-offsetX, 0)
78
+ ctx.textAlign = 'center'
79
+ ctx.font = ctx.font.replace(/\d+px/, `${Math.max(8, rowHeight - 8)}px`)
80
+
81
+ const xStart = Math.max(0, Math.floor(offsetX / colWidth))
82
+ ctx.fillStyle = 'goldenrod'
83
+ layout.rectangles.forEach(value => {
84
+ const { minX, maxX, minY, maxY } = value
85
+
86
+ const x1 = (minX - xStart) * colWidth + offsetX - (offsetX % colWidth)
87
+ const x2 = (maxX - xStart) * colWidth + offsetX - (offsetX % colWidth)
88
+
89
+ if (x2 - x1 > 0) {
90
+ ctx.fillRect(x1, minY, x2 - x1, (maxY - minY) / 2)
91
+ }
92
+ })
93
+ }, [
94
+ associatedRowName,
95
+ blockSize,
96
+ colWidth,
97
+ layout.rectangles,
98
+ model,
99
+ rowHeight,
100
+ height,
101
+ offsetX,
102
+ highResScaleFactor,
103
+ features,
104
+ blanks,
105
+ ])
106
+
107
+ useEffect(() => {
108
+ if (!labelRef.current) {
109
+ return
110
+ }
111
+
112
+ const ctx = labelRef.current.getContext('2d')
113
+ if (!ctx) {
114
+ return
115
+ }
116
+
117
+ // this logic is very similar to MSACanvas
118
+ ctx.resetTransform()
119
+ ctx.scale(highResScaleFactor, highResScaleFactor)
120
+ ctx.clearRect(0, 0, blockSize, height)
121
+ ctx.translate(-offsetX, 0)
122
+ ctx.textAlign = 'center'
123
+ ctx.font = ctx.font.replace(/\d+px/, `${Math.max(8, rowHeight - 8)}px`)
124
+
125
+ ctx.fillStyle = 'black'
126
+ ctx.textAlign = 'left'
127
+ layout.rectangles.forEach(value => {
128
+ const { minX, maxX, maxY, minY } = value
129
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
130
+ const feature = value.data as any
131
+
132
+ const x1 = minX * colWidth
133
+ const x2 = maxX * colWidth
134
+
135
+ if (x2 - x1 > 0) {
136
+ const note = feature.attributes?.Note?.[0]
137
+ const name = feature.attributes?.Name?.[0]
138
+ const type = feature.type
139
+ ctx.fillText(
140
+ [type, name, note].filter(f => !!f).join(' - '),
141
+ Math.max(Math.min(-scrollX, x2), x1),
142
+ minY + (maxY - minY),
143
+ )
144
+ }
145
+ })
146
+ }, [
147
+ blockSize,
148
+ colWidth,
149
+ scrollX,
150
+ highResScaleFactor,
151
+ height,
152
+ layout.rectangles,
153
+ offsetX,
154
+ features,
155
+ model,
156
+ rowHeight,
157
+ blanks,
158
+ ])
159
+
160
+ return !features ? null : (
161
+ <>
162
+ <canvas
163
+ ref={ref}
164
+ height={height * highResScaleFactor}
165
+ width={blockSize * highResScaleFactor}
166
+ style={{
167
+ position: 'absolute',
168
+ left: scrollX + offsetX,
169
+ width: blockSize,
170
+ height,
171
+ }}
172
+ />
173
+ <canvas
174
+ ref={labelRef}
175
+ height={height * highResScaleFactor}
176
+ width={blockSize * highResScaleFactor}
177
+ style={{
178
+ position: 'absolute',
179
+ left: scrollX + offsetX,
180
+ width: blockSize,
181
+ height,
182
+ }}
183
+ />
184
+ <canvas
185
+ ref={mouseoverRef}
186
+ height={height * highResScaleFactor}
187
+ width={blockSize * highResScaleFactor}
188
+ style={{
189
+ position: 'absolute',
190
+ left: scrollX + offsetX,
191
+ width: blockSize,
192
+ height,
193
+ }}
194
+ />
195
+ </>
196
+ )
197
+ })
198
+ const AnnotationTrack = observer(function ({
199
+ model,
200
+ track,
201
+ }: {
202
+ model: MsaViewModel
203
+ track: IBoxTrack
204
+ }) {
205
+ const { blocksX, msaAreaWidth } = model
206
+ const {
207
+ model: { height },
208
+ } = track
209
+ return (
210
+ <div
211
+ style={{
212
+ position: 'relative',
213
+ height,
214
+ width: msaAreaWidth,
215
+ overflow: 'hidden',
216
+ }}
217
+ >
218
+ {blocksX.map(bx => (
219
+ <AnnotationBlock track={track} key={bx} model={model} offsetX={bx} />
220
+ ))}
221
+ </div>
222
+ )
223
+ })
224
+
225
+ export default AnnotationTrack
@@ -0,0 +1,28 @@
1
+ import React from 'react'
2
+ import { DialogContent } from '@mui/material'
3
+ import { observer } from 'mobx-react'
4
+ import { Dialog } from '@jbrowse/core/ui'
5
+ import { Attributes } from '@jbrowse/core/BaseFeatureWidget/BaseFeatureDetail'
6
+
7
+ // locals
8
+ import { MsaViewModel } from '../model'
9
+
10
+ export default observer(function ({
11
+ model,
12
+ onClose,
13
+ open,
14
+ }: {
15
+ model: MsaViewModel
16
+ onClose: () => void
17
+ open: boolean
18
+ }) {
19
+ const { header } = model
20
+
21
+ return (
22
+ <Dialog onClose={() => onClose()} open={open} title="Metadata">
23
+ <DialogContent>
24
+ <Attributes attributes={header} />
25
+ </DialogContent>
26
+ </Dialog>
27
+ )
28
+ })
@@ -0,0 +1,117 @@
1
+ import React, { useState } from 'react'
2
+ import { IconButton, Select, Typography } from '@mui/material'
3
+ import { observer } from 'mobx-react'
4
+
5
+ // locals
6
+ import { MsaViewModel } from '../model'
7
+ import SettingsDialog from './SettingsDlg'
8
+ import AboutDialog from './AboutDlg'
9
+ import DetailsDialog from './DetailsDlg'
10
+ import TracklistDialog from './TracklistDlg'
11
+
12
+ // icons
13
+ import FolderOpenIcon from '@mui/icons-material/FolderOpen'
14
+ import SettingsIcon from '@mui/icons-material/Settings'
15
+ import HelpIcon from '@mui/icons-material/Help'
16
+ import AssignmentIcon from '@mui/icons-material/Assignment'
17
+ import ListIcon from '@mui/icons-material/List'
18
+
19
+ const InfoArea = observer(({ model }: { model: MsaViewModel }) => {
20
+ const { mouseOverRowName, mouseCol } = model
21
+ return (
22
+ <div>
23
+ <Typography display="inline">Row name: {mouseOverRowName}</Typography>
24
+ <span style={{ marginLeft: 10 }} />
25
+ <Typography display="inline">Position: {mouseCol}</Typography>
26
+ </div>
27
+ )
28
+ })
29
+
30
+ const Header = observer(({ model }: { model: MsaViewModel }) => {
31
+ const [settingsDialogViz, setSettingsDialogViz] = useState(false)
32
+ const [aboutDialogViz, setAboutDialogViz] = useState(false)
33
+ const [detailsDialogViz, setDetailsDialogViz] = useState(false)
34
+ const [tracklistDialogViz, setTracklistDialogViz] = useState(false)
35
+ const { currentAlignment, alignmentNames } = model
36
+
37
+ return (
38
+ <div style={{ display: 'flex' }}>
39
+ <IconButton
40
+ onClick={async () => {
41
+ try {
42
+ model.setData({ tree: '', msa: '' })
43
+ await model.setTreeFilehandle(undefined)
44
+ await model.setMSAFilehandle(undefined)
45
+ model.setScrollY(0)
46
+ model.setScrollX(0)
47
+ model.setCurrentAlignment(0)
48
+ } catch (e) {
49
+ console.error(e)
50
+ model.setError(e)
51
+ }
52
+ }}
53
+ >
54
+ <FolderOpenIcon />
55
+ </IconButton>
56
+ <IconButton onClick={() => setSettingsDialogViz(true)}>
57
+ <SettingsIcon />
58
+ </IconButton>
59
+ <IconButton onClick={() => setDetailsDialogViz(true)}>
60
+ <AssignmentIcon />
61
+ </IconButton>
62
+ <IconButton onClick={() => setTracklistDialogViz(true)}>
63
+ <ListIcon />
64
+ </IconButton>
65
+ {settingsDialogViz ? (
66
+ <SettingsDialog
67
+ open
68
+ model={model}
69
+ onClose={() => setSettingsDialogViz(false)}
70
+ />
71
+ ) : null}
72
+ {aboutDialogViz ? (
73
+ <AboutDialog open onClose={() => setAboutDialogViz(false)} />
74
+ ) : null}
75
+ {detailsDialogViz ? (
76
+ <DetailsDialog
77
+ open
78
+ model={model}
79
+ onClose={() => setDetailsDialogViz(false)}
80
+ />
81
+ ) : null}
82
+
83
+ {tracklistDialogViz ? (
84
+ <TracklistDialog
85
+ open
86
+ model={model}
87
+ onClose={() => setTracklistDialogViz(false)}
88
+ />
89
+ ) : null}
90
+ {alignmentNames.length > 0 ? (
91
+ <Select
92
+ native
93
+ value={currentAlignment}
94
+ size="small"
95
+ onChange={event => {
96
+ model.setCurrentAlignment(+(event.target.value as string))
97
+ model.setScrollX(0)
98
+ model.setScrollY(0)
99
+ }}
100
+ >
101
+ {alignmentNames.map((option, index) => (
102
+ <option key={`${option}-${index}`} value={index}>
103
+ {option}
104
+ </option>
105
+ ))}
106
+ </Select>
107
+ ) : null}
108
+ <InfoArea model={model} />
109
+ <div style={{ flex: 1 }} />
110
+ <IconButton onClick={() => setAboutDialogViz(true)}>
111
+ <HelpIcon />
112
+ </IconButton>
113
+ </div>
114
+ )
115
+ })
116
+
117
+ export default Header
@@ -0,0 +1,192 @@
1
+ import React, { useState } from 'react'
2
+ import { Button, Container, Grid, Typography, Link } from '@mui/material'
3
+ import { observer } from 'mobx-react'
4
+ import { FileSelector } from '@jbrowse/core/ui'
5
+ import { FileLocation } from '@jbrowse/core/util/types'
6
+
7
+ // locals
8
+ import { MsaViewModel } from '../model'
9
+ import { smallTree, smallMSA, smallMSAOnly } from './data/seq2'
10
+
11
+ async function load(
12
+ model: MsaViewModel,
13
+ msaFile?: FileLocation,
14
+ treeFile?: FileLocation,
15
+ ) {
16
+ model.setError(undefined)
17
+ try {
18
+ if (msaFile) {
19
+ await model.setMSAFilehandle(msaFile)
20
+ }
21
+ if (treeFile) {
22
+ await model.setTreeFilehandle(treeFile)
23
+ }
24
+ } catch (e) {
25
+ model.setError(e)
26
+ }
27
+ }
28
+
29
+ const ListItem = ({
30
+ onClick,
31
+ model,
32
+ children,
33
+ }: {
34
+ onClick: () => void
35
+ model: MsaViewModel
36
+ children: React.ReactNode
37
+ }) => (
38
+ <li>
39
+ <Link
40
+ onClick={event => {
41
+ model.setError(undefined)
42
+ event.preventDefault()
43
+ onClick()
44
+ }}
45
+ href="#"
46
+ >
47
+ <Typography display="inline">{children}</Typography>
48
+ </Link>
49
+ </li>
50
+ )
51
+ export default observer(({ model }: { model: MsaViewModel }) => {
52
+ const [msaFile, setMsaFile] = useState<FileLocation>()
53
+ const [treeFile, setTreeFile] = useState<FileLocation>()
54
+ const { error } = model
55
+
56
+ return (
57
+ <Container>
58
+ <div style={{ width: '50%' }}>
59
+ {error ? (
60
+ <div style={{ padding: 20 }}>
61
+ <Typography color="error">Error: {`${error}`}</Typography>
62
+ </div>
63
+ ) : null}
64
+ <Typography>
65
+ Open an MSA file (stockholm or clustal format) and/or a tree file
66
+ (newick format).
67
+ </Typography>
68
+ <Typography color="error">
69
+ Note: you can open up just an MSA or just a tree, both are not
70
+ required. Some MSA files e.g. stockholm format have an embedded tree
71
+ also and this is fine, and opening a separate tree file is not
72
+ required.
73
+ </Typography>
74
+ </div>
75
+
76
+ <Grid container spacing={10} justifyContent="center" alignItems="center">
77
+ <Grid item>
78
+ <Typography>MSA file or URL</Typography>
79
+ <FileSelector location={msaFile} setLocation={setMsaFile} />
80
+ <Typography>Tree file or URL</Typography>
81
+ <FileSelector location={treeFile} setLocation={setTreeFile} />
82
+ </Grid>
83
+
84
+ <Grid item>
85
+ <Button
86
+ onClick={() => load(model, msaFile, treeFile)}
87
+ variant="contained"
88
+ color="primary"
89
+ disabled={!msaFile && !treeFile}
90
+ >
91
+ Open
92
+ </Button>
93
+ </Grid>
94
+
95
+ <Grid item>
96
+ <Typography>Examples</Typography>
97
+ <ul>
98
+ <ListItem
99
+ model={model}
100
+ onClick={() =>
101
+ load(model, undefined, {
102
+ uri: 'https://jbrowse.org/genomes/newick_trees/sarscov2phylo.pub.ft.nh',
103
+ locationType: 'UriLocation',
104
+ })
105
+ }
106
+ >
107
+ 230k COVID-19 samples (tree only)
108
+ </ListItem>
109
+ <ListItem
110
+ model={model}
111
+ onClick={() => {
112
+ model.setData({ msa: smallMSA, tree: smallTree })
113
+ }}
114
+ >
115
+ Small protein MSA+tree
116
+ </ListItem>
117
+ <ListItem
118
+ model={model}
119
+ onClick={() => {
120
+ model.setData({ msa: smallMSAOnly })
121
+ }}
122
+ >
123
+ Small MSA only
124
+ </ListItem>
125
+ <ListItem
126
+ model={model}
127
+ onClick={() =>
128
+ load(model, {
129
+ uri: 'https://ihh.github.io/abrowse/build/pfam-cov2.stock',
130
+ locationType: 'UriLocation',
131
+ })
132
+ }
133
+ >
134
+ PFAM SARS-CoV2 multi-stockholm
135
+ </ListItem>
136
+ <ListItem
137
+ model={model}
138
+ onClick={() =>
139
+ load(model, {
140
+ uri: 'https://jbrowse.org/genomes/multiple_sequence_alignments/Lysine.stock',
141
+ locationType: 'UriLocation',
142
+ })
143
+ }
144
+ >
145
+ Lysine stockholm file
146
+ </ListItem>
147
+ <ListItem
148
+ model={model}
149
+ onClick={() =>
150
+ load(model, {
151
+ uri: 'https://jbrowse.org/genomes/multiple_sequence_alignments/PF01601_full.txt',
152
+ locationType: 'UriLocation',
153
+ })
154
+ }
155
+ >
156
+ PF01601 stockholm file (SARS-CoV2 spike protein)
157
+ </ListItem>
158
+ <ListItem
159
+ model={model}
160
+ onClick={() =>
161
+ load(model, {
162
+ uri: 'https://jbrowse.org/genomes/multiple_sequence_alignments/europe_covid.fa',
163
+ locationType: 'UriLocation',
164
+ })
165
+ }
166
+ >
167
+ Europe COVID full genomes (LR883044.1 and 199 other sequences)
168
+ </ListItem>
169
+ <ListItem
170
+ model={model}
171
+ onClick={() =>
172
+ load(
173
+ model,
174
+ {
175
+ uri: 'https://jbrowse.org/genomes/multiple_sequence_alignments/rhv_test-only.aligned_with_mafft_auto.fa',
176
+ locationType: 'UriLocation',
177
+ },
178
+ {
179
+ uri: 'https://jbrowse.org/genomes/multiple_sequence_alignments/rhv_test-only.aligned_with_mafft_auto.nh',
180
+ locationType: 'UriLocation',
181
+ },
182
+ )
183
+ }
184
+ >
185
+ MAFFT+VeryFastTree(17.9k samples)
186
+ </ListItem>
187
+ </ul>
188
+ </Grid>
189
+ </Grid>
190
+ </Container>
191
+ )
192
+ })