react-msaview 2.0.0 → 2.1.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 (148) 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 +4 -4
  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.map +1 -1
  95. package/dist/version.d.ts +1 -1
  96. package/dist/version.js +1 -1
  97. package/dist/version.js.map +1 -1
  98. package/package.json +9 -9
  99. package/src/UniprotTrack.ts +6 -7
  100. package/src/colorSchemes.ts +7 -7
  101. package/src/components/BoxTrack.tsx +6 -198
  102. package/src/components/BoxTrackBlock.tsx +198 -0
  103. package/src/components/Header.tsx +49 -60
  104. package/src/components/MSABlock.tsx +164 -0
  105. package/src/components/MSACanvas.tsx +3 -158
  106. package/src/components/MSAMouseoverCanvas.tsx +87 -0
  107. package/src/components/MSAView.tsx +19 -63
  108. package/src/components/MultiAlignmentSelector.tsx +33 -0
  109. package/src/components/Rubberband.tsx +0 -1
  110. package/src/components/Ruler.tsx +2 -1
  111. package/src/components/Track.tsx +9 -6
  112. package/src/components/TreeBranchMenu.tsx +67 -0
  113. package/src/components/TreeCanvas.tsx +2 -507
  114. package/src/components/TreeCanvasBlock.tsx +359 -0
  115. package/src/components/TreeMenu.tsx +105 -0
  116. package/src/components/ZoomControls.tsx +78 -0
  117. package/src/components/{AboutDlg.tsx → dialogs/AboutDlg.tsx} +3 -9
  118. package/src/components/{AddTrackDlg.tsx → dialogs/AddTrackDlg.tsx} +1 -1
  119. package/src/components/{AnnotationDlg.tsx → dialogs/AnnotationDlg.tsx} +2 -2
  120. package/src/components/{DetailsDlg.tsx → dialogs/DetailsDlg.tsx} +5 -5
  121. package/src/components/{SettingsDlg.tsx → dialogs/SettingsDlg.tsx} +6 -6
  122. package/src/components/{TracklistDlg.tsx → dialogs/TracklistDlg.tsx} +2 -4
  123. package/src/components/util.ts +0 -1
  124. package/src/declare.d.ts +0 -1
  125. package/src/model.ts +32 -29
  126. package/src/parseNewick.ts +2 -3
  127. package/src/parsers/FastaMSA.ts +3 -3
  128. package/src/parsers/StockholmMSA.ts +5 -5
  129. package/src/util.ts +3 -3
  130. package/src/version.ts +1 -1
  131. package/dist/components/AboutDlg.d.ts +0 -4
  132. package/dist/components/AboutDlg.js.map +0 -1
  133. package/dist/components/AddTrackDlg.js.map +0 -1
  134. package/dist/components/AnnotationDlg.js.map +0 -1
  135. package/dist/components/DetailsDlg.d.ts +0 -7
  136. package/dist/components/DetailsDlg.js.map +0 -1
  137. package/dist/components/MoreInfoDlg.js.map +0 -1
  138. package/dist/components/SettingsDlg.d.ts +0 -7
  139. package/dist/components/SettingsDlg.js.map +0 -1
  140. package/dist/components/TrackInfoDlg.js.map +0 -1
  141. package/dist/components/TracklistDlg.d.ts +0 -7
  142. package/dist/components/TracklistDlg.js.map +0 -1
  143. /package/dist/components/{AddTrackDlg.js → dialogs/AddTrackDlg.js} +0 -0
  144. /package/dist/components/{AnnotationDlg.js → dialogs/AnnotationDlg.js} +0 -0
  145. /package/dist/components/{MoreInfoDlg.js → dialogs/MoreInfoDlg.js} +0 -0
  146. /package/dist/components/{TrackInfoDlg.js → dialogs/TrackInfoDlg.js} +0 -0
  147. /package/src/components/{MoreInfoDlg.tsx → dialogs/MoreInfoDlg.tsx} +0 -0
  148. /package/src/components/{TrackInfoDlg.tsx → dialogs/TrackInfoDlg.tsx} +0 -0
@@ -0,0 +1,164 @@
1
+ import React, { useEffect, useRef, useMemo } from 'react'
2
+ import { useTheme } from '@mui/material'
3
+ import { observer } from 'mobx-react'
4
+
5
+ // locals
6
+ import { MsaViewModel } from '../model'
7
+ import { colorContrast } from '../util'
8
+ import { getClustalXColor, getPercentIdentityColor } from '../colorSchemes'
9
+
10
+ const MSABlock = observer(function ({
11
+ model,
12
+ offsetX,
13
+ offsetY,
14
+ }: {
15
+ model: MsaViewModel
16
+ offsetX: number
17
+ offsetY: number
18
+ }) {
19
+ const {
20
+ MSA,
21
+ colWidth,
22
+ bgColor,
23
+ columns,
24
+ rowHeight,
25
+ scrollY,
26
+ scrollX,
27
+ hierarchy,
28
+ colorScheme,
29
+ colorSchemeName,
30
+ blockSize,
31
+ highResScaleFactor,
32
+ colStats,
33
+ } = model
34
+ const theme = useTheme()
35
+
36
+ const contrastScheme = useMemo(
37
+ () => colorContrast(colorScheme, theme),
38
+ [colorScheme, theme],
39
+ )
40
+
41
+ const ref = useRef<HTMLCanvasElement>(null)
42
+ useEffect(() => {
43
+ if (!ref.current) {
44
+ return
45
+ }
46
+
47
+ const ctx = ref.current.getContext('2d')
48
+ if (!ctx) {
49
+ return
50
+ }
51
+
52
+ ctx.resetTransform()
53
+ ctx.scale(highResScaleFactor, highResScaleFactor)
54
+ ctx.clearRect(0, 0, blockSize, blockSize)
55
+ ctx.translate(-offsetX, rowHeight / 2 - offsetY)
56
+ ctx.textAlign = 'center'
57
+ ctx.font = ctx.font.replace(/\d+px/, `${Math.max(8, rowHeight - 8)}px`)
58
+
59
+ const leaves = hierarchy.leaves()
60
+ const b = blockSize
61
+
62
+ // slice vertical rows, e.g. tree leaves, avoid negative slice
63
+ const yStart = Math.max(0, Math.floor((offsetY - rowHeight) / rowHeight))
64
+ const yEnd = Math.max(0, Math.ceil((offsetY + b + rowHeight) / rowHeight))
65
+
66
+ // slice horizontal visible letters, avoid negative slice
67
+ const xStart = Math.max(0, Math.floor(offsetX / colWidth))
68
+ const xEnd = Math.max(0, Math.ceil((offsetX + b) / colWidth))
69
+ const visibleLeaves = leaves.slice(yStart, yEnd)
70
+ for (const node of visibleLeaves) {
71
+ const {
72
+ // @ts-expect-error
73
+ x: y,
74
+ data: { name },
75
+ } = node
76
+
77
+ const str = columns[name]?.slice(xStart, xEnd)
78
+ for (let i = 0; i < str?.length; i++) {
79
+ const letter = str[i]
80
+ const color =
81
+ colorSchemeName === 'clustalx_protein_dynamic'
82
+ ? getClustalXColor(colStats[xStart + i], model, name, xStart + i)
83
+ : colorSchemeName === 'percent_identity_dynamic'
84
+ ? getPercentIdentityColor(
85
+ colStats[xStart + i],
86
+ model,
87
+ name,
88
+ xStart + i,
89
+ )
90
+ : colorScheme[letter.toUpperCase()]
91
+ if (bgColor) {
92
+ const x = i * colWidth + offsetX - (offsetX % colWidth)
93
+ ctx.fillStyle = color || 'white'
94
+ ctx.fillRect(x, y - rowHeight, colWidth, rowHeight)
95
+ }
96
+ }
97
+ }
98
+
99
+ if (rowHeight >= 5 && colWidth > rowHeight / 2) {
100
+ for (const node of visibleLeaves) {
101
+ const {
102
+ // @ts-expect-error
103
+ x: y,
104
+ data: { name },
105
+ } = node
106
+
107
+ const str = columns[name]?.slice(xStart, xEnd)
108
+ for (let i = 0; i < str?.length; i++) {
109
+ const letter = str[i]
110
+ const color = colorScheme[letter.toUpperCase()]
111
+ const contrast = contrastScheme[letter.toUpperCase()] || 'black'
112
+ const x = i * colWidth + offsetX - (offsetX % colWidth)
113
+
114
+ // note: -rowHeight/4 matches +rowHeight/4 in tree
115
+ ctx.fillStyle = bgColor ? contrast : color || 'black'
116
+ ctx.fillText(letter, x + colWidth / 2, y - rowHeight / 4)
117
+ }
118
+ }
119
+ }
120
+ // eslint-disable-next-line react-hooks/exhaustive-deps
121
+ }, [
122
+ MSA,
123
+ highResScaleFactor,
124
+ columns,
125
+ colorScheme,
126
+ contrastScheme,
127
+ bgColor,
128
+ rowHeight,
129
+ colWidth,
130
+ hierarchy,
131
+ offsetX,
132
+ offsetY,
133
+ blockSize,
134
+ ])
135
+ return (
136
+ <canvas
137
+ ref={ref}
138
+ onMouseMove={event => {
139
+ if (!ref.current) {
140
+ return
141
+ }
142
+ const { left, top } = ref.current.getBoundingClientRect()
143
+ const mouseX = event.clientX - left + offsetX
144
+ const mouseY = event.clientY - top + offsetY
145
+ model.setMousePos(
146
+ Math.floor(mouseX / colWidth) + 1,
147
+ Math.floor(mouseY / rowHeight),
148
+ )
149
+ }}
150
+ onMouseLeave={() => model.setMousePos()}
151
+ width={blockSize * highResScaleFactor}
152
+ height={blockSize * highResScaleFactor}
153
+ style={{
154
+ position: 'absolute',
155
+ top: scrollY + offsetY,
156
+ left: scrollX + offsetX,
157
+ width: blockSize,
158
+ height: blockSize,
159
+ }}
160
+ />
161
+ )
162
+ })
163
+
164
+ export default MSABlock
@@ -1,166 +1,11 @@
1
- import React, { useEffect, useState, useRef, useMemo } from 'react'
2
- import { Typography, CircularProgress, useTheme } from '@mui/material'
1
+ import React, { useEffect, useState, useRef } from 'react'
2
+ import { Typography, CircularProgress } from '@mui/material'
3
3
  import { observer } from 'mobx-react'
4
4
  import normalizeWheel from 'normalize-wheel'
5
5
 
6
6
  // locals
7
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
- })
8
+ import MSABlock from './MSABlock'
164
9
 
165
10
  const MSACanvas = observer(function ({ model }: { model: MsaViewModel }) {
166
11
  const { MSA, msaFilehandle, height, msaAreaWidth, blocks2d } = model
@@ -0,0 +1,87 @@
1
+ import React, { useEffect, useRef } from 'react'
2
+ import { observer } from 'mobx-react'
3
+
4
+ // locals
5
+ import { MsaViewModel } from '../model'
6
+ import { sum } from '@jbrowse/core/util'
7
+
8
+ const MSAMouseoverCanvas = observer(function ({
9
+ model,
10
+ }: {
11
+ model: MsaViewModel
12
+ }) {
13
+ const ref = useRef<HTMLCanvasElement>(null)
14
+ const {
15
+ height,
16
+ width,
17
+ treeAreaWidth,
18
+ resizeHandleWidth,
19
+ rulerHeight,
20
+ turnedOnTracks,
21
+ scrollX,
22
+ scrollY,
23
+ mouseCol,
24
+ mouseRow,
25
+ rowHeight,
26
+ colWidth,
27
+ } = model
28
+ const totalTrackAreaHeight = sum(turnedOnTracks.map(r => r.model.height))
29
+ useEffect(() => {
30
+ if (!ref.current) {
31
+ return
32
+ }
33
+
34
+ const ctx = ref.current.getContext('2d')
35
+ if (!ctx) {
36
+ return
37
+ }
38
+
39
+ ctx.resetTransform()
40
+ ctx.clearRect(0, 0, width, height)
41
+
42
+ ctx.fillStyle = 'rgba(0,0,0,0.15)'
43
+ if (mouseCol !== undefined) {
44
+ const x =
45
+ (mouseCol - 1) * colWidth + scrollX + treeAreaWidth + resizeHandleWidth
46
+
47
+ ctx.fillRect(x, 0, colWidth, height)
48
+ }
49
+ if (mouseRow !== undefined) {
50
+ const y =
51
+ mouseRow * rowHeight + scrollY + rulerHeight + totalTrackAreaHeight
52
+ ctx.fillRect(treeAreaWidth + resizeHandleWidth, y, width, rowHeight)
53
+ }
54
+ }, [
55
+ mouseCol,
56
+ colWidth,
57
+ scrollY,
58
+ totalTrackAreaHeight,
59
+ mouseRow,
60
+ rowHeight,
61
+ rulerHeight,
62
+ scrollX,
63
+ height,
64
+ resizeHandleWidth,
65
+ treeAreaWidth,
66
+ width,
67
+ ])
68
+
69
+ return (
70
+ <canvas
71
+ ref={ref}
72
+ width={width}
73
+ height={height}
74
+ style={{
75
+ position: 'absolute',
76
+ top: 0,
77
+ left: 0,
78
+ width,
79
+ height,
80
+ zIndex: 1000,
81
+ pointerEvents: 'none',
82
+ }}
83
+ />
84
+ )
85
+ })
86
+
87
+ export default MSAMouseoverCanvas
@@ -1,4 +1,4 @@
1
- import React, { useRef, useEffect } from 'react'
1
+ import React, { lazy, Suspense } from 'react'
2
2
 
3
3
  import { observer } from 'mobx-react'
4
4
  import { Typography } from '@mui/material'
@@ -12,61 +12,13 @@ import Ruler from './Ruler'
12
12
  import TreeRuler from './TreeRuler'
13
13
  import Header from './Header'
14
14
  import Track from './Track'
15
- import AnnotationDialog from './AnnotationDlg'
16
15
 
17
16
  import { HorizontalResizeHandle, VerticalResizeHandle } from './ResizeHandles'
18
17
  import { MsaViewModel } from '../model'
18
+ import MSAMouseoverCanvas from './MSAMouseoverCanvas'
19
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
20
+ const AnnotationDialog = lazy(() => import('./dialogs/AnnotationDlg'))
31
21
 
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
22
  export default observer(function ({ model }: { model: MsaViewModel }) {
71
23
  const { done, initialized, treeAreaWidth, height, turnedOnTracks } = model
72
24
 
@@ -102,7 +54,7 @@ export default observer(function ({ model }: { model: MsaViewModel }) {
102
54
  </div>
103
55
  <VerticalResizeHandle model={model} />
104
56
  <MSACanvas model={model} />
105
- <MouseoverCanvas model={model} />
57
+ <MSAMouseoverCanvas model={model} />
106
58
  </div>
107
59
  </div>
108
60
  </div>
@@ -112,20 +64,24 @@ export default observer(function ({ model }: { model: MsaViewModel }) {
112
64
  )}
113
65
 
114
66
  {model.DialogComponent ? (
115
- <model.DialogComponent
116
- {...(model.DialogProps || {})}
117
- onClose={() => {
118
- model.setDialogComponent(undefined, undefined)
119
- }}
120
- />
67
+ <Suspense fallback={null}>
68
+ <model.DialogComponent
69
+ {...(model.DialogProps || {})}
70
+ onClose={() => {
71
+ model.setDialogComponent(undefined, undefined)
72
+ }}
73
+ />
74
+ </Suspense>
121
75
  ) : null}
122
76
 
123
77
  {model.annotPos ? (
124
- <AnnotationDialog
125
- data={model.annotPos}
126
- model={model}
127
- onClose={() => model.clearAnnotPos()}
128
- />
78
+ <Suspense fallback={null}>
79
+ <AnnotationDialog
80
+ data={model.annotPos}
81
+ model={model}
82
+ onClose={() => model.clearAnnotPos()}
83
+ />
84
+ </Suspense>
129
85
  ) : null}
130
86
  </div>
131
87
  )
@@ -0,0 +1,33 @@
1
+ import React from 'react'
2
+ import { observer } from 'mobx-react'
3
+ import { Select } from '@mui/material'
4
+
5
+ // locals
6
+ import { MsaViewModel } from '../model'
7
+
8
+ const MultiAlignmentSelector = observer(function ({
9
+ model,
10
+ }: {
11
+ model: MsaViewModel
12
+ }) {
13
+ const { currentAlignment, alignmentNames } = model
14
+ return alignmentNames.length > 0 ? (
15
+ <Select
16
+ native
17
+ value={currentAlignment}
18
+ size="small"
19
+ onChange={event => {
20
+ model.setCurrentAlignment(+(event.target.value as string))
21
+ model.setScrollX(0)
22
+ model.setScrollY(0)
23
+ }}
24
+ >
25
+ {alignmentNames.map((option, index) => (
26
+ <option key={`${option}-${index}`} value={index}>
27
+ {option}
28
+ </option>
29
+ ))}
30
+ </Select>
31
+ ) : null
32
+ })
33
+ export default MultiAlignmentSelector
@@ -142,7 +142,6 @@ function Rubberband({
142
142
  setCurrentX(undefined)
143
143
  }
144
144
 
145
- // eslint-disable-next-line @typescript-eslint/ban-types
146
145
  function handleMenuItemClick(_: unknown, callback: Function) {
147
146
  callback()
148
147
  handleClose()
@@ -80,6 +80,7 @@ const Ruler = observer(function ({ model }: { model: MsaViewModel }) {
80
80
  MSA,
81
81
  colWidth,
82
82
  msaAreaWidth,
83
+ rulerHeight,
83
84
  resizeHandleWidth,
84
85
  scrollX,
85
86
  blocksX,
@@ -96,7 +97,7 @@ const Ruler = observer(function ({ model }: { model: MsaViewModel }) {
96
97
  width: msaAreaWidth,
97
98
  cursor: 'crosshair',
98
99
  overflow: 'hidden',
99
- height: 20,
100
+ height: rulerHeight,
100
101
  background: '#ccc',
101
102
  }}
102
103
  >
@@ -1,4 +1,4 @@
1
- import React, { useState, useRef, useEffect } from 'react'
1
+ import React, { useState, useRef, useEffect, lazy, Suspense } from 'react'
2
2
  import normalizeWheel from 'normalize-wheel'
3
3
  import { observer } from 'mobx-react'
4
4
  import { IconButton, Menu, MenuItem } from '@mui/material'
@@ -9,7 +9,8 @@ import ArrowDropDownIcon from '@mui/icons-material/ArrowDropDown'
9
9
 
10
10
  // locals
11
11
  import { MsaViewModel } from '../model'
12
- import TrackInfoDialog from './TrackInfoDlg'
12
+
13
+ const TrackInfoDialog = lazy(() => import('./dialogs/TrackInfoDlg'))
13
14
 
14
15
  const useStyles = makeStyles()({
15
16
  button: {
@@ -84,10 +85,12 @@ export const TrackLabel = observer(function ({
84
85
  </Menu>
85
86
  ) : null}
86
87
  {trackInfoDlgOpen ? (
87
- <TrackInfoDialog
88
- model={track.model}
89
- onClose={() => setTrackInfoDlgOpen(false)}
90
- />
88
+ <Suspense fallback={null}>
89
+ <TrackInfoDialog
90
+ model={track.model}
91
+ onClose={() => setTrackInfoDlgOpen(false)}
92
+ />
93
+ </Suspense>
91
94
  ) : null}
92
95
  </div>
93
96
  )
@@ -0,0 +1,67 @@
1
+ import React 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
+ interface Node {
9
+ x: number
10
+ y: number
11
+ name: string
12
+ id: string
13
+ }
14
+
15
+ const TreeBranchMenu = observer(function ({
16
+ node,
17
+ model,
18
+ onClose,
19
+ }: {
20
+ node: Node
21
+ model: MsaViewModel
22
+ onClose: () => void
23
+ }) {
24
+ return (
25
+ <Menu
26
+ anchorReference="anchorPosition"
27
+ anchorPosition={{
28
+ left: node.x,
29
+ top: node.y,
30
+ }}
31
+ transitionDuration={0}
32
+ keepMounted
33
+ open={Boolean(node)}
34
+ onClose={onClose}
35
+ >
36
+ <MenuItem dense disabled>
37
+ {node.name}
38
+ </MenuItem>
39
+ <MenuItem
40
+ dense
41
+ onClick={() => {
42
+ model.toggleCollapsed(node.id)
43
+ onClose()
44
+ }}
45
+ >
46
+ {model.collapsed.includes(node.id)
47
+ ? 'Expand this node'
48
+ : 'Collapse this node'}
49
+ </MenuItem>
50
+ <MenuItem
51
+ dense
52
+ onClick={() => {
53
+ model.showOnly === node.id
54
+ ? model.setShowOnly(undefined)
55
+ : model.setShowOnly(node.id)
56
+ onClose()
57
+ }}
58
+ >
59
+ {model.showOnly === node.id
60
+ ? 'Disable show only this node'
61
+ : 'Show only this node'}
62
+ </MenuItem>
63
+ </Menu>
64
+ )
65
+ })
66
+
67
+ export default TreeBranchMenu