react-msaview 5.1.1 → 5.2.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 (190) hide show
  1. package/README.md +41 -8
  2. package/bundle/index.js +17 -17
  3. package/bundle/index.js.LICENSE.txt +1 -1
  4. package/bundle/index.js.map +1 -1
  5. package/dist/calculateBlocks.d.ts +4 -10
  6. package/dist/calculateBlocks.js +12 -20
  7. package/dist/calculateBlocks.js.map +1 -1
  8. package/dist/components/ConservationTrack.js +20 -37
  9. package/dist/components/ConservationTrack.js.map +1 -1
  10. package/dist/components/MSAView.js +8 -1
  11. package/dist/components/MSAView.js.map +1 -1
  12. package/dist/components/ResizeHandles.d.ts +3 -0
  13. package/dist/components/ResizeHandles.js +60 -105
  14. package/dist/components/ResizeHandles.js.map +1 -1
  15. package/dist/components/TextTrack.js +25 -34
  16. package/dist/components/TextTrack.js.map +1 -1
  17. package/dist/components/Track.js +1 -1
  18. package/dist/components/Track.js.map +1 -1
  19. package/dist/components/VerticalScrollbar.js +2 -2
  20. package/dist/components/VerticalScrollbar.js.map +1 -1
  21. package/dist/components/dialogs/ExportSVGDialog.js +20 -20
  22. package/dist/components/dialogs/ExportSVGDialog.js.map +1 -1
  23. package/dist/components/dialogs/InterProScanDialog.js +6 -1
  24. package/dist/components/dialogs/InterProScanDialog.js.map +1 -1
  25. package/dist/components/dialogs/UserProvidedDomainsDialog.js +1 -2
  26. package/dist/components/dialogs/UserProvidedDomainsDialog.js.map +1 -1
  27. package/dist/components/header/ColorSchemeMenu.js +1 -1
  28. package/dist/components/header/ColorSchemeMenu.js.map +1 -1
  29. package/dist/components/header/FileMenu.js +3 -2
  30. package/dist/components/header/FileMenu.js.map +1 -1
  31. package/dist/components/header/Header.js +1 -1
  32. package/dist/components/header/HeaderInfoArea.js +28 -8
  33. package/dist/components/header/HeaderInfoArea.js.map +1 -1
  34. package/dist/components/header/MSASettingsMenu.js +1 -1
  35. package/dist/components/header/MSASettingsMenu.js.map +1 -1
  36. package/dist/components/header/TreeSettingsMenu.js +1 -1
  37. package/dist/components/header/TreeSettingsMenu.js.map +1 -1
  38. package/dist/components/header/ZoomControls.js +7 -2
  39. package/dist/components/header/ZoomControls.js.map +1 -1
  40. package/dist/components/header/ZoomMenu.js +9 -0
  41. package/dist/components/header/ZoomMenu.js.map +1 -1
  42. package/dist/components/header/settingsMenuItems.js +24 -8
  43. package/dist/components/header/settingsMenuItems.js.map +1 -1
  44. package/dist/components/minimap/Minimap.js +6 -17
  45. package/dist/components/minimap/Minimap.js.map +1 -1
  46. package/dist/components/minimap/MinimapSVG.js +7 -17
  47. package/dist/components/minimap/MinimapSVG.js.map +1 -1
  48. package/dist/components/minimap/minimapLayout.d.ts +9 -0
  49. package/dist/components/minimap/minimapLayout.js +15 -0
  50. package/dist/components/minimap/minimapLayout.js.map +1 -0
  51. package/dist/components/msa/MSACanvas.js +6 -1
  52. package/dist/components/msa/MSACanvas.js.map +1 -1
  53. package/dist/components/msa/MSACanvasBlock.js +5 -1
  54. package/dist/components/msa/MSACanvasBlock.js.map +1 -1
  55. package/dist/components/msa/MSAMouseoverCanvas.js +2 -2
  56. package/dist/components/msa/MSAMouseoverCanvas.js.map +1 -1
  57. package/dist/components/msa/getVisibleLeaves.d.ts +6 -0
  58. package/dist/components/msa/getVisibleLeaves.js +9 -0
  59. package/dist/components/msa/getVisibleLeaves.js.map +1 -0
  60. package/dist/components/msa/renderBoxFeatureCanvasBlock.d.ts +2 -1
  61. package/dist/components/msa/renderBoxFeatureCanvasBlock.js +5 -6
  62. package/dist/components/msa/renderBoxFeatureCanvasBlock.js.map +1 -1
  63. package/dist/components/msa/renderMSABlock.d.ts +2 -1
  64. package/dist/components/msa/renderMSABlock.js +100 -88
  65. package/dist/components/msa/renderMSABlock.js.map +1 -1
  66. package/dist/components/msa/renderMSAMouseover.js +5 -4
  67. package/dist/components/msa/renderMSAMouseover.js.map +1 -1
  68. package/dist/components/msa/visibleColRange.d.ts +8 -0
  69. package/dist/components/msa/visibleColRange.js +10 -0
  70. package/dist/components/msa/visibleColRange.js.map +1 -0
  71. package/dist/components/renderCtx.d.ts +7 -0
  72. package/dist/components/renderCtx.js +2 -0
  73. package/dist/components/renderCtx.js.map +1 -0
  74. package/dist/components/tracks/renderTracksSvg.d.ts +6 -5
  75. package/dist/components/tracks/renderTracksSvg.js +13 -5
  76. package/dist/components/tracks/renderTracksSvg.js.map +1 -1
  77. package/dist/components/tree/TreeCanvas.js.map +1 -1
  78. package/dist/components/tree/TreeCanvasBlock.js +2 -10
  79. package/dist/components/tree/TreeCanvasBlock.js.map +1 -1
  80. package/dist/components/tree/renderTreeCanvas.d.ts +5 -4
  81. package/dist/components/tree/renderTreeCanvas.js +18 -26
  82. package/dist/components/tree/renderTreeCanvas.js.map +1 -1
  83. package/dist/components/util.d.ts +0 -14
  84. package/dist/components/util.js +0 -58
  85. package/dist/components/util.js.map +1 -1
  86. package/dist/constants.d.ts +4 -0
  87. package/dist/constants.js +7 -0
  88. package/dist/constants.js.map +1 -1
  89. package/dist/flatToTree.js +6 -0
  90. package/dist/flatToTree.js.map +1 -1
  91. package/dist/hierarchy.d.ts +3 -1
  92. package/dist/hierarchy.js +142 -89
  93. package/dist/hierarchy.js.map +1 -1
  94. package/dist/index.d.ts +1 -0
  95. package/dist/index.js +8 -0
  96. package/dist/index.js.map +1 -1
  97. package/dist/launchInterProScan.js +22 -9
  98. package/dist/launchInterProScan.js.map +1 -1
  99. package/dist/measureTextCanvas.js +2 -1
  100. package/dist/measureTextCanvas.js.map +1 -1
  101. package/dist/model.d.ts +234 -176
  102. package/dist/model.js +285 -259
  103. package/dist/model.js.map +1 -1
  104. package/dist/parseAsn1.js +8 -2
  105. package/dist/parseAsn1.js.map +1 -1
  106. package/dist/renderToSvg.d.ts +1 -1
  107. package/dist/renderToSvg.js +43 -46
  108. package/dist/renderToSvg.js.map +1 -1
  109. package/dist/rowCoordinateCalculations.d.ts +0 -9
  110. package/dist/rowCoordinateCalculations.js +0 -20
  111. package/dist/rowCoordinateCalculations.js.map +1 -1
  112. package/dist/setFontSize.d.ts +3 -0
  113. package/dist/setFontSize.js +14 -0
  114. package/dist/setFontSize.js.map +1 -0
  115. package/dist/useDevicePixelRatio.d.ts +1 -0
  116. package/dist/useDevicePixelRatio.js +27 -0
  117. package/dist/useDevicePixelRatio.js.map +1 -0
  118. package/dist/useWheelScroll.d.ts +3 -1
  119. package/dist/useWheelScroll.js +64 -22
  120. package/dist/useWheelScroll.js.map +1 -1
  121. package/dist/util.d.ts +5 -0
  122. package/dist/util.js +59 -1
  123. package/dist/util.js.map +1 -1
  124. package/dist/version.d.ts +1 -1
  125. package/dist/version.js +1 -1
  126. package/dist/webpack.js +0 -1
  127. package/dist/webpack.js.map +1 -1
  128. package/dist/wheelZoom.d.ts +4 -0
  129. package/dist/wheelZoom.js +43 -0
  130. package/dist/wheelZoom.js.map +1 -0
  131. package/package.json +5 -5
  132. package/src/calculateBlocks.ts +18 -51
  133. package/src/components/ConservationTrack.tsx +20 -45
  134. package/src/components/MSAView.tsx +10 -1
  135. package/src/components/ResizeHandles.tsx +101 -113
  136. package/src/components/TextTrack.tsx +31 -38
  137. package/src/components/Track.tsx +1 -1
  138. package/src/components/VerticalScrollbar.tsx +2 -2
  139. package/src/components/dialogs/ExportSVGDialog.tsx +27 -17
  140. package/src/components/dialogs/InterProScanDialog.tsx +6 -1
  141. package/src/components/dialogs/UserProvidedDomainsDialog.tsx +1 -2
  142. package/src/components/header/ColorSchemeMenu.tsx +1 -0
  143. package/src/components/header/FileMenu.tsx +3 -1
  144. package/src/components/header/Header.tsx +1 -1
  145. package/src/components/header/HeaderInfoArea.tsx +32 -7
  146. package/src/components/header/MSASettingsMenu.tsx +1 -0
  147. package/src/components/header/TreeSettingsMenu.tsx +1 -0
  148. package/src/components/header/ZoomControls.tsx +14 -1
  149. package/src/components/header/ZoomMenu.tsx +10 -0
  150. package/src/components/header/settingsMenuItems.ts +24 -8
  151. package/src/components/minimap/Minimap.tsx +6 -20
  152. package/src/components/minimap/MinimapSVG.tsx +10 -27
  153. package/src/components/minimap/minimapLayout.ts +17 -0
  154. package/src/components/msa/MSACanvas.tsx +9 -0
  155. package/src/components/msa/MSACanvasBlock.tsx +7 -1
  156. package/src/components/msa/MSAMouseoverCanvas.tsx +4 -3
  157. package/src/components/msa/getVisibleLeaves.ts +21 -0
  158. package/src/components/msa/renderBoxFeatureCanvasBlock.ts +9 -9
  159. package/src/components/msa/renderMSABlock.ts +123 -125
  160. package/src/components/msa/renderMSAMouseover.ts +5 -4
  161. package/src/components/msa/visibleColRange.ts +17 -0
  162. package/src/components/renderCtx.ts +29 -0
  163. package/src/components/tracks/renderTracksSvg.ts +20 -10
  164. package/src/components/tree/TreeCanvas.tsx +3 -1
  165. package/src/components/tree/TreeCanvasBlock.tsx +2 -12
  166. package/src/components/tree/renderTreeCanvas.ts +27 -30
  167. package/src/components/util.ts +0 -81
  168. package/src/constants.ts +8 -0
  169. package/src/flatToTree.test.ts +30 -0
  170. package/src/flatToTree.ts +10 -1
  171. package/src/hierarchy.test.ts +53 -1
  172. package/src/hierarchy.ts +153 -100
  173. package/src/index.ts +8 -0
  174. package/src/launchInterProScan.ts +33 -11
  175. package/src/measureTextCanvas.ts +3 -1
  176. package/src/model.ts +359 -306
  177. package/src/parseAsn1.test.ts +6 -0
  178. package/src/parseAsn1.ts +11 -3
  179. package/src/renderToSvg.tsx +99 -95
  180. package/src/rowCoordinateCalculations.test.ts +0 -51
  181. package/src/rowCoordinateCalculations.ts +0 -23
  182. package/src/setFontSize.test.ts +33 -0
  183. package/src/setFontSize.ts +20 -0
  184. package/src/useDevicePixelRatio.ts +29 -0
  185. package/src/useWheelScroll.ts +79 -21
  186. package/src/util.ts +60 -1
  187. package/src/version.ts +1 -1
  188. package/src/webpack.ts +0 -1
  189. package/src/wheelZoom.test.ts +46 -0
  190. package/src/wheelZoom.ts +47 -0
@@ -1,74 +1,49 @@
1
- import React, { useEffect, useRef, useState } from 'react'
1
+ import React, { useCallback, useState } from 'react'
2
2
 
3
3
  import { observer } from 'mobx-react'
4
4
 
5
+ import { useDragScroll } from '../useDragScroll.ts'
6
+
5
7
  import type { MsaViewModel } from '../model.ts'
6
8
 
9
+ const handleBg = 'rgba(200,200,200)'
10
+ const handleBgHover = 'rgba(150,150,150)'
11
+
7
12
  export const VerticalResizeHandle = observer(function ({
8
13
  model,
9
14
  }: {
10
15
  model: MsaViewModel
11
16
  }) {
12
17
  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])
18
+ const [hovered, setHovered] = useState(false)
19
+ const { startDrag } = useDragScroll(
20
+ 'x',
21
+ useCallback(
22
+ (delta: number, startWidth: number) => {
23
+ model.setTreeAreaWidth(startWidth + delta)
24
+ },
25
+ [model],
26
+ ),
27
+ )
56
28
 
57
29
  return (
58
- <div>
59
- <div
60
- onMouseDown={() => {
61
- setMouseDragging(true)
62
- }}
63
- style={{
64
- cursor: 'ew-resize',
65
- height: '100%',
66
- width: resizeHandleWidth,
67
- background: 'rgba(200,200,200)',
68
- position: 'relative',
69
- }}
70
- />
71
- </div>
30
+ <div
31
+ onMouseDown={event => {
32
+ startDrag(event, model.treeAreaWidth)
33
+ }}
34
+ onMouseEnter={() => {
35
+ setHovered(true)
36
+ }}
37
+ onMouseLeave={() => {
38
+ setHovered(false)
39
+ }}
40
+ style={{
41
+ cursor: 'ew-resize',
42
+ width: resizeHandleWidth,
43
+ background: hovered ? handleBgHover : handleBg,
44
+ position: 'relative',
45
+ }}
46
+ />
72
47
  )
73
48
  })
74
49
 
@@ -78,64 +53,77 @@ export const HorizontalResizeHandle = observer(function ({
78
53
  model: MsaViewModel
79
54
  }) {
80
55
  const { resizeHandleWidth } = model
81
- const [mouseDragging, setMouseDragging] = useState(false)
82
- const scheduled = useRef(false)
83
- const prevY = useRef(0)
56
+ const [hovered, setHovered] = useState(false)
57
+ const { startDrag } = useDragScroll(
58
+ 'y',
59
+ useCallback(
60
+ (delta: number, startHeight: number) => {
61
+ model.setHeight(startHeight + delta)
62
+ },
63
+ [model],
64
+ ),
65
+ )
84
66
 
85
- useEffect(() => {
86
- function globalMouseMove(event: MouseEvent) {
87
- event.preventDefault()
88
- const currY = event.clientY
89
- if (prevY.current === 0) {
90
- prevY.current = event.clientY
91
- } else {
92
- const distance = currY - prevY.current
93
- if (distance) {
94
- // use rAF to make it so multiple event handlers aren't fired per-frame
95
- // see https://calendar.perfplanet.com/2013/the-runtime-performance-checklist/
96
- if (!scheduled.current) {
97
- scheduled.current = true
98
- window.requestAnimationFrame(() => {
99
- model.setHeight(model.height + distance)
100
- scheduled.current = false
101
- prevY.current = event.clientY
102
- })
103
- }
104
- }
105
- }
106
- }
67
+ return (
68
+ <div
69
+ onMouseDown={event => {
70
+ startDrag(event, model.height)
71
+ }}
72
+ onMouseEnter={() => {
73
+ setHovered(true)
74
+ }}
75
+ onMouseLeave={() => {
76
+ setHovered(false)
77
+ }}
78
+ style={{
79
+ cursor: 'ns-resize',
80
+ width: '100%',
81
+ height: resizeHandleWidth,
82
+ background: hovered ? handleBgHover : handleBg,
83
+ position: 'relative',
84
+ }}
85
+ />
86
+ )
87
+ })
107
88
 
108
- function globalMouseUp() {
109
- prevY.current = 0
110
- if (mouseDragging) {
111
- setMouseDragging(false)
112
- }
113
- }
114
- if (mouseDragging) {
115
- document.addEventListener('mousemove', globalMouseMove, true)
116
- document.addEventListener('mouseup', globalMouseUp, true)
117
- return () => {
118
- document.removeEventListener('mousemove', globalMouseMove, true)
119
- document.removeEventListener('mouseup', globalMouseUp, true)
120
- }
121
- }
122
- return () => {}
123
- }, [mouseDragging, model])
89
+ export const ConservationTrackResizeHandle = observer(function ({
90
+ model,
91
+ }: {
92
+ model: MsaViewModel
93
+ }) {
94
+ const { resizeHandleWidth } = model
95
+ const [hovered, setHovered] = useState(false)
96
+ const { startDrag } = useDragScroll(
97
+ 'y',
98
+ useCallback(
99
+ (delta: number, startHeight: number) => {
100
+ model.setConservationTrackHeight(Math.max(10, startHeight + delta))
101
+ },
102
+ [model],
103
+ ),
104
+ )
124
105
 
125
106
  return (
126
- <div>
127
- <div
128
- onMouseDown={() => {
129
- setMouseDragging(true)
130
- }}
131
- style={{
132
- cursor: 'ns-resize',
133
- width: '100%',
134
- height: resizeHandleWidth,
135
- background: 'rgba(200,200,200)',
136
- position: 'relative',
137
- }}
138
- />
139
- </div>
107
+ <div
108
+ onMouseDown={event => {
109
+ startDrag(event, model.conservationTrackHeight)
110
+ }}
111
+ onMouseEnter={() => {
112
+ setHovered(true)
113
+ }}
114
+ onMouseLeave={() => {
115
+ setHovered(false)
116
+ }}
117
+ style={{
118
+ position: 'absolute',
119
+ bottom: 0,
120
+ left: 0,
121
+ cursor: 'ns-resize',
122
+ width: '100%',
123
+ height: resizeHandleWidth,
124
+ zIndex: 1,
125
+ background: hovered ? handleBgHover : handleBg,
126
+ }}
127
+ />
140
128
  )
141
129
  })
@@ -1,7 +1,9 @@
1
1
  import React, { useEffect, useRef } from 'react'
2
2
 
3
+ import { autorun } from 'mobx'
3
4
  import { observer } from 'mobx-react'
4
5
 
6
+ import { setFontSize } from '../setFontSize.ts'
5
7
  import { useColorContrast } from '../useColorContrast.ts'
6
8
  import { drawTextTrackContent } from './tracks/renderTracksSvg.ts'
7
9
 
@@ -20,10 +22,7 @@ const AnnotationBlock = observer(function ({
20
22
  const {
21
23
  blockSize,
22
24
  scrollX,
23
- bgColor,
24
25
  colorScheme: modelColorScheme,
25
- colWidth,
26
- fontSize,
27
26
  rowHeight,
28
27
  highResScaleFactor,
29
28
  } = model
@@ -31,49 +30,43 @@ const AnnotationBlock = observer(function ({
31
30
  model: { customColorScheme, data },
32
31
  } = track
33
32
 
34
- const colorScheme = customColorScheme || modelColorScheme
33
+ const colorScheme = customColorScheme ?? modelColorScheme
35
34
  const ref = useRef<HTMLCanvasElement>(null)
36
35
  const { contrastScheme } = useColorContrast(colorScheme)
37
36
  useEffect(() => {
38
- if (!ref.current) {
39
- return
40
- }
41
-
42
- const ctx = ref.current.getContext('2d')
37
+ const ctx = ref.current?.getContext('2d')
43
38
  if (!ctx) {
44
39
  return
45
40
  }
41
+ return autorun(() => {
42
+ const {
43
+ blockSize,
44
+ bgColor,
45
+ colWidth,
46
+ fontSize,
47
+ rowHeight,
48
+ highResScaleFactor,
49
+ } = model
50
+ ctx.resetTransform()
51
+ ctx.scale(highResScaleFactor, highResScaleFactor)
52
+ ctx.clearRect(0, 0, blockSize, rowHeight)
53
+ ctx.translate(-offsetX, 0)
54
+ ctx.textAlign = 'center'
55
+ setFontSize(ctx, fontSize)
46
56
 
47
- ctx.resetTransform()
48
- ctx.scale(highResScaleFactor, highResScaleFactor)
49
- ctx.clearRect(0, 0, blockSize, rowHeight)
50
- ctx.translate(-offsetX, 0)
51
- ctx.textAlign = 'center'
52
- ctx.font = ctx.font.replace(/\d+px/, `${fontSize}px`)
53
-
54
- drawTextTrackContent({
55
- ctx,
56
- data,
57
- colorScheme,
58
- contrastScheme,
59
- bgColor,
60
- colWidth,
61
- rowHeight,
62
- offsetX,
63
- blockSize,
57
+ drawTextTrackContent({
58
+ ctx,
59
+ data,
60
+ colorScheme,
61
+ contrastScheme,
62
+ bgColor,
63
+ colWidth,
64
+ rowHeight,
65
+ offsetX,
66
+ blockSize,
67
+ })
64
68
  })
65
- }, [
66
- fontSize,
67
- bgColor,
68
- blockSize,
69
- colWidth,
70
- rowHeight,
71
- offsetX,
72
- contrastScheme,
73
- colorScheme,
74
- highResScaleFactor,
75
- data,
76
- ])
69
+ }, [model, offsetX, contrastScheme, colorScheme, data])
77
70
  return (
78
71
  <canvas
79
72
  ref={ref}
@@ -31,7 +31,7 @@ const TrackLabel = observer(function TrackLabel({
31
31
  model: { name, height },
32
32
  } = track
33
33
  const { classes } = useStyles()
34
- const trackLabelHeight = Math.max(8, rowHeight - 8)
34
+ const trackLabelHeight = Math.min(height, Math.max(8, rowHeight - 8))
35
35
 
36
36
  return (
37
37
  <div
@@ -20,9 +20,9 @@ const VerticalScrollbar = observer(({ model }: { model: MsaViewModel }) => {
20
20
  'y',
21
21
  useCallback(
22
22
  (delta: number, startScroll: number) => {
23
- model.setScrollY(clamp(startScroll - delta / unit, -totalHeight, 0))
23
+ model.setScrollY(clamp(startScroll - delta / unit, model.maxScrollY, 0))
24
24
  },
25
- [model, unit, totalHeight],
25
+ [model, unit],
26
26
  ),
27
27
  )
28
28
  return (
@@ -28,13 +28,21 @@ export default function ExportSVGDialog({
28
28
  }) {
29
29
  const [includeMinimap, setIncludeMinimap] = useState(true)
30
30
  const [includeTracks, setIncludeTracks] = useState(true)
31
- const [exportType, setExportType] = useState('viewport')
31
+ const [exportType, setExportType] = useState<'entire' | 'viewport'>(
32
+ 'viewport',
33
+ )
32
34
  const [error, setError] = useState<unknown>()
33
35
  const theme = useTheme()
34
- const { totalWidth, totalHeight, treeAreaWidth, turnedOnTracks } = model
36
+ const {
37
+ totalWidth,
38
+ totalHeight,
39
+ treeAreaWidth,
40
+ turnedOnTracks,
41
+ totalTrackAreaHeight,
42
+ } = model
35
43
  const hasTracks = turnedOnTracks.length > 0
36
44
  const entireWidth = totalWidth + treeAreaWidth
37
- const entireHeight = totalHeight
45
+ const entireHeight = totalHeight + (includeTracks ? totalTrackAreaHeight : 0)
38
46
  const isLargeExport =
39
47
  exportType === 'entire' && (entireWidth > 10000 || entireHeight > 10000)
40
48
  return (
@@ -71,7 +79,10 @@ export default function ExportSVGDialog({
71
79
  <RadioGroup
72
80
  value={exportType}
73
81
  onChange={event => {
74
- setExportType(event.target.value)
82
+ const { value } = event.target
83
+ if (value === 'entire' || value === 'viewport') {
84
+ setExportType(value)
85
+ }
75
86
  }}
76
87
  >
77
88
  <FormControlLabel
@@ -99,22 +110,21 @@ export default function ExportSVGDialog({
99
110
  variant="contained"
100
111
  color="primary"
101
112
  onClick={() => {
102
- // eslint-disable-next-line @typescript-eslint/no-floating-promises
103
- ;(async () => {
104
- try {
105
- await model.exportSVG({
106
- theme,
107
- includeMinimap:
108
- exportType === 'entire' ? false : includeMinimap,
109
- includeTracks: hasTracks && includeTracks,
110
- exportType,
111
- })
113
+ void model
114
+ .exportSVG({
115
+ theme,
116
+ includeMinimap:
117
+ exportType === 'entire' ? false : includeMinimap,
118
+ includeTracks: hasTracks && includeTracks,
119
+ exportType,
120
+ })
121
+ .then(() => {
112
122
  onClose()
113
- } catch (e) {
123
+ })
124
+ .catch((e: unknown) => {
114
125
  console.error(e)
115
126
  setError(e)
116
- }
117
- })()
127
+ })
118
128
  }}
119
129
  >
120
130
  Submit
@@ -258,7 +258,12 @@ const InterProScanDialog = observer(function ({
258
258
  onProgress: arg => {
259
259
  model.setStatus(
260
260
  arg
261
- ? { ...arg, onCancel: () => { controller.abort() } }
261
+ ? {
262
+ ...arg,
263
+ onCancel: () => {
264
+ controller.abort()
265
+ },
266
+ }
262
267
  : undefined,
263
268
  )
264
269
  },
@@ -112,10 +112,9 @@ const UserProvidedDomainsDialog = observer(function ({
112
112
  ? JSON.parse(await file.text())
113
113
  : await jsonfetch(interProURL)
114
114
 
115
- model.setInterProAnnotations(
115
+ model.setDomains(
116
116
  Object.fromEntries(ret.results.map(r => [r.xref[0]!.id, r])),
117
117
  )
118
- model.setShowDomains(true)
119
118
  getSession(model).notify(
120
119
  'Loaded interproscan results',
121
120
  'success',
@@ -12,6 +12,7 @@ const ColorSchemeMenu = observer(function ({ model }: { model: MsaViewModel }) {
12
12
  const { colorSchemeName } = model
13
13
  return (
14
14
  <CascadingMenuButton
15
+ data-testid="color_scheme_menu"
15
16
  closeAfterItemClick
16
17
  anchorOrigin={{ vertical: 'bottom', horizontal: 'left' }}
17
18
  transformOrigin={{ vertical: 'top', horizontal: 'left' }}
@@ -3,6 +3,7 @@ import React, { lazy } from 'react'
3
3
  import CascadingMenuButton from '@jbrowse/core/ui/CascadingMenuButton'
4
4
  import Assignment from '@mui/icons-material/Assignment'
5
5
  import FolderOpen from '@mui/icons-material/FolderOpen'
6
+ import Menu from '@mui/icons-material/Menu'
6
7
  import PhotoCamera from '@mui/icons-material/PhotoCamera'
7
8
  import Settings from '@mui/icons-material/Settings'
8
9
  import { observer } from 'mobx-react'
@@ -18,6 +19,7 @@ const SettingsDialog = lazy(() => import('../dialogs/SettingsDialog.tsx'))
18
19
  const FileMenu = observer(({ model }: { model: MsaViewModel }) => {
19
20
  return (
20
21
  <CascadingMenuButton
22
+ data-testid="file_menu"
21
23
  menuItems={[
22
24
  {
23
25
  label: 'Return to import form',
@@ -76,7 +78,7 @@ const FileMenu = observer(({ model }: { model: MsaViewModel }) => {
76
78
  anchorOrigin={{ vertical: 'bottom', horizontal: 'left' }}
77
79
  transformOrigin={{ vertical: 'top', horizontal: 'left' }}
78
80
  >
79
- <FolderOpen />
81
+ <Menu />
80
82
  </CascadingMenuButton>
81
83
  )
82
84
  })
@@ -23,7 +23,7 @@ const AboutDialog = lazy(() => import('../dialogs/AboutDialog.tsx'))
23
23
  const Header = observer(function ({ model }: { model: MsaViewModel }) {
24
24
  const [ref, { height }] = useMeasure()
25
25
  useEffect(() => {
26
- model.setHeaderHeight(height || 0)
26
+ model.setHeaderHeight(height ?? 0)
27
27
  }, [model, height])
28
28
  return (
29
29
  <div ref={ref} style={{ display: 'flex' }}>
@@ -7,22 +7,47 @@ import { makeStyles } from 'tss-react/mui'
7
7
  import type { MsaViewModel } from '../../model.ts'
8
8
 
9
9
  const useStyles = makeStyles()({
10
- margin: {
10
+ container: {
11
+ display: 'flex',
12
+ overflow: 'hidden',
11
13
  margin: 'auto',
12
14
  marginLeft: 10,
13
15
  },
16
+ nameFirst: {
17
+ overflow: 'hidden',
18
+ textOverflow: 'ellipsis',
19
+ whiteSpace: 'nowrap',
20
+ minWidth: 0,
21
+ },
22
+ nameRest: {
23
+ whiteSpace: 'nowrap',
24
+ flexShrink: 0,
25
+ },
14
26
  })
15
27
 
16
28
  const HeaderInfoArea = observer(function ({ model }: { model: MsaViewModel }) {
17
29
  const { mouseOverRowName, mouseCol } = model
18
30
  const { classes } = useStyles()
19
- return mouseOverRowName && mouseCol !== undefined ? (
20
- <Typography className={classes.margin}>
21
- {mouseOverRowName}:
22
- {model.visibleColToSeqPosOneBased(mouseOverRowName, mouseCol)} (
23
- {model.visibleColToRowLetter(mouseOverRowName, mouseCol)})
31
+ if (!mouseOverRowName || mouseCol === undefined) {
32
+ return null
33
+ }
34
+ const mid = Math.floor(mouseOverRowName.length / 2)
35
+ const pos = model.visibleColToSeqPosOneBased(mouseOverRowName, mouseCol)
36
+ const letter = model.visibleColToRowLetter(mouseOverRowName, mouseCol)
37
+ return (
38
+ <Typography
39
+ component="div"
40
+ className={classes.container}
41
+ title={mouseOverRowName}
42
+ >
43
+ <span className={classes.nameFirst}>
44
+ {mouseOverRowName.slice(0, mid)}
45
+ </span>
46
+ <span className={classes.nameRest}>
47
+ {mouseOverRowName.slice(mid)}:{pos} ({letter})
48
+ </span>
24
49
  </Typography>
25
- ) : null
50
+ )
26
51
  })
27
52
 
28
53
  export default HeaderInfoArea
@@ -11,6 +11,7 @@ import type { MsaViewModel } from '../../model.ts'
11
11
  const MSASettingsMenu = observer(function ({ model }: { model: MsaViewModel }) {
12
12
  return (
13
13
  <CascadingMenuButton
14
+ data-testid="msa_settings_menu"
14
15
  closeAfterItemClick={false}
15
16
  anchorOrigin={{ vertical: 'bottom', horizontal: 'left' }}
16
17
  transformOrigin={{ vertical: 'top', horizontal: 'left' }}
@@ -16,6 +16,7 @@ const TreeSettingsMenu = observer(function ({
16
16
  const { rows } = model
17
17
  return (
18
18
  <CascadingMenuButton
19
+ data-testid="tree_settings_menu"
19
20
  closeAfterItemClick={false}
20
21
  anchorOrigin={{ vertical: 'bottom', horizontal: 'left' }}
21
22
  transformOrigin={{ vertical: 'top', horizontal: 'left' }}
@@ -1,8 +1,9 @@
1
1
  import React from 'react'
2
2
 
3
3
  import ZoomIn from '@mui/icons-material/ZoomIn'
4
+ import ZoomInMap from '@mui/icons-material/ZoomInMap'
4
5
  import ZoomOut from '@mui/icons-material/ZoomOut'
5
- import { IconButton } from '@mui/material'
6
+ import { IconButton, ToggleButton } from '@mui/material'
6
7
  import { observer } from 'mobx-react'
7
8
 
8
9
  import type { MsaViewModel } from '../../model.ts'
@@ -28,6 +29,18 @@ const ZoomControls = observer(function ZoomControls({
28
29
  >
29
30
  <ZoomOut />
30
31
  </IconButton>
32
+ <ToggleButton
33
+ value="scrollZoom"
34
+ selected={model.scrollZoom}
35
+ title="Toggle scroll-to-zoom (zoom on plain mouse wheel; ctrl+wheel always zooms)"
36
+ size="small"
37
+ sx={{ border: 'none' }}
38
+ onChange={() => {
39
+ model.setScrollZoom(!model.scrollZoom)
40
+ }}
41
+ >
42
+ <ZoomInMap />
43
+ </ToggleButton>
31
44
  </>
32
45
  )
33
46
  })
@@ -37,6 +37,16 @@ const ZoomMenu = observer(function ({ model }: { model: MsaViewModel }) {
37
37
  model.resetZoom()
38
38
  },
39
39
  },
40
+ {
41
+ label: 'Scroll zoom',
42
+ type: 'checkbox',
43
+ checked: model.scrollZoom,
44
+ onClick: () => {
45
+ model.setScrollZoom(!model.scrollZoom)
46
+ },
47
+ helpText:
48
+ 'When enabled, scrolling the mouse wheel zooms in and out without holding ctrl. Hold shift to pan instead. Ctrl/⌘+wheel always zooms.',
49
+ },
40
50
  ]}
41
51
  >
42
52
  <MoreVert />