react-msaview 3.0.0 → 3.0.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 (84) hide show
  1. package/bundle/index.js +31 -31
  2. package/dist/components/Header.js +4 -9
  3. package/dist/components/Header.js.map +1 -1
  4. package/dist/components/HeaderInfoArea.d.ts +6 -0
  5. package/dist/components/HeaderInfoArea.js +12 -0
  6. package/dist/components/HeaderInfoArea.js.map +1 -0
  7. package/dist/components/MSAPanel/Loading.d.ts +2 -0
  8. package/dist/components/MSAPanel/Loading.js +12 -0
  9. package/dist/components/MSAPanel/Loading.js.map +1 -0
  10. package/dist/components/MSAPanel/MSACanvas.js +1 -10
  11. package/dist/components/MSAPanel/MSACanvas.js.map +1 -1
  12. package/dist/components/MSAPanel/MSAMouseoverCanvas.js +1 -22
  13. package/dist/components/MSAPanel/MSAMouseoverCanvas.js.map +1 -1
  14. package/dist/components/MSAPanel/renderMSABlock.js +0 -1
  15. package/dist/components/MSAPanel/renderMSABlock.js.map +1 -1
  16. package/dist/components/MSAPanel/renderMSAMouseover.d.ts +5 -0
  17. package/dist/components/MSAPanel/renderMSAMouseover.js +24 -0
  18. package/dist/components/MSAPanel/renderMSAMouseover.js.map +1 -0
  19. package/dist/components/Minimap.js +13 -13
  20. package/dist/components/Minimap.js.map +1 -1
  21. package/dist/components/TreePanel/TreeCanvasBlock.js +8 -4
  22. package/dist/components/TreePanel/TreeCanvasBlock.js.map +1 -1
  23. package/dist/components/TreePanel/{TreeMenu.d.ts → TreeNodeMenu.d.ts} +1 -0
  24. package/dist/components/TreePanel/{TreeMenu.js → TreeNodeMenu.js} +8 -4
  25. package/dist/components/TreePanel/TreeNodeMenu.js.map +1 -0
  26. package/dist/components/TreePanel/dialogs/{TreeNodeInfoDlg.js → TreeNodeInfoDialog.js} +2 -2
  27. package/dist/components/TreePanel/dialogs/TreeNodeInfoDialog.js.map +1 -0
  28. package/dist/components/TreePanel/renderTreeCanvas.d.ts +8 -3
  29. package/dist/components/TreePanel/renderTreeCanvas.js +8 -7
  30. package/dist/components/TreePanel/renderTreeCanvas.js.map +1 -1
  31. package/dist/components/dialogs/SettingsDialog.js +31 -22
  32. package/dist/components/dialogs/SettingsDialog.js.map +1 -1
  33. package/dist/model.d.ts +120 -62
  34. package/dist/model.js +176 -124
  35. package/dist/model.js.map +1 -1
  36. package/dist/parsers/ClustalMSA.d.ts +1 -1
  37. package/dist/parsers/ClustalMSA.js +1 -1
  38. package/dist/parsers/ClustalMSA.js.map +1 -1
  39. package/dist/parsers/FastaMSA.d.ts +1 -1
  40. package/dist/parsers/FastaMSA.js +2 -2
  41. package/dist/parsers/FastaMSA.js.map +1 -1
  42. package/dist/parsers/StockholmMSA.d.ts +1 -1
  43. package/dist/parsers/StockholmMSA.js +2 -2
  44. package/dist/parsers/StockholmMSA.js.map +1 -1
  45. package/dist/util.d.ts +1 -0
  46. package/dist/util.js +15 -7
  47. package/dist/util.js.map +1 -1
  48. package/dist/version.d.ts +1 -1
  49. package/dist/version.js +1 -1
  50. package/package.json +1 -1
  51. package/src/components/Header.tsx +5 -11
  52. package/src/components/HeaderInfoArea.tsx +21 -0
  53. package/src/components/MSAPanel/Loading.tsx +16 -0
  54. package/src/components/MSAPanel/MSACanvas.tsx +1 -16
  55. package/src/components/MSAPanel/MSAMouseoverCanvas.tsx +2 -50
  56. package/src/components/MSAPanel/renderMSABlock.ts +0 -2
  57. package/src/components/MSAPanel/renderMSAMouseover.ts +51 -0
  58. package/src/components/Minimap.tsx +15 -15
  59. package/src/components/TreePanel/TreeCanvasBlock.tsx +8 -3
  60. package/src/components/TreePanel/{TreeMenu.tsx → TreeNodeMenu.tsx} +12 -3
  61. package/src/components/TreePanel/dialogs/{TreeNodeInfoDlg.tsx → TreeNodeInfoDialog.tsx} +1 -1
  62. package/src/components/TreePanel/renderTreeCanvas.ts +13 -4
  63. package/src/components/dialogs/SettingsDialog.tsx +61 -44
  64. package/src/model.ts +279 -154
  65. package/src/parsers/ClustalMSA.ts +1 -1
  66. package/src/parsers/FastaMSA.ts +1 -1
  67. package/src/parsers/StockholmMSA.ts +1 -1
  68. package/src/util.ts +19 -6
  69. package/src/version.ts +1 -1
  70. package/dist/components/OverviewRubberband.d.ts +0 -8
  71. package/dist/components/OverviewRubberband.js +0 -185
  72. package/dist/components/OverviewRubberband.js.map +0 -1
  73. package/dist/components/Rubberband.d.ts +0 -8
  74. package/dist/components/Rubberband.js +0 -185
  75. package/dist/components/Rubberband.js.map +0 -1
  76. package/dist/components/TreePanel/TreeMenu.js.map +0 -1
  77. package/dist/components/TreePanel/dialogs/TreeNodeInfoDlg.js.map +0 -1
  78. package/dist/components/dialogs/AnnotationDialog.d.ts +0 -11
  79. package/dist/components/dialogs/AnnotationDialog.js +0 -65
  80. package/dist/components/dialogs/AnnotationDialog.js.map +0 -1
  81. package/src/components/OverviewRubberband.tsx +0 -283
  82. package/src/components/Rubberband.tsx +0 -283
  83. package/src/components/dialogs/AnnotationDialog.tsx +0 -144
  84. /package/dist/components/TreePanel/dialogs/{TreeNodeInfoDlg.d.ts → TreeNodeInfoDialog.d.ts} +0 -0
@@ -1,283 +0,0 @@
1
- import React, { useRef, useEffect, useState, lazy } from 'react'
2
- import { observer } from 'mobx-react'
3
- import { makeStyles } from 'tss-react/mui'
4
- import { Popover, Typography, alpha } from '@mui/material'
5
- import { Menu } from '@jbrowse/core/ui'
6
-
7
- // icons
8
- import AssignmentIcon from '@mui/icons-material/Assignment'
9
-
10
- // locals
11
- import { MsaViewModel } from '../model'
12
- import VerticalGuide from './VerticalGuide'
13
-
14
- const AnnotationDialog = lazy(() => import('./dialogs/AnnotationDialog'))
15
-
16
- const useStyles = makeStyles()(theme => {
17
- const background =
18
- 'tertiary' in theme.palette && theme.palette.tertiary
19
- ? alpha(theme.palette.tertiary.main, 0.7)
20
- : alpha(theme.palette.primary.main, 0.7)
21
- return {
22
- rubberband: {
23
- height: '100%',
24
- background,
25
- position: 'absolute',
26
- zIndex: 10,
27
- textAlign: 'center',
28
- overflow: 'hidden',
29
- },
30
- rubberbandControl: {
31
- cursor: 'crosshair',
32
- width: '100%',
33
- minHeight: 8,
34
- },
35
- rubberbandText: {
36
- color: theme.palette.tertiary
37
- ? theme.palette.tertiary.contrastText
38
- : theme.palette.primary.contrastText,
39
- },
40
- popover: {
41
- mouseEvents: 'none',
42
- cursor: 'crosshair',
43
- },
44
- paper: {
45
- paddingLeft: theme.spacing(1),
46
- paddingRight: theme.spacing(1),
47
- },
48
- }
49
- })
50
-
51
- function OverviewRubberband({
52
- model,
53
- ControlComponent = <div />,
54
- }: {
55
- model: MsaViewModel
56
- ControlComponent?: React.ReactElement
57
- }) {
58
- const { treeAreaWidth } = model
59
- const [startX, setStartX] = useState<number>()
60
- const [currentX, setCurrentX] = useState<number>()
61
-
62
- // clientX and clientY used for anchorPosition for menu
63
- // offsetX used for calculations about width of selection
64
- const [anchorPosition, setAnchorPosition] = useState<{
65
- offsetX: number
66
- clientX: number
67
- clientY: number
68
- }>()
69
- const [guideX, setGuideX] = useState<number | undefined>()
70
- const controlsRef = useRef<HTMLDivElement>(null)
71
- const rubberbandRef = useRef(null)
72
- const { classes } = useStyles()
73
- const mouseDragging = startX !== undefined && anchorPosition === undefined
74
-
75
- useEffect(() => {
76
- function globalMouseMove(event: MouseEvent) {
77
- if (controlsRef.current && mouseDragging) {
78
- const relativeX =
79
- event.clientX - controlsRef.current.getBoundingClientRect().left
80
- setCurrentX(relativeX)
81
- }
82
- }
83
-
84
- function globalMouseUp(event: MouseEvent) {
85
- if (startX !== undefined && controlsRef.current) {
86
- const { clientX, clientY } = event
87
- const ref = controlsRef.current
88
- const offsetX = clientX - ref.getBoundingClientRect().left
89
- // as stated above, store both clientX/Y and offsetX for different
90
- // purposes
91
- setAnchorPosition({
92
- offsetX,
93
- clientX,
94
- clientY,
95
- })
96
- setGuideX(undefined)
97
- }
98
- }
99
- if (mouseDragging) {
100
- window.addEventListener('mousemove', globalMouseMove)
101
- window.addEventListener('mouseup', globalMouseUp)
102
- return () => {
103
- window.removeEventListener('mousemove', globalMouseMove)
104
- window.removeEventListener('mouseup', globalMouseUp)
105
- }
106
- }
107
- return () => {}
108
- }, [startX, mouseDragging, anchorPosition])
109
-
110
- useEffect(() => {
111
- if (
112
- !mouseDragging &&
113
- currentX !== undefined &&
114
- startX !== undefined &&
115
- Math.abs(currentX - startX) <= 3
116
- ) {
117
- handleClose()
118
- }
119
- }, [mouseDragging, currentX, startX, model.colWidth])
120
-
121
- function mouseDown(event: React.MouseEvent<HTMLDivElement>) {
122
- event.preventDefault()
123
- event.stopPropagation()
124
- const relativeX =
125
- event.clientX -
126
- (event.target as HTMLDivElement).getBoundingClientRect().left
127
- setStartX(relativeX)
128
- setCurrentX(relativeX)
129
- }
130
-
131
- function mouseMove(event: React.MouseEvent<HTMLDivElement>) {
132
- const target = event.target as HTMLDivElement
133
- setGuideX(event.clientX - target.getBoundingClientRect().left)
134
- }
135
-
136
- function mouseOut() {
137
- setGuideX(undefined)
138
- model.clearAnnotationClickBoundaries()
139
- }
140
-
141
- function handleClose() {
142
- setAnchorPosition(undefined)
143
- setStartX(undefined)
144
- setCurrentX(undefined)
145
- }
146
-
147
- function handleMenuItemClick(_: unknown, callback: Function) {
148
- callback()
149
- handleClose()
150
- }
151
-
152
- if (startX === undefined) {
153
- return (
154
- <>
155
- {guideX !== undefined ? (
156
- <VerticalGuide model={model} coordX={guideX} />
157
- ) : null}
158
- <div
159
- data-testid="rubberband_controls"
160
- className={classes.rubberbandControl}
161
- role="presentation"
162
- ref={controlsRef}
163
- onMouseDown={mouseDown}
164
- onMouseOut={mouseOut}
165
- onMouseMove={mouseMove}
166
- >
167
- {ControlComponent}
168
- </div>
169
- </>
170
- )
171
- }
172
-
173
- const right = anchorPosition ? anchorPosition.offsetX : currentX || 0
174
- const left = right < startX ? right : startX
175
- const width = Math.abs(right - startX)
176
- const leftBpOffset = model.pxToBp(left)
177
- const rightBpOffset = model.pxToBp(left + width)
178
- const numOfBpSelected = Math.ceil(width / model.colWidth)
179
-
180
- const menuItems = [
181
- {
182
- label: 'Create annotation',
183
- icon: AssignmentIcon,
184
- onClick: () => {
185
- model.setAnnotationClickBoundaries(leftBpOffset, rightBpOffset)
186
- model.queueDialog(onClose => [
187
- AnnotationDialog,
188
- {
189
- data: model.annotPos,
190
- model,
191
- onClose: () => {
192
- model.clearAnnotationClickBoundaries()
193
- onClose()
194
- },
195
- },
196
- ])
197
- handleClose()
198
- },
199
- },
200
- ]
201
- return (
202
- <>
203
- {rubberbandRef.current ? (
204
- <>
205
- <Popover
206
- className={classes.popover}
207
- classes={{
208
- paper: classes.paper,
209
- }}
210
- open
211
- anchorEl={rubberbandRef.current}
212
- anchorOrigin={{
213
- vertical: 'top',
214
- horizontal: 'left',
215
- }}
216
- transformOrigin={{
217
- vertical: 'bottom',
218
- horizontal: 'right',
219
- }}
220
- keepMounted
221
- disableRestoreFocus
222
- >
223
- <Typography>{leftBpOffset + 1}</Typography>
224
- </Popover>
225
- <Popover
226
- className={classes.popover}
227
- classes={{
228
- paper: classes.paper,
229
- }}
230
- open
231
- anchorEl={rubberbandRef.current}
232
- anchorOrigin={{
233
- vertical: 'top',
234
- horizontal: 'right',
235
- }}
236
- transformOrigin={{
237
- vertical: 'bottom',
238
- horizontal: 'left',
239
- }}
240
- keepMounted
241
- disableRestoreFocus
242
- >
243
- <Typography>{rightBpOffset + 1}</Typography>
244
- </Popover>
245
- </>
246
- ) : null}
247
- <div
248
- ref={rubberbandRef}
249
- className={classes.rubberband}
250
- style={{ left: left + treeAreaWidth, width }}
251
- >
252
- <Typography variant="h6" className={classes.rubberbandText}>
253
- {numOfBpSelected.toLocaleString('en-US')} bp
254
- </Typography>
255
- </div>
256
- <div
257
- data-testid="rubberband_controls"
258
- className={classes.rubberbandControl}
259
- role="presentation"
260
- ref={controlsRef}
261
- onMouseDown={mouseDown}
262
- onMouseOut={mouseOut}
263
- onMouseMove={mouseMove}
264
- >
265
- {ControlComponent}
266
- </div>
267
- {anchorPosition ? (
268
- <Menu
269
- anchorReference="anchorPosition"
270
- anchorPosition={{
271
- left: anchorPosition.clientX,
272
- top: anchorPosition.clientY,
273
- }}
274
- onMenuItemClick={handleMenuItemClick}
275
- open={Boolean(anchorPosition)}
276
- onClose={handleClose}
277
- menuItems={menuItems}
278
- />
279
- ) : null}
280
- </>
281
- )
282
- }
283
- export default observer(OverviewRubberband)
@@ -1,283 +0,0 @@
1
- import React, { useRef, useEffect, useState, lazy } from 'react'
2
- import { observer } from 'mobx-react'
3
- import { makeStyles } from 'tss-react/mui'
4
- import { Popover, Typography, alpha } from '@mui/material'
5
- import { Menu } from '@jbrowse/core/ui'
6
-
7
- // icons
8
- import AssignmentIcon from '@mui/icons-material/Assignment'
9
-
10
- // locals
11
- import { MsaViewModel } from '../model'
12
- import VerticalGuide from './VerticalGuide'
13
-
14
- const AnnotationDialog = lazy(() => import('./dialogs/AnnotationDialog'))
15
-
16
- const useStyles = makeStyles()(theme => {
17
- const background =
18
- 'tertiary' in theme.palette && theme.palette.tertiary
19
- ? alpha(theme.palette.tertiary.main, 0.7)
20
- : alpha(theme.palette.primary.main, 0.7)
21
- return {
22
- rubberband: {
23
- height: '100%',
24
- background,
25
- position: 'absolute',
26
- zIndex: 10,
27
- textAlign: 'center',
28
- overflow: 'hidden',
29
- },
30
- rubberbandControl: {
31
- cursor: 'crosshair',
32
- width: '100%',
33
- minHeight: 8,
34
- },
35
- rubberbandText: {
36
- color: theme.palette.tertiary
37
- ? theme.palette.tertiary.contrastText
38
- : theme.palette.primary.contrastText,
39
- },
40
- popover: {
41
- mouseEvents: 'none',
42
- cursor: 'crosshair',
43
- },
44
- paper: {
45
- paddingLeft: theme.spacing(1),
46
- paddingRight: theme.spacing(1),
47
- },
48
- }
49
- })
50
-
51
- function Rubberband({
52
- model,
53
- ControlComponent = <div />,
54
- }: {
55
- model: MsaViewModel
56
- ControlComponent?: React.ReactElement
57
- }) {
58
- const { treeAreaWidth } = model
59
- const [startX, setStartX] = useState<number>()
60
- const [currentX, setCurrentX] = useState<number>()
61
-
62
- // clientX and clientY used for anchorPosition for menu
63
- // offsetX used for calculations about width of selection
64
- const [anchorPosition, setAnchorPosition] = useState<{
65
- offsetX: number
66
- clientX: number
67
- clientY: number
68
- }>()
69
- const [guideX, setGuideX] = useState<number | undefined>()
70
- const controlsRef = useRef<HTMLDivElement>(null)
71
- const rubberbandRef = useRef(null)
72
- const { classes } = useStyles()
73
- const mouseDragging = startX !== undefined && anchorPosition === undefined
74
-
75
- useEffect(() => {
76
- function globalMouseMove(event: MouseEvent) {
77
- if (controlsRef.current && mouseDragging) {
78
- const relativeX =
79
- event.clientX - controlsRef.current.getBoundingClientRect().left
80
- setCurrentX(relativeX)
81
- }
82
- }
83
-
84
- function globalMouseUp(event: MouseEvent) {
85
- if (startX !== undefined && controlsRef.current) {
86
- const { clientX, clientY } = event
87
- const ref = controlsRef.current
88
- const offsetX = clientX - ref.getBoundingClientRect().left
89
- // as stated above, store both clientX/Y and offsetX for different
90
- // purposes
91
- setAnchorPosition({
92
- offsetX,
93
- clientX,
94
- clientY,
95
- })
96
- setGuideX(undefined)
97
- }
98
- }
99
- if (mouseDragging) {
100
- window.addEventListener('mousemove', globalMouseMove)
101
- window.addEventListener('mouseup', globalMouseUp)
102
- return () => {
103
- window.removeEventListener('mousemove', globalMouseMove)
104
- window.removeEventListener('mouseup', globalMouseUp)
105
- }
106
- }
107
- return () => {}
108
- }, [startX, mouseDragging, anchorPosition])
109
-
110
- useEffect(() => {
111
- if (
112
- !mouseDragging &&
113
- currentX !== undefined &&
114
- startX !== undefined &&
115
- Math.abs(currentX - startX) <= 3
116
- ) {
117
- handleClose()
118
- }
119
- }, [mouseDragging, currentX, startX, model.colWidth])
120
-
121
- function mouseDown(event: React.MouseEvent<HTMLDivElement>) {
122
- event.preventDefault()
123
- event.stopPropagation()
124
- const relativeX =
125
- event.clientX -
126
- (event.target as HTMLDivElement).getBoundingClientRect().left
127
- setStartX(relativeX)
128
- setCurrentX(relativeX)
129
- }
130
-
131
- function mouseMove(event: React.MouseEvent<HTMLDivElement>) {
132
- const target = event.target as HTMLDivElement
133
- setGuideX(event.clientX - target.getBoundingClientRect().left)
134
- }
135
-
136
- function mouseOut() {
137
- setGuideX(undefined)
138
- model.clearAnnotationClickBoundaries()
139
- }
140
-
141
- function handleClose() {
142
- setAnchorPosition(undefined)
143
- setStartX(undefined)
144
- setCurrentX(undefined)
145
- }
146
-
147
- function handleMenuItemClick(_: unknown, callback: Function) {
148
- callback()
149
- handleClose()
150
- }
151
-
152
- if (startX === undefined) {
153
- return (
154
- <>
155
- {guideX !== undefined ? (
156
- <VerticalGuide model={model} coordX={guideX} />
157
- ) : null}
158
- <div
159
- data-testid="rubberband_controls"
160
- className={classes.rubberbandControl}
161
- role="presentation"
162
- ref={controlsRef}
163
- onMouseDown={mouseDown}
164
- onMouseOut={mouseOut}
165
- onMouseMove={mouseMove}
166
- >
167
- {ControlComponent}
168
- </div>
169
- </>
170
- )
171
- }
172
-
173
- const right = anchorPosition ? anchorPosition.offsetX : currentX || 0
174
- const left = right < startX ? right : startX
175
- const width = Math.abs(right - startX)
176
- const leftBpOffset = model.pxToBp(left)
177
- const rightBpOffset = model.pxToBp(left + width)
178
- const numOfBpSelected = Math.ceil(width / model.colWidth)
179
-
180
- const menuItems = [
181
- {
182
- label: 'Create annotation',
183
- icon: AssignmentIcon,
184
- onClick: () => {
185
- model.setAnnotationClickBoundaries(leftBpOffset, rightBpOffset)
186
- model.queueDialog(onClose => [
187
- AnnotationDialog,
188
- {
189
- data: model.annotPos,
190
- model,
191
- onClose: () => {
192
- model.clearAnnotationClickBoundaries()
193
- onClose()
194
- },
195
- },
196
- ])
197
- handleClose()
198
- },
199
- },
200
- ]
201
- return (
202
- <>
203
- {rubberbandRef.current ? (
204
- <>
205
- <Popover
206
- className={classes.popover}
207
- classes={{
208
- paper: classes.paper,
209
- }}
210
- open
211
- anchorEl={rubberbandRef.current}
212
- anchorOrigin={{
213
- vertical: 'top',
214
- horizontal: 'left',
215
- }}
216
- transformOrigin={{
217
- vertical: 'bottom',
218
- horizontal: 'right',
219
- }}
220
- keepMounted
221
- disableRestoreFocus
222
- >
223
- <Typography>{leftBpOffset + 1}</Typography>
224
- </Popover>
225
- <Popover
226
- className={classes.popover}
227
- classes={{
228
- paper: classes.paper,
229
- }}
230
- open
231
- anchorEl={rubberbandRef.current}
232
- anchorOrigin={{
233
- vertical: 'top',
234
- horizontal: 'right',
235
- }}
236
- transformOrigin={{
237
- vertical: 'bottom',
238
- horizontal: 'left',
239
- }}
240
- keepMounted
241
- disableRestoreFocus
242
- >
243
- <Typography>{rightBpOffset + 1}</Typography>
244
- </Popover>
245
- </>
246
- ) : null}
247
- <div
248
- ref={rubberbandRef}
249
- className={classes.rubberband}
250
- style={{ left: left + treeAreaWidth, width }}
251
- >
252
- <Typography variant="h6" className={classes.rubberbandText}>
253
- {numOfBpSelected.toLocaleString('en-US')} bp
254
- </Typography>
255
- </div>
256
- <div
257
- data-testid="rubberband_controls"
258
- className={classes.rubberbandControl}
259
- role="presentation"
260
- ref={controlsRef}
261
- onMouseDown={mouseDown}
262
- onMouseOut={mouseOut}
263
- onMouseMove={mouseMove}
264
- >
265
- {ControlComponent}
266
- </div>
267
- {anchorPosition ? (
268
- <Menu
269
- anchorReference="anchorPosition"
270
- anchorPosition={{
271
- left: anchorPosition.clientX,
272
- top: anchorPosition.clientY,
273
- }}
274
- onMenuItemClick={handleMenuItemClick}
275
- open={Boolean(anchorPosition)}
276
- onClose={handleClose}
277
- menuItems={menuItems}
278
- />
279
- ) : null}
280
- </>
281
- )
282
- }
283
- export default observer(Rubberband)
@@ -1,144 +0,0 @@
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 Record<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
- )