react-msaview 5.0.6 → 5.0.13

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/bundle/index.js +106 -106
  2. package/bundle/index.js.LICENSE.txt +1 -1
  3. package/bundle/index.js.map +1 -1
  4. package/dist/components/Checkbox2.js +3 -6
  5. package/dist/components/Checkbox2.js.map +1 -1
  6. package/dist/components/MSAViewer.d.ts +14 -0
  7. package/dist/components/MSAViewer.js +34 -0
  8. package/dist/components/MSAViewer.js.map +1 -0
  9. package/dist/components/Track.d.ts +0 -4
  10. package/dist/components/Track.js +6 -26
  11. package/dist/components/Track.js.map +1 -1
  12. package/dist/components/dialogs/DomainDialog.js +2 -5
  13. package/dist/components/dialogs/DomainDialog.js.map +1 -1
  14. package/dist/components/dialogs/InterProScanDialog.js +7 -7
  15. package/dist/components/dialogs/InterProScanDialog.js.map +1 -1
  16. package/dist/components/dialogs/SettingsDialog.js +3 -19
  17. package/dist/components/dialogs/SettingsDialog.js.map +1 -1
  18. package/dist/components/header/ColorSchemeMenu.d.ts +6 -0
  19. package/dist/components/header/ColorSchemeMenu.js +19 -0
  20. package/dist/components/header/ColorSchemeMenu.js.map +1 -0
  21. package/dist/components/header/{ZoomStar.d.ts → FileMenu.d.ts} +2 -2
  22. package/dist/components/header/FileMenu.js +71 -0
  23. package/dist/components/header/FileMenu.js.map +1 -0
  24. package/dist/components/header/Header.js +8 -6
  25. package/dist/components/header/Header.js.map +1 -1
  26. package/dist/components/header/HeaderMenu.js +3 -145
  27. package/dist/components/header/HeaderMenu.js.map +1 -1
  28. package/dist/components/header/MSASettingsMenu.d.ts +6 -0
  29. package/dist/components/header/MSASettingsMenu.js +36 -0
  30. package/dist/components/header/MSASettingsMenu.js.map +1 -0
  31. package/dist/components/header/SettingsMenu.js +1 -21
  32. package/dist/components/header/SettingsMenu.js.map +1 -1
  33. package/dist/components/header/TreeSettingsMenu.d.ts +6 -0
  34. package/dist/components/header/TreeSettingsMenu.js +74 -0
  35. package/dist/components/header/TreeSettingsMenu.js.map +1 -0
  36. package/dist/components/header/ZoomMenu.js +0 -8
  37. package/dist/components/header/ZoomMenu.js.map +1 -1
  38. package/dist/components/header/getDomainsMenu.d.ts +31 -0
  39. package/dist/components/header/getDomainsMenu.js +75 -0
  40. package/dist/components/header/getDomainsMenu.js.map +1 -0
  41. package/dist/components/import/ImportFormExamples.js +21 -19
  42. package/dist/components/import/ImportFormExamples.js.map +1 -1
  43. package/dist/components/msa/MSACanvas.js +13 -89
  44. package/dist/components/msa/MSACanvas.js.map +1 -1
  45. package/dist/components/msa/MSACanvasBlock.js +1 -3
  46. package/dist/components/msa/MSACanvasBlock.js.map +1 -1
  47. package/dist/components/msa/renderMSABlock.js +2 -4
  48. package/dist/components/msa/renderMSABlock.js.map +1 -1
  49. package/dist/components/msa/renderMSAMouseover.js +1 -7
  50. package/dist/components/msa/renderMSAMouseover.js.map +1 -1
  51. package/dist/components/tree/TreeCanvas.js +18 -101
  52. package/dist/components/tree/TreeCanvas.js.map +1 -1
  53. package/dist/components/tree/TreeCanvasBlock.js +33 -1
  54. package/dist/components/tree/TreeCanvasBlock.js.map +1 -1
  55. package/dist/components/tree/TreeNodeMenu.js +5 -16
  56. package/dist/components/tree/TreeNodeMenu.js.map +1 -1
  57. package/dist/components/tree/renderTreeCanvas.js +4 -12
  58. package/dist/components/tree/renderTreeCanvas.js.map +1 -1
  59. package/dist/constants.d.ts +0 -2
  60. package/dist/constants.js +0 -2
  61. package/dist/constants.js.map +1 -1
  62. package/dist/fetchUtils.d.ts +0 -1
  63. package/dist/fetchUtils.js +0 -4
  64. package/dist/fetchUtils.js.map +1 -1
  65. package/dist/flatToTree.d.ts +0 -5
  66. package/dist/flatToTree.js +13 -30
  67. package/dist/flatToTree.js.map +1 -1
  68. package/dist/hierarchy.d.ts +28 -0
  69. package/dist/hierarchy.js +164 -0
  70. package/dist/hierarchy.js.map +1 -0
  71. package/dist/index.d.ts +2 -0
  72. package/dist/index.js +1 -0
  73. package/dist/index.js.map +1 -1
  74. package/dist/launchInterProScan.d.ts +0 -5
  75. package/dist/launchInterProScan.js +5 -3
  76. package/dist/launchInterProScan.js.map +1 -1
  77. package/dist/model/DataModel.d.ts +9 -0
  78. package/dist/model/DataModel.js +12 -1
  79. package/dist/model/DataModel.js.map +1 -1
  80. package/dist/model/msaModel.d.ts +3 -0
  81. package/dist/model/msaModel.js +0 -1
  82. package/dist/model/msaModel.js.map +1 -1
  83. package/dist/model/treeModel.d.ts +3 -6
  84. package/dist/model/treeModel.js +3 -15
  85. package/dist/model/treeModel.js.map +1 -1
  86. package/dist/model.d.ts +34 -77
  87. package/dist/model.js +140 -239
  88. package/dist/model.js.map +1 -1
  89. package/dist/neighborJoining.js +40 -633
  90. package/dist/neighborJoining.js.map +1 -1
  91. package/dist/parseAsn1.d.ts +0 -12
  92. package/dist/parseAsn1.js +125 -332
  93. package/dist/parseAsn1.js.map +1 -1
  94. package/dist/useWheelScroll.d.ts +8 -0
  95. package/dist/useWheelScroll.js +93 -0
  96. package/dist/useWheelScroll.js.map +1 -0
  97. package/dist/util.d.ts +1 -6
  98. package/dist/util.js +5 -34
  99. package/dist/util.js.map +1 -1
  100. package/dist/vendor/copyToClipboard.d.ts +1 -10
  101. package/dist/vendor/copyToClipboard.js +14 -109
  102. package/dist/vendor/copyToClipboard.js.map +1 -1
  103. package/dist/vendor/fileSaver.d.ts +1 -11
  104. package/dist/vendor/fileSaver.js +7 -76
  105. package/dist/vendor/fileSaver.js.map +1 -1
  106. package/dist/version.d.ts +1 -1
  107. package/dist/version.js +1 -1
  108. package/dist/version.js.map +1 -1
  109. package/package.json +14 -14
  110. package/src/collapseLogic.test.ts +115 -0
  111. package/src/components/Checkbox2.tsx +9 -18
  112. package/src/components/MSAViewer.tsx +67 -0
  113. package/src/components/Track.tsx +11 -30
  114. package/src/components/dialogs/DomainDialog.tsx +4 -5
  115. package/src/components/dialogs/InterProScanDialog.tsx +7 -7
  116. package/src/components/dialogs/SettingsDialog.tsx +0 -37
  117. package/src/components/header/ColorSchemeMenu.tsx +35 -0
  118. package/src/components/header/FileMenu.tsx +84 -0
  119. package/src/components/header/Header.tsx +8 -6
  120. package/src/components/header/HeaderMenu.tsx +4 -155
  121. package/src/components/header/MSASettingsMenu.tsx +48 -0
  122. package/src/components/header/SettingsMenu.tsx +0 -23
  123. package/src/components/header/TreeSettingsMenu.tsx +96 -0
  124. package/src/components/header/ZoomMenu.tsx +0 -8
  125. package/src/components/header/getDomainsMenu.ts +83 -0
  126. package/src/components/import/ImportFormExamples.tsx +37 -34
  127. package/src/components/msa/MSACanvas.tsx +21 -97
  128. package/src/components/msa/MSACanvasBlock.tsx +1 -3
  129. package/src/components/msa/renderBoxFeatureCanvasBlock.ts +1 -1
  130. package/src/components/msa/renderMSABlock.ts +2 -5
  131. package/src/components/msa/renderMSAMouseover.ts +0 -6
  132. package/src/components/tree/TreeCanvas.tsx +48 -111
  133. package/src/components/tree/TreeCanvasBlock.tsx +44 -0
  134. package/src/components/tree/TreeNodeMenu.tsx +5 -14
  135. package/src/components/tree/renderTreeCanvas.ts +8 -21
  136. package/src/constants.ts +0 -2
  137. package/src/fetchUtils.ts +0 -5
  138. package/src/flatToTree.ts +20 -38
  139. package/src/hierarchy.test.ts +120 -0
  140. package/src/hierarchy.ts +220 -0
  141. package/src/index.ts +2 -0
  142. package/src/launchInterProScan.ts +4 -3
  143. package/src/model/DataModel.ts +12 -1
  144. package/src/model/msaModel.ts +0 -2
  145. package/src/model/treeModel.ts +2 -18
  146. package/src/model.ts +203 -278
  147. package/src/neighborJoining.test.ts +15 -7
  148. package/src/neighborJoining.ts +40 -632
  149. package/src/parseAsn1.test.ts +5 -2
  150. package/src/parseAsn1.ts +135 -405
  151. package/src/useWheelScroll.ts +109 -0
  152. package/src/util.ts +5 -50
  153. package/src/vendor/copyToClipboard.ts +14 -122
  154. package/src/vendor/fileSaver.ts +8 -105
  155. package/src/version.ts +1 -1
  156. package/dist/components/dialogs/AddTrackDialog.d.ts +0 -8
  157. package/dist/components/dialogs/AddTrackDialog.js +0 -30
  158. package/dist/components/dialogs/AddTrackDialog.js.map +0 -1
  159. package/dist/components/dialogs/TabPanel.d.ts +0 -6
  160. package/dist/components/dialogs/TabPanel.js +0 -6
  161. package/dist/components/dialogs/TabPanel.js.map +0 -1
  162. package/dist/components/header/ZoomStar.js +0 -40
  163. package/dist/components/header/ZoomStar.js.map +0 -1
  164. package/dist/createPaletteMap.test.d.ts +0 -1
  165. package/dist/createPaletteMap.test.js +0 -49
  166. package/dist/createPaletteMap.test.js.map +0 -1
  167. package/dist/layout.d.ts +0 -26
  168. package/dist/layout.js +0 -74
  169. package/dist/layout.js.map +0 -1
  170. package/dist/neighborJoining.test.d.ts +0 -1
  171. package/dist/neighborJoining.test.js +0 -110
  172. package/dist/neighborJoining.test.js.map +0 -1
  173. package/dist/parseAsn1.test.d.ts +0 -1
  174. package/dist/parseAsn1.test.js +0 -8
  175. package/dist/parseAsn1.test.js.map +0 -1
  176. package/dist/reparseTree.d.ts +0 -2
  177. package/dist/reparseTree.js +0 -15
  178. package/dist/reparseTree.js.map +0 -1
  179. package/dist/rowCoordinateCalculations.test.d.ts +0 -1
  180. package/dist/rowCoordinateCalculations.test.js +0 -224
  181. package/dist/rowCoordinateCalculations.test.js.map +0 -1
  182. package/dist/seqPosToGlobalCol.test.d.ts +0 -1
  183. package/dist/seqPosToGlobalCol.test.js +0 -60
  184. package/dist/seqPosToGlobalCol.test.js.map +0 -1
  185. package/src/components/dialogs/AddTrackDialog.tsx +0 -85
  186. package/src/components/dialogs/TabPanel.tsx +0 -19
  187. package/src/components/header/ZoomStar.tsx +0 -74
  188. package/src/createPaletteMap.test.ts +0 -57
  189. package/src/layout.ts +0 -118
  190. package/src/reparseTree.ts +0 -18
@@ -1,9 +1,10 @@
1
- import React, { useEffect, useRef, useState } from 'react'
1
+ import React, { useCallback, useRef } from 'react'
2
2
 
3
3
  import { observer } from 'mobx-react'
4
4
 
5
5
  import Loading from './Loading.tsx'
6
6
  import MSACanvasBlock from './MSACanvasBlock.tsx'
7
+ import { useWheelScroll } from '../../useWheelScroll.ts'
7
8
 
8
9
  import type { MsaViewModel } from '../../model.ts'
9
10
 
@@ -17,106 +18,29 @@ const MSACanvas = observer(function ({ model }: { model: MsaViewModel }) {
17
18
  blocks2d,
18
19
  } = model
19
20
  const ref = useRef<HTMLDivElement>(null)
20
- // wheel
21
- const scheduled = useRef(false)
22
- const deltaX = useRef(0)
23
- const deltaY = useRef(0)
24
- // mouse click-and-drag scrolling
25
- const prevX = useRef<number>(0)
26
- const prevY = useRef<number>(0)
27
- const [mouseDragging, setMouseDragging] = useState(false)
28
- useEffect(() => {
29
- const curr = ref.current
30
- if (!curr) {
31
- return
32
- }
33
- function onWheel(event: WheelEvent) {
34
- deltaX.current += event.deltaX
35
- deltaY.current += event.deltaY
36
-
37
- if (!scheduled.current) {
38
- scheduled.current = true
39
- requestAnimationFrame(() => {
40
- model.doScrollX(-deltaX.current)
41
- model.doScrollY(-deltaY.current)
42
- deltaX.current = 0
43
- deltaY.current = 0
44
- scheduled.current = false
45
- })
46
- }
47
- event.preventDefault()
48
- event.stopPropagation()
49
- }
50
- curr.addEventListener('wheel', onWheel, { passive: false })
51
- return () => {
52
- curr.removeEventListener('wheel', onWheel)
53
- }
54
- }, [model])
55
-
56
- useEffect(() => {
57
- let cleanup = () => {}
58
-
59
- function globalMouseMove(event: MouseEvent) {
60
- event.preventDefault()
61
- const currX = event.clientX
62
- const currY = event.clientY
63
- const distanceX = currX - prevX.current
64
- const distanceY = currY - prevY.current
65
- if (distanceX || distanceY) {
66
- // use rAF to make it so multiple event handlers aren't fired per-frame
67
- // see https://calendar.perfplanet.com/2013/the-runtime-performance-checklist/
68
- if (!scheduled.current) {
69
- scheduled.current = true
70
- window.requestAnimationFrame(() => {
71
- model.doScrollX(distanceX)
72
- model.doScrollY(distanceY)
73
- scheduled.current = false
74
- prevX.current = event.clientX
75
- prevY.current = event.clientY
76
- })
77
- }
78
- }
79
- }
80
-
81
- function globalMouseUp() {
82
- prevX.current = 0
83
- if (mouseDragging) {
84
- setMouseDragging(false)
85
- }
86
- }
87
-
88
- if (mouseDragging) {
89
- window.addEventListener('mousemove', globalMouseMove, true)
90
- window.addEventListener('mouseup', globalMouseUp, true)
91
- cleanup = () => {
92
- window.removeEventListener('mousemove', globalMouseMove, true)
93
- window.removeEventListener('mouseup', globalMouseUp, true)
94
- }
95
- }
96
- return cleanup
97
- }, [model, mouseDragging])
21
+ const onScrollX = useCallback(
22
+ (d: number) => {
23
+ model.doScrollX(d)
24
+ },
25
+ [model],
26
+ )
27
+ const onScrollY = useCallback(
28
+ (d: number) => {
29
+ model.doScrollY(d)
30
+ },
31
+ [model],
32
+ )
33
+ const { onMouseDown, onMouseUp } = useWheelScroll({
34
+ ref,
35
+ onScrollX,
36
+ onScrollY,
37
+ })
98
38
 
99
39
  return (
100
40
  <div
101
41
  ref={ref}
102
- onMouseDown={event => {
103
- // check if clicking a draggable element or a resize handle
104
- const target = event.target as HTMLElement
105
- if (target.draggable || target.dataset.resizer) {
106
- return
107
- }
108
-
109
- // otherwise do click and drag scroll
110
- if (event.button === 0) {
111
- prevX.current = event.clientX
112
- prevY.current = event.clientY
113
- setMouseDragging(true)
114
- }
115
- }}
116
- onMouseUp={event => {
117
- event.preventDefault()
118
- setMouseDragging(false)
119
- }}
42
+ onMouseDown={onMouseDown}
43
+ onMouseUp={onMouseUp}
120
44
  onMouseLeave={event => {
121
45
  event.preventDefault()
122
46
  }}
@@ -52,8 +52,7 @@ const MSACanvasBlock = observer(function ({
52
52
  blockSize * highResScaleFactor,
53
53
  blockSize * highResScaleFactor,
54
54
  )
55
- const { actuallyShowDomains } = model
56
- if (actuallyShowDomains) {
55
+ if (model.actuallyShowDomains) {
57
56
  renderBoxFeatureCanvasBlock({
58
57
  ctx,
59
58
  offsetX,
@@ -102,7 +101,6 @@ const MSACanvasBlock = observer(function ({
102
101
  if (x >= 0 && x < model.numColumns && y >= 0 && y < model.numRows) {
103
102
  model.setMousePos(x, y)
104
103
  } else {
105
- // Clear mouse position when outside bounds
106
104
  model.setMousePos(undefined, undefined)
107
105
  }
108
106
  }}
@@ -1,6 +1,6 @@
1
+ import type { HierarchyNode } from '../../hierarchy.ts'
1
2
  import type { MsaViewModel } from '../../model.ts'
2
3
  import type { NodeWithIdsAndLength } from '../../types.ts'
3
- import type { HierarchyNode } from 'd3-hierarchy'
4
4
 
5
5
  export function renderBoxFeatureCanvasBlock({
6
6
  model,
@@ -1,7 +1,7 @@
1
+ import type { HierarchyNode } from '../../hierarchy.ts'
1
2
  import type { MsaViewModel } from '../../model.ts'
2
3
  import type { NodeWithIdsAndLength } from '../../types.ts'
3
4
  import type { Theme } from '@mui/material'
4
- import type { HierarchyNode } from 'd3-hierarchy'
5
5
 
6
6
  export function renderMSABlock({
7
7
  model,
@@ -180,7 +180,6 @@ function drawText({
180
180
  colorScheme,
181
181
  columns,
182
182
  colWidth,
183
- contrastLettering,
184
183
  rowHeight,
185
184
  relativeTo,
186
185
  } = model
@@ -214,9 +213,7 @@ function drawText({
214
213
  const displayLetter = isMatchingReference ? '.' : letter
215
214
 
216
215
  const color = colorScheme[letter.toUpperCase()]
217
- const contrast = contrastLettering
218
- ? contrastScheme[letter.toUpperCase()] || 'black'
219
- : 'black'
216
+ const contrast = contrastScheme[letter.toUpperCase()] || 'black'
220
217
 
221
218
  // note: -rowHeight/4 matches +rowHeight/4 in tree
222
219
  ctx.fillStyle = actuallyShowDomains
@@ -22,8 +22,6 @@ export function renderMouseover({
22
22
  scrollX,
23
23
  scrollY,
24
24
  mouseRow,
25
- // @ts-expect-error
26
- mouseCol2,
27
25
  mouseClickRow,
28
26
  mouseClickCol,
29
27
  relativeTo,
@@ -79,8 +77,4 @@ export function renderMouseover({
79
77
  ctx.fillStyle = highlightColor
80
78
  ctx.fillRect(0, mouseClickRow * rowHeight + scrollY, width, rowHeight)
81
79
  }
82
- if (mouseCol2 !== undefined) {
83
- ctx.fillStyle = highlightColor
84
- ctx.fillRect(mouseCol2 * colWidth + scrollX, 0, colWidth, height)
85
- }
86
80
  }
@@ -1,4 +1,4 @@
1
- import React, { useEffect, useRef, useState } from 'react'
1
+ import React, { useCallback, useEffect, useRef } from 'react'
2
2
 
3
3
  import { isAlive } from '@jbrowse/mobx-state-tree'
4
4
  import { autorun } from 'mobx'
@@ -6,166 +6,103 @@ import { observer } from 'mobx-react'
6
6
 
7
7
  import TreeCanvasBlock from './TreeCanvasBlock.tsx'
8
8
  import { padding } from './renderTreeCanvas.ts'
9
+ import { useWheelScroll } from '../../useWheelScroll.ts'
9
10
 
10
11
  import type { MsaViewModel } from '../../model.ts'
11
12
 
13
+ const referenceColor = 'rgba(0,128,255,0.3)'
14
+ const treeHoverColor = 'rgba(255,165,0,0.2)'
15
+
12
16
  const TreeCanvas = observer(function ({ model }: { model: MsaViewModel }) {
13
17
  const ref = useRef<HTMLDivElement>(null)
14
18
  const mouseoverRef = useRef<HTMLCanvasElement>(null)
15
- const scheduled = useRef(false)
16
- const deltaY = useRef(0)
17
- const prevY = useRef<number>(0)
18
- const { treeWidth, height, blocksY, treeAreaWidth, scrollY } = model
19
- const [mouseDragging, setMouseDragging] = useState(false)
20
-
21
- useEffect(() => {
22
- const curr = ref.current
23
- if (!curr) {
24
- return
25
- }
26
- function onWheel(event: WheelEvent) {
27
- deltaY.current += event.deltaY
28
-
29
- if (!scheduled.current) {
30
- scheduled.current = true
31
- requestAnimationFrame(() => {
32
- model.doScrollY(-deltaY.current)
33
- deltaY.current = 0
34
- scheduled.current = false
35
- })
36
- }
37
- event.preventDefault()
38
- event.stopPropagation()
39
- }
40
- curr.addEventListener('wheel', onWheel)
41
- return () => {
42
- curr.removeEventListener('wheel', onWheel)
43
- }
44
- }, [model])
45
-
46
- useEffect(() => {
47
- let cleanup = () => {}
48
-
49
- function globalMouseMove(event: MouseEvent) {
50
- event.preventDefault()
51
- const currY = event.clientY
52
- const distanceY = currY - prevY.current
53
- if (distanceY) {
54
- // use rAF to make it so multiple event handlers aren't fired per-frame
55
- // see
56
- // https://calendar.perfplanet.com/2013/the-runtime-performance-checklist/
57
- if (!scheduled.current) {
58
- scheduled.current = true
59
- window.requestAnimationFrame(() => {
60
- model.doScrollY(distanceY)
61
- scheduled.current = false
62
- prevY.current = event.clientY
63
- })
64
- }
65
- }
66
- }
67
-
68
- function globalMouseUp() {
69
- prevY.current = 0
70
- if (mouseDragging) {
71
- setMouseDragging(false)
72
- }
73
- }
74
-
75
- if (mouseDragging) {
76
- window.addEventListener('mousemove', globalMouseMove, true)
77
- window.addEventListener('mouseup', globalMouseUp, true)
78
- cleanup = () => {
79
- window.removeEventListener('mousemove', globalMouseMove, true)
80
- window.removeEventListener('mouseup', globalMouseUp, true)
81
- }
82
- }
83
- return cleanup
84
- }, [model, mouseDragging])
19
+ const { treeWidth, height, blocksY, treeAreaWidth } = model
20
+ const onScrollY = useCallback(
21
+ (d: number) => {
22
+ model.doScrollY(d)
23
+ },
24
+ [model],
25
+ )
26
+ const { onMouseDown, onMouseUp } = useWheelScroll({ ref, onScrollY })
85
27
 
86
- // Global tree mouseover effect
87
28
  useEffect(() => {
88
29
  const ctx = mouseoverRef.current?.getContext('2d')
89
30
  return ctx
90
31
  ? autorun(() => {
91
32
  if (isAlive(model)) {
33
+ const {
34
+ relativeTo,
35
+ leaves,
36
+ rowHeight,
37
+ hoveredTreeNode,
38
+ treeAreaWidth: w,
39
+ height: h,
40
+ scrollY: sy,
41
+ mouseOverRowName,
42
+ } = model
92
43
  ctx.resetTransform()
93
- ctx.clearRect(0, 0, treeAreaWidth, height)
44
+ ctx.clearRect(0, 0, w, h)
94
45
 
95
- // Highlight reference row (relativeTo) persistently
96
- const { relativeTo, leaves, rowHeight, hoveredTreeNode } = model
97
46
  if (relativeTo) {
98
47
  const referenceLeaf = leaves.find(
99
48
  leaf => leaf.data.name === relativeTo,
100
49
  )
101
50
  if (referenceLeaf) {
102
- const y = referenceLeaf.x! + scrollY
103
- ctx.fillStyle = 'rgba(0,128,255,0.3)' // Blue highlight for reference row
104
- ctx.fillRect(0, y - rowHeight / 2, treeAreaWidth, rowHeight)
51
+ ctx.fillStyle = referenceColor
52
+ ctx.fillRect(
53
+ 0,
54
+ referenceLeaf.x! + sy - rowHeight / 2,
55
+ w,
56
+ rowHeight,
57
+ )
105
58
  }
106
59
  }
107
60
 
108
- // Highlight multiple rows when hovering over tree nodes
109
61
  if (hoveredTreeNode) {
110
- ctx.fillStyle = 'rgba(255,165,0,0.2)' // Orange highlight for tree hover
62
+ ctx.fillStyle = treeHoverColor
111
63
  for (const descendantName of hoveredTreeNode.descendantNames) {
112
64
  const matchingLeaf = leaves.find(
113
65
  leaf => leaf.data.name === descendantName,
114
66
  )
115
67
  if (matchingLeaf) {
116
- const y = matchingLeaf.x! + scrollY
117
- ctx.fillRect(0, y - rowHeight / 2, treeAreaWidth, rowHeight)
68
+ ctx.fillRect(
69
+ 0,
70
+ matchingLeaf.x! + sy - rowHeight / 2,
71
+ w,
72
+ rowHeight,
73
+ )
118
74
  }
119
75
  }
120
76
  }
121
77
 
122
- // Highlight single tree row corresponding to MSA mouseover (if not part of multi-row hover)
123
- const { mouseOverRowName } = model
124
78
  if (
125
79
  mouseOverRowName &&
126
80
  mouseOverRowName !== relativeTo &&
127
81
  !hoveredTreeNode?.descendantNames.includes(mouseOverRowName)
128
82
  ) {
129
- // Find the leaf node that matches the hovered row
130
83
  const matchingLeaf = leaves.find(
131
84
  leaf => leaf.data.name === mouseOverRowName,
132
85
  )
133
86
  if (matchingLeaf) {
134
- const y = matchingLeaf.x! + scrollY
135
- ctx.fillStyle = 'rgba(255,165,0,0.2)' // Orange highlight for MSA sync
136
- ctx.fillRect(0, y - rowHeight / 2, treeAreaWidth, rowHeight)
87
+ ctx.fillStyle = treeHoverColor
88
+ ctx.fillRect(
89
+ 0,
90
+ matchingLeaf.x! + sy - rowHeight / 2,
91
+ w,
92
+ rowHeight,
93
+ )
137
94
  }
138
95
  }
139
96
  }
140
97
  })
141
98
  : undefined
142
- }, [model, treeAreaWidth, height, scrollY])
143
-
144
- function mouseDown(event: React.MouseEvent) {
145
- // check if clicking a draggable element or a resize handle
146
- const target = event.target as HTMLElement
147
- if (target.draggable || target.dataset.resizer) {
148
- return
149
- }
150
-
151
- // otherwise do click and drag scroll
152
- if (event.button === 0) {
153
- prevY.current = event.clientY
154
- setMouseDragging(true)
155
- }
156
- }
99
+ }, [model])
157
100
 
158
101
  return (
159
102
  <div
160
103
  ref={ref}
161
- onMouseDown={mouseDown}
162
- onMouseUp={event => {
163
- // this local mouseup is used in addition to the global because
164
- // sometimes the global add/remove are not called in time, resulting in
165
- // issue #533
166
- event.preventDefault()
167
- setMouseDragging(false)
168
- }}
104
+ onMouseDown={onMouseDown}
105
+ onMouseUp={onMouseUp}
169
106
  onMouseLeave={event => {
170
107
  event.preventDefault()
171
108
  }}
@@ -4,6 +4,7 @@ import { useTheme } from '@mui/material'
4
4
  import Flatbush from 'flatbush'
5
5
  import { autorun } from 'mobx'
6
6
  import { observer } from 'mobx-react'
7
+ import { makeStyles } from 'tss-react/mui'
7
8
 
8
9
  import TreeBranchMenu from './TreeBranchMenu.tsx'
9
10
  import TreeNodeMenu from './TreeNodeMenu.tsx'
@@ -11,6 +12,23 @@ import { padding, renderTreeCanvas } from './renderTreeCanvas.ts'
11
12
 
12
13
  import type { MsaViewModel } from '../../model.ts'
13
14
 
15
+ const useStyles = makeStyles()(theme => ({
16
+ tooltip: {
17
+ position: 'fixed',
18
+ pointerEvents: 'none',
19
+ zIndex: 10000,
20
+ backgroundColor: theme.palette.grey[700],
21
+ color: theme.palette.common.white,
22
+ padding: '4px 8px',
23
+ borderRadius: 4,
24
+ fontSize: 12,
25
+ whiteSpace: 'nowrap',
26
+ maxWidth: 300,
27
+ overflow: 'hidden',
28
+ textOverflow: 'ellipsis',
29
+ },
30
+ }))
31
+
14
32
  interface TooltipData {
15
33
  name: string
16
34
  id: string
@@ -75,6 +93,7 @@ const TreeCanvasBlock = observer(function ({
75
93
  model: MsaViewModel
76
94
  offsetY: number
77
95
  }) {
96
+ const { classes } = useStyles()
78
97
  const theme = useTheme()
79
98
  const ref = useRef<HTMLCanvasElement>(null)
80
99
  const clickMap = useRef(new ClickMapIndex())
@@ -82,6 +101,11 @@ const TreeCanvasBlock = observer(function ({
82
101
  const [branchMenu, setBranchMenu] = useState<TooltipData>()
83
102
  const [toggleNodeMenu, setToggleNodeMenu] = useState<TooltipData>()
84
103
  const [hoverElt, setHoverElt] = useState<ClickEntry>()
104
+ const [tooltipInfo, setTooltipInfo] = useState<{
105
+ name: string
106
+ x: number
107
+ y: number
108
+ }>()
85
109
 
86
110
  const { scrollY, treeAreaWidth, blockSize, highResScaleFactor } = model
87
111
 
@@ -217,6 +241,17 @@ const TreeCanvasBlock = observer(function ({
217
241
  ref.current.style.cursor = hoveredAny ? 'pointer' : 'default'
218
242
  setHoverElt(hoveredLeaf) // Only show direct hover highlight for leaf nodes
219
243
 
244
+ // Set tooltip info
245
+ if (hoveredAny) {
246
+ setTooltipInfo({
247
+ name: hoveredAny.name,
248
+ x: event.clientX,
249
+ y: event.clientY,
250
+ })
251
+ } else {
252
+ setTooltipInfo(undefined)
253
+ }
254
+
220
255
  // Handle tree node hover for multi-row highlighting
221
256
  if (hoveredAny) {
222
257
  model.setHoveredTreeNode(hoveredAny.id)
@@ -249,6 +284,7 @@ const TreeCanvasBlock = observer(function ({
249
284
  }}
250
285
  onMouseLeave={() => {
251
286
  setHoverElt(undefined)
287
+ setTooltipInfo(undefined)
252
288
  // Clear all highlighting when leaving tree area
253
289
  model.setHoveredTreeNode(undefined)
254
290
  model.setMousePos(undefined, undefined)
@@ -265,6 +301,14 @@ const TreeCanvasBlock = observer(function ({
265
301
  height={height}
266
302
  ref={mouseoverRef}
267
303
  />
304
+ {tooltipInfo ? (
305
+ <div
306
+ className={classes.tooltip}
307
+ style={{ left: tooltipInfo.x + 12, top: tooltipInfo.y + 12 }}
308
+ >
309
+ {tooltipInfo.name}
310
+ </div>
311
+ ) : null}
268
312
  </>
269
313
  )
270
314
  })
@@ -24,8 +24,9 @@ const TreeMenu = observer(function ({
24
24
  model: MsaViewModel
25
25
  onClose: () => void
26
26
  }) {
27
- const { collapsed, collapsedLeaves } = model
28
- const { name } = node
27
+ const { collapsed } = model
28
+ const { name, id } = node
29
+ const isCollapsed = collapsed.includes(id)
29
30
  return (
30
31
  <Menu
31
32
  anchorReference="anchorPosition"
@@ -62,21 +63,11 @@ const TreeMenu = observer(function ({
62
63
  <MenuItem
63
64
  dense
64
65
  onClick={() => {
65
- if (collapsed.includes(node.id)) {
66
- model.toggleCollapsed(node.id)
67
- } else {
68
- if (node.id.endsWith('-leafnode')) {
69
- model.toggleCollapsedLeaf(node.id)
70
- } else {
71
- model.toggleCollapsedLeaf(`${node.id}-leafnode`)
72
- }
73
- }
66
+ model.toggleCollapsed(id)
74
67
  onClose()
75
68
  }}
76
69
  >
77
- {collapsed.includes(node.id) || collapsedLeaves.includes(node.id)
78
- ? 'Show node'
79
- : 'Hide node'}
70
+ {isCollapsed ? 'Show node' : 'Hide node'}
80
71
  </MenuItem>
81
72
  <MenuItem
82
73
  dense
@@ -1,3 +1,5 @@
1
+ import { descendants, links } from '../../hierarchy.ts'
2
+
1
3
  import type { MsaViewModel } from '../../model.ts'
2
4
  import type { Theme } from '@mui/material'
3
5
 
@@ -45,15 +47,15 @@ export function renderTree({
45
47
  const { hierarchy, showBranchLenEffective: showBranchLen, blockSize } = model
46
48
  const by = blockSizeYOverride || blockSize
47
49
  ctx.strokeStyle = theme.palette.text.primary
48
- for (const link of hierarchy.links()) {
50
+ for (const link of links(hierarchy)) {
49
51
  const { source, target } = link
50
52
  if (target.height === 0 && !showBranchLen) {
51
53
  continue
52
54
  }
53
55
  const sy = source.x!
54
56
  const ty = target.x!
55
- const tx = showBranchLen ? (target as { len?: number }).len : target.y
56
- const sx = showBranchLen ? (source as { len?: number }).len : source.y
57
+ const tx = showBranchLen ? target.len : target.y
58
+ const sx = showBranchLen ? source.len : source.y
57
59
  if (tx === undefined || sx === undefined) {
58
60
  continue
59
61
  }
@@ -94,8 +96,8 @@ export function renderNodeBubbles({
94
96
  marginLeft: ml,
95
97
  } = model
96
98
  const by = blockSizeYOverride || blockSize
97
- for (const node of hierarchy.descendants()) {
98
- const x = showBranchLen ? (node as { len?: number }).len : node.y
99
+ for (const node of descendants(hierarchy)) {
100
+ const x = showBranchLen ? node.len : node.y
99
101
  if (x === undefined) {
100
102
  continue
101
103
  }
@@ -146,14 +148,10 @@ export function renderTreeLabels({
146
148
  fontSize,
147
149
  showBranchLenEffective: showBranchLen,
148
150
  treeMetadata,
149
- hierarchy,
150
- collapsed,
151
- collapsedLeaves,
152
151
  blockSize,
153
152
  labelsAlignRight,
154
153
  drawTree,
155
154
  treeAreaWidth,
156
- treeWidth,
157
155
  treeAreaWidthMinusMargin,
158
156
  marginLeft,
159
157
  leaves,
@@ -171,7 +169,7 @@ export function renderTreeLabels({
171
169
  const {
172
170
  data: { name, id },
173
171
  } = node
174
- const len = (node as { len?: number }).len
172
+ const len = node.len
175
173
  const y = node.x!
176
174
  const x = node.y!
177
175
 
@@ -182,17 +180,6 @@ export function renderTreeLabels({
182
180
  let xp = 0
183
181
  if (!noTree) {
184
182
  xp = (showBranchLen ? len : x) || 0
185
- if (
186
- !showBranchLen &&
187
- !collapsed.includes(id) &&
188
- !collapsedLeaves.includes(id)
189
- ) {
190
- // this subtraction is a hack to compensate for the leafnode rendering
191
- // glitch (issue #71). the context is that an extra leaf node is added
192
- // so that 'collapsing/hiding leaf nodes is possible' but this causes
193
- // weird workarounds
194
- xp -= treeWidth / hierarchy.height
195
- }
196
183
  }
197
184
 
198
185
  const { width } = ctx.measureText(displayName)
package/src/constants.ts CHANGED
@@ -8,7 +8,6 @@ export const defaultCurrentAlignment = 0
8
8
  export const defaultShowDomains = false
9
9
  export const defaultHideGaps = true
10
10
  export const defaultAllowedGappyness = 100
11
- export const defaultContrastLettering = true
12
11
  export const defaultSubFeatureRows = false
13
12
  export const defaultDrawMsaLetters = true
14
13
 
@@ -21,7 +20,6 @@ export const defaultDrawLabels = true
21
20
  export const defaultLabelsAlignRight = false
22
21
  export const defaultTreeAreaWidth = 400
23
22
  export const defaultTreeWidth = 300
24
- export const defaultTreeWidthMatchesArea = true
25
23
  export const defaultShowBranchLen = true
26
24
  export const defaultDrawTree = true
27
25
  export const defaultDrawNodeBubbles = true
package/src/fetchUtils.ts CHANGED
@@ -20,11 +20,6 @@ export async function jsonfetch<T>(url: string, args?: RequestInit) {
20
20
  return response.json() as T
21
21
  }
22
22
 
23
- export async function arraybufferfetch(url: string) {
24
- const res = await myfetch(url)
25
- return res.arrayBuffer()
26
- }
27
-
28
23
  export function timeout(time: number) {
29
24
  return new Promise(res => setTimeout(res, time))
30
25
  }