react-msaview 5.0.7 → 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 +25 -25
- 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.js +5 -24
- 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 -84
- 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 +14 -91
- package/dist/components/tree/TreeCanvas.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 +24 -77
- package/dist/model.js +117 -239
- package/dist/model.js.map +1 -1
- package/dist/neighborJoining.js +38 -629
- 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 +10 -13
- 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 +10 -26
- 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 -91
- 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 +35 -100
- 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 +179 -278
- package/src/neighborJoining.ts +38 -628
- package/src/parseAsn1.test.ts +4 -1
- 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/layout.d.ts +0 -26
- package/dist/layout.js +0 -74
- package/dist/layout.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/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,100 +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
|
-
if (mouseDragging) {
|
|
58
|
-
function globalMouseMove(event: MouseEvent) {
|
|
59
|
-
event.preventDefault()
|
|
60
|
-
const currX = event.clientX
|
|
61
|
-
const currY = event.clientY
|
|
62
|
-
const distanceX = currX - prevX.current
|
|
63
|
-
const distanceY = currY - prevY.current
|
|
64
|
-
if (distanceX || distanceY) {
|
|
65
|
-
if (!scheduled.current) {
|
|
66
|
-
scheduled.current = true
|
|
67
|
-
window.requestAnimationFrame(() => {
|
|
68
|
-
model.doScrollX(distanceX)
|
|
69
|
-
model.doScrollY(distanceY)
|
|
70
|
-
scheduled.current = false
|
|
71
|
-
prevX.current = event.clientX
|
|
72
|
-
prevY.current = event.clientY
|
|
73
|
-
})
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
function globalMouseUp() {
|
|
79
|
-
prevX.current = 0
|
|
80
|
-
setMouseDragging(false)
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
window.addEventListener('mousemove', globalMouseMove, true)
|
|
84
|
-
window.addEventListener('mouseup', globalMouseUp, true)
|
|
85
|
-
return () => {
|
|
86
|
-
window.removeEventListener('mousemove', globalMouseMove, true)
|
|
87
|
-
window.removeEventListener('mouseup', globalMouseUp, true)
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
return undefined
|
|
91
|
-
}, [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
|
+
})
|
|
92
38
|
|
|
93
39
|
return (
|
|
94
40
|
<div
|
|
95
41
|
ref={ref}
|
|
96
|
-
onMouseDown={
|
|
97
|
-
|
|
98
|
-
const target = event.target as HTMLElement
|
|
99
|
-
if (target.draggable || target.dataset.resizer) {
|
|
100
|
-
return
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
// otherwise do click and drag scroll
|
|
104
|
-
if (event.button === 0) {
|
|
105
|
-
prevX.current = event.clientX
|
|
106
|
-
prevY.current = event.clientY
|
|
107
|
-
setMouseDragging(true)
|
|
108
|
-
}
|
|
109
|
-
}}
|
|
110
|
-
onMouseUp={event => {
|
|
111
|
-
event.preventDefault()
|
|
112
|
-
setMouseDragging(false)
|
|
113
|
-
}}
|
|
42
|
+
onMouseDown={onMouseDown}
|
|
43
|
+
onMouseUp={onMouseUp}
|
|
114
44
|
onMouseLeave={event => {
|
|
115
45
|
event.preventDefault()
|
|
116
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,79 +6,25 @@ 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(0)
|
|
18
19
|
const { treeWidth, height, blocksY, treeAreaWidth } = model
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
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, { passive: false })
|
|
41
|
-
return () => {
|
|
42
|
-
curr.removeEventListener('wheel', onWheel)
|
|
43
|
-
}
|
|
44
|
-
}, [model])
|
|
45
|
-
|
|
46
|
-
useEffect(() => {
|
|
47
|
-
if (mouseDragging) {
|
|
48
|
-
function globalMouseMove(event: MouseEvent) {
|
|
49
|
-
event.preventDefault()
|
|
50
|
-
const currY = event.clientY
|
|
51
|
-
const distanceY = currY - prevY.current
|
|
52
|
-
if (distanceY) {
|
|
53
|
-
if (!scheduled.current) {
|
|
54
|
-
scheduled.current = true
|
|
55
|
-
window.requestAnimationFrame(() => {
|
|
56
|
-
model.doScrollY(distanceY)
|
|
57
|
-
scheduled.current = false
|
|
58
|
-
prevY.current = event.clientY
|
|
59
|
-
})
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
function globalMouseUp() {
|
|
65
|
-
prevY.current = 0
|
|
66
|
-
setMouseDragging(false)
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
window.addEventListener('mousemove', globalMouseMove, true)
|
|
70
|
-
window.addEventListener('mouseup', globalMouseUp, true)
|
|
71
|
-
return () => {
|
|
72
|
-
window.removeEventListener('mousemove', globalMouseMove, true)
|
|
73
|
-
window.removeEventListener('mouseup', globalMouseUp, true)
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
return undefined
|
|
77
|
-
}, [model, mouseDragging])
|
|
20
|
+
const onScrollY = useCallback(
|
|
21
|
+
(d: number) => {
|
|
22
|
+
model.doScrollY(d)
|
|
23
|
+
},
|
|
24
|
+
[model],
|
|
25
|
+
)
|
|
26
|
+
const { onMouseDown, onMouseUp } = useWheelScroll({ ref, onScrollY })
|
|
78
27
|
|
|
79
|
-
// Global tree mouseover effect. Only [model] is needed in the dependency
|
|
80
|
-
// array because autorun internally tracks all accessed observables
|
|
81
|
-
// (treeAreaWidth, height, scrollY, etc.) and re-runs when they change
|
|
82
28
|
useEffect(() => {
|
|
83
29
|
const ctx = mouseoverRef.current?.getContext('2d')
|
|
84
30
|
return ctx
|
|
@@ -97,33 +43,38 @@ const TreeCanvas = observer(function ({ model }: { model: MsaViewModel }) {
|
|
|
97
43
|
ctx.resetTransform()
|
|
98
44
|
ctx.clearRect(0, 0, w, h)
|
|
99
45
|
|
|
100
|
-
// Highlight reference row (relativeTo) persistently
|
|
101
46
|
if (relativeTo) {
|
|
102
47
|
const referenceLeaf = leaves.find(
|
|
103
48
|
leaf => leaf.data.name === relativeTo,
|
|
104
49
|
)
|
|
105
50
|
if (referenceLeaf) {
|
|
106
|
-
|
|
107
|
-
ctx.
|
|
108
|
-
|
|
51
|
+
ctx.fillStyle = referenceColor
|
|
52
|
+
ctx.fillRect(
|
|
53
|
+
0,
|
|
54
|
+
referenceLeaf.x! + sy - rowHeight / 2,
|
|
55
|
+
w,
|
|
56
|
+
rowHeight,
|
|
57
|
+
)
|
|
109
58
|
}
|
|
110
59
|
}
|
|
111
60
|
|
|
112
|
-
// Highlight multiple rows when hovering over tree nodes
|
|
113
61
|
if (hoveredTreeNode) {
|
|
114
|
-
ctx.fillStyle =
|
|
62
|
+
ctx.fillStyle = treeHoverColor
|
|
115
63
|
for (const descendantName of hoveredTreeNode.descendantNames) {
|
|
116
64
|
const matchingLeaf = leaves.find(
|
|
117
65
|
leaf => leaf.data.name === descendantName,
|
|
118
66
|
)
|
|
119
67
|
if (matchingLeaf) {
|
|
120
|
-
|
|
121
|
-
|
|
68
|
+
ctx.fillRect(
|
|
69
|
+
0,
|
|
70
|
+
matchingLeaf.x! + sy - rowHeight / 2,
|
|
71
|
+
w,
|
|
72
|
+
rowHeight,
|
|
73
|
+
)
|
|
122
74
|
}
|
|
123
75
|
}
|
|
124
76
|
}
|
|
125
77
|
|
|
126
|
-
// Highlight single tree row corresponding to MSA mouseover (if not part of multi-row hover)
|
|
127
78
|
if (
|
|
128
79
|
mouseOverRowName &&
|
|
129
80
|
mouseOverRowName !== relativeTo &&
|
|
@@ -133,9 +84,13 @@ const TreeCanvas = observer(function ({ model }: { model: MsaViewModel }) {
|
|
|
133
84
|
leaf => leaf.data.name === mouseOverRowName,
|
|
134
85
|
)
|
|
135
86
|
if (matchingLeaf) {
|
|
136
|
-
|
|
137
|
-
ctx.
|
|
138
|
-
|
|
87
|
+
ctx.fillStyle = treeHoverColor
|
|
88
|
+
ctx.fillRect(
|
|
89
|
+
0,
|
|
90
|
+
matchingLeaf.x! + sy - rowHeight / 2,
|
|
91
|
+
w,
|
|
92
|
+
rowHeight,
|
|
93
|
+
)
|
|
139
94
|
}
|
|
140
95
|
}
|
|
141
96
|
}
|
|
@@ -143,31 +98,11 @@ const TreeCanvas = observer(function ({ model }: { model: MsaViewModel }) {
|
|
|
143
98
|
: undefined
|
|
144
99
|
}, [model])
|
|
145
100
|
|
|
146
|
-
function mouseDown(event: React.MouseEvent) {
|
|
147
|
-
// check if clicking a draggable element or a resize handle
|
|
148
|
-
const target = event.target as HTMLElement
|
|
149
|
-
if (target.draggable || target.dataset.resizer) {
|
|
150
|
-
return
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
// otherwise do click and drag scroll
|
|
154
|
-
if (event.button === 0) {
|
|
155
|
-
prevY.current = event.clientY
|
|
156
|
-
setMouseDragging(true)
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
|
|
160
101
|
return (
|
|
161
102
|
<div
|
|
162
103
|
ref={ref}
|
|
163
|
-
onMouseDown={
|
|
164
|
-
onMouseUp={
|
|
165
|
-
// this local mouseup is used in addition to the global because
|
|
166
|
-
// sometimes the global add/remove are not called in time, resulting in
|
|
167
|
-
// issue #533
|
|
168
|
-
event.preventDefault()
|
|
169
|
-
setMouseDragging(false)
|
|
170
|
-
}}
|
|
104
|
+
onMouseDown={onMouseDown}
|
|
105
|
+
onMouseUp={onMouseUp}
|
|
171
106
|
onMouseLeave={event => {
|
|
172
107
|
event.preventDefault()
|
|
173
108
|
}}
|
|
@@ -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
|
}
|
package/src/flatToTree.ts
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
|
-
// Define the input item interface
|
|
2
1
|
interface FlatItem {
|
|
3
2
|
id: number
|
|
4
3
|
parent?: number
|
|
5
4
|
}
|
|
6
5
|
|
|
7
|
-
// Define the tree node interface
|
|
8
6
|
interface TreeNode {
|
|
9
7
|
id: string
|
|
10
8
|
name: string
|
|
@@ -12,46 +10,30 @@ interface TreeNode {
|
|
|
12
10
|
children: TreeNode[]
|
|
13
11
|
}
|
|
14
12
|
|
|
15
|
-
/**
|
|
16
|
-
* Parses a flat list of items into a tree structure
|
|
17
|
-
* @param items - Array of flat items with id and optional parent
|
|
18
|
-
* @returns Array of root tree nodes
|
|
19
|
-
*/
|
|
20
13
|
export function flatToTree(items: FlatItem[]): TreeNode {
|
|
21
|
-
|
|
22
|
-
|
|
14
|
+
const nodeMap = new Map(
|
|
15
|
+
items.map(item => [
|
|
16
|
+
item.id,
|
|
17
|
+
{
|
|
18
|
+
id: `${item.id}`,
|
|
19
|
+
name: `${item.id}`,
|
|
20
|
+
parent: item.parent !== undefined ? `${item.parent}` : undefined,
|
|
21
|
+
children: [] as TreeNode[],
|
|
22
|
+
},
|
|
23
|
+
]),
|
|
24
|
+
)
|
|
23
25
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
nodeMap.set(item.id, {
|
|
27
|
-
...item,
|
|
28
|
-
id: `${item.id}`,
|
|
29
|
-
name: `${item.id}`,
|
|
30
|
-
parent: item.parent !== undefined ? `${item.parent}` : undefined,
|
|
31
|
-
children: [],
|
|
32
|
-
})
|
|
33
|
-
})
|
|
34
|
-
|
|
35
|
-
// Second pass: Build parent-child relationships
|
|
36
|
-
const roots: TreeNode[] = []
|
|
37
|
-
|
|
38
|
-
items.forEach(item => {
|
|
26
|
+
let root: TreeNode | undefined
|
|
27
|
+
for (const item of items) {
|
|
39
28
|
const node = nodeMap.get(item.id)!
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
if (parentNode) {
|
|
45
|
-
parentNode.children.push(node)
|
|
46
|
-
} else {
|
|
47
|
-
// Parent doesn't exist, treat as root
|
|
48
|
-
roots.push(node)
|
|
49
|
-
}
|
|
29
|
+
const parent =
|
|
30
|
+
item.parent !== undefined ? nodeMap.get(item.parent) : undefined
|
|
31
|
+
if (parent) {
|
|
32
|
+
parent.children.push(node)
|
|
50
33
|
} else {
|
|
51
|
-
|
|
52
|
-
roots.push(node)
|
|
34
|
+
root ??= node
|
|
53
35
|
}
|
|
54
|
-
}
|
|
36
|
+
}
|
|
55
37
|
|
|
56
|
-
return
|
|
38
|
+
return root!
|
|
57
39
|
}
|