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.
- package/bundle/index.js +106 -106
- package/bundle/index.js.LICENSE.txt +1 -1
- package/bundle/index.js.map +1 -1
- package/dist/components/Checkbox2.js +3 -6
- package/dist/components/Checkbox2.js.map +1 -1
- package/dist/components/MSAViewer.d.ts +14 -0
- package/dist/components/MSAViewer.js +34 -0
- package/dist/components/MSAViewer.js.map +1 -0
- package/dist/components/Track.d.ts +0 -4
- package/dist/components/Track.js +6 -26
- package/dist/components/Track.js.map +1 -1
- package/dist/components/dialogs/DomainDialog.js +2 -5
- package/dist/components/dialogs/DomainDialog.js.map +1 -1
- package/dist/components/dialogs/InterProScanDialog.js +7 -7
- package/dist/components/dialogs/InterProScanDialog.js.map +1 -1
- package/dist/components/dialogs/SettingsDialog.js +3 -19
- package/dist/components/dialogs/SettingsDialog.js.map +1 -1
- package/dist/components/header/ColorSchemeMenu.d.ts +6 -0
- package/dist/components/header/ColorSchemeMenu.js +19 -0
- package/dist/components/header/ColorSchemeMenu.js.map +1 -0
- package/dist/components/header/{ZoomStar.d.ts → FileMenu.d.ts} +2 -2
- package/dist/components/header/FileMenu.js +71 -0
- package/dist/components/header/FileMenu.js.map +1 -0
- package/dist/components/header/Header.js +8 -6
- package/dist/components/header/Header.js.map +1 -1
- package/dist/components/header/HeaderMenu.js +3 -145
- package/dist/components/header/HeaderMenu.js.map +1 -1
- package/dist/components/header/MSASettingsMenu.d.ts +6 -0
- package/dist/components/header/MSASettingsMenu.js +36 -0
- package/dist/components/header/MSASettingsMenu.js.map +1 -0
- package/dist/components/header/SettingsMenu.js +1 -21
- package/dist/components/header/SettingsMenu.js.map +1 -1
- package/dist/components/header/TreeSettingsMenu.d.ts +6 -0
- package/dist/components/header/TreeSettingsMenu.js +74 -0
- package/dist/components/header/TreeSettingsMenu.js.map +1 -0
- package/dist/components/header/ZoomMenu.js +0 -8
- package/dist/components/header/ZoomMenu.js.map +1 -1
- package/dist/components/header/getDomainsMenu.d.ts +31 -0
- package/dist/components/header/getDomainsMenu.js +75 -0
- package/dist/components/header/getDomainsMenu.js.map +1 -0
- package/dist/components/import/ImportFormExamples.js +21 -19
- package/dist/components/import/ImportFormExamples.js.map +1 -1
- package/dist/components/msa/MSACanvas.js +13 -89
- package/dist/components/msa/MSACanvas.js.map +1 -1
- package/dist/components/msa/MSACanvasBlock.js +1 -3
- package/dist/components/msa/MSACanvasBlock.js.map +1 -1
- package/dist/components/msa/renderMSABlock.js +2 -4
- package/dist/components/msa/renderMSABlock.js.map +1 -1
- package/dist/components/msa/renderMSAMouseover.js +1 -7
- package/dist/components/msa/renderMSAMouseover.js.map +1 -1
- package/dist/components/tree/TreeCanvas.js +18 -101
- package/dist/components/tree/TreeCanvas.js.map +1 -1
- package/dist/components/tree/TreeCanvasBlock.js +33 -1
- package/dist/components/tree/TreeCanvasBlock.js.map +1 -1
- package/dist/components/tree/TreeNodeMenu.js +5 -16
- package/dist/components/tree/TreeNodeMenu.js.map +1 -1
- package/dist/components/tree/renderTreeCanvas.js +4 -12
- package/dist/components/tree/renderTreeCanvas.js.map +1 -1
- package/dist/constants.d.ts +0 -2
- package/dist/constants.js +0 -2
- package/dist/constants.js.map +1 -1
- package/dist/fetchUtils.d.ts +0 -1
- package/dist/fetchUtils.js +0 -4
- package/dist/fetchUtils.js.map +1 -1
- package/dist/flatToTree.d.ts +0 -5
- package/dist/flatToTree.js +13 -30
- package/dist/flatToTree.js.map +1 -1
- package/dist/hierarchy.d.ts +28 -0
- package/dist/hierarchy.js +164 -0
- package/dist/hierarchy.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/launchInterProScan.d.ts +0 -5
- package/dist/launchInterProScan.js +5 -3
- package/dist/launchInterProScan.js.map +1 -1
- package/dist/model/DataModel.d.ts +9 -0
- package/dist/model/DataModel.js +12 -1
- package/dist/model/DataModel.js.map +1 -1
- package/dist/model/msaModel.d.ts +3 -0
- package/dist/model/msaModel.js +0 -1
- package/dist/model/msaModel.js.map +1 -1
- package/dist/model/treeModel.d.ts +3 -6
- package/dist/model/treeModel.js +3 -15
- package/dist/model/treeModel.js.map +1 -1
- package/dist/model.d.ts +34 -77
- package/dist/model.js +140 -239
- package/dist/model.js.map +1 -1
- package/dist/neighborJoining.js +40 -633
- package/dist/neighborJoining.js.map +1 -1
- package/dist/parseAsn1.d.ts +0 -12
- package/dist/parseAsn1.js +125 -332
- package/dist/parseAsn1.js.map +1 -1
- package/dist/useWheelScroll.d.ts +8 -0
- package/dist/useWheelScroll.js +93 -0
- package/dist/useWheelScroll.js.map +1 -0
- package/dist/util.d.ts +1 -6
- package/dist/util.js +5 -34
- package/dist/util.js.map +1 -1
- package/dist/vendor/copyToClipboard.d.ts +1 -10
- package/dist/vendor/copyToClipboard.js +14 -109
- package/dist/vendor/copyToClipboard.js.map +1 -1
- package/dist/vendor/fileSaver.d.ts +1 -11
- package/dist/vendor/fileSaver.js +7 -76
- package/dist/vendor/fileSaver.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/version.js.map +1 -1
- package/package.json +14 -14
- package/src/collapseLogic.test.ts +115 -0
- package/src/components/Checkbox2.tsx +9 -18
- package/src/components/MSAViewer.tsx +67 -0
- package/src/components/Track.tsx +11 -30
- package/src/components/dialogs/DomainDialog.tsx +4 -5
- package/src/components/dialogs/InterProScanDialog.tsx +7 -7
- package/src/components/dialogs/SettingsDialog.tsx +0 -37
- package/src/components/header/ColorSchemeMenu.tsx +35 -0
- package/src/components/header/FileMenu.tsx +84 -0
- package/src/components/header/Header.tsx +8 -6
- package/src/components/header/HeaderMenu.tsx +4 -155
- package/src/components/header/MSASettingsMenu.tsx +48 -0
- package/src/components/header/SettingsMenu.tsx +0 -23
- package/src/components/header/TreeSettingsMenu.tsx +96 -0
- package/src/components/header/ZoomMenu.tsx +0 -8
- package/src/components/header/getDomainsMenu.ts +83 -0
- package/src/components/import/ImportFormExamples.tsx +37 -34
- package/src/components/msa/MSACanvas.tsx +21 -97
- package/src/components/msa/MSACanvasBlock.tsx +1 -3
- package/src/components/msa/renderBoxFeatureCanvasBlock.ts +1 -1
- package/src/components/msa/renderMSABlock.ts +2 -5
- package/src/components/msa/renderMSAMouseover.ts +0 -6
- package/src/components/tree/TreeCanvas.tsx +48 -111
- package/src/components/tree/TreeCanvasBlock.tsx +44 -0
- package/src/components/tree/TreeNodeMenu.tsx +5 -14
- package/src/components/tree/renderTreeCanvas.ts +8 -21
- package/src/constants.ts +0 -2
- package/src/fetchUtils.ts +0 -5
- package/src/flatToTree.ts +20 -38
- package/src/hierarchy.test.ts +120 -0
- package/src/hierarchy.ts +220 -0
- package/src/index.ts +2 -0
- package/src/launchInterProScan.ts +4 -3
- package/src/model/DataModel.ts +12 -1
- package/src/model/msaModel.ts +0 -2
- package/src/model/treeModel.ts +2 -18
- package/src/model.ts +203 -278
- package/src/neighborJoining.test.ts +15 -7
- package/src/neighborJoining.ts +40 -632
- package/src/parseAsn1.test.ts +5 -2
- package/src/parseAsn1.ts +135 -405
- package/src/useWheelScroll.ts +109 -0
- package/src/util.ts +5 -50
- package/src/vendor/copyToClipboard.ts +14 -122
- package/src/vendor/fileSaver.ts +8 -105
- package/src/version.ts +1 -1
- package/dist/components/dialogs/AddTrackDialog.d.ts +0 -8
- package/dist/components/dialogs/AddTrackDialog.js +0 -30
- package/dist/components/dialogs/AddTrackDialog.js.map +0 -1
- package/dist/components/dialogs/TabPanel.d.ts +0 -6
- package/dist/components/dialogs/TabPanel.js +0 -6
- package/dist/components/dialogs/TabPanel.js.map +0 -1
- package/dist/components/header/ZoomStar.js +0 -40
- package/dist/components/header/ZoomStar.js.map +0 -1
- package/dist/createPaletteMap.test.d.ts +0 -1
- package/dist/createPaletteMap.test.js +0 -49
- package/dist/createPaletteMap.test.js.map +0 -1
- package/dist/layout.d.ts +0 -26
- package/dist/layout.js +0 -74
- package/dist/layout.js.map +0 -1
- package/dist/neighborJoining.test.d.ts +0 -1
- package/dist/neighborJoining.test.js +0 -110
- package/dist/neighborJoining.test.js.map +0 -1
- package/dist/parseAsn1.test.d.ts +0 -1
- package/dist/parseAsn1.test.js +0 -8
- package/dist/parseAsn1.test.js.map +0 -1
- package/dist/reparseTree.d.ts +0 -2
- package/dist/reparseTree.js +0 -15
- package/dist/reparseTree.js.map +0 -1
- package/dist/rowCoordinateCalculations.test.d.ts +0 -1
- package/dist/rowCoordinateCalculations.test.js +0 -224
- package/dist/rowCoordinateCalculations.test.js.map +0 -1
- package/dist/seqPosToGlobalCol.test.d.ts +0 -1
- package/dist/seqPosToGlobalCol.test.js +0 -60
- package/dist/seqPosToGlobalCol.test.js.map +0 -1
- package/src/components/dialogs/AddTrackDialog.tsx +0 -85
- package/src/components/dialogs/TabPanel.tsx +0 -19
- package/src/components/header/ZoomStar.tsx +0 -74
- package/src/createPaletteMap.test.ts +0 -57
- package/src/layout.ts +0 -118
- package/src/reparseTree.ts +0 -18
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import 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
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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={
|
|
103
|
-
|
|
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
|
-
|
|
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 =
|
|
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
|
|
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
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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,
|
|
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
|
-
|
|
103
|
-
ctx.
|
|
104
|
-
|
|
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 =
|
|
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
|
-
|
|
117
|
-
|
|
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
|
-
|
|
135
|
-
ctx.
|
|
136
|
-
|
|
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
|
|
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={
|
|
162
|
-
onMouseUp={
|
|
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
|
|
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
|
-
|
|
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
|
-
{
|
|
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
|
|
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 ?
|
|
56
|
-
const sx = showBranchLen ?
|
|
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
|
|
98
|
-
const x = showBranchLen ?
|
|
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 =
|
|
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
|
}
|