react-msaview 5.4.1 → 5.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/bundle/index.js +487 -149
- package/bundle/index.js.map +7 -1
- package/dist/colorSchemes.js +11 -7
- package/dist/colorSchemes.js.map +1 -1
- package/dist/components/ConservationTrack.js +20 -26
- package/dist/components/ConservationTrack.js.map +1 -1
- package/dist/components/MSAViewer.d.ts +13 -1
- package/dist/components/MSAViewer.js +13 -7
- package/dist/components/MSAViewer.js.map +1 -1
- package/dist/components/SequenceTextArea.js +1 -1
- package/dist/components/SequenceTextArea.js.map +1 -1
- package/dist/components/TextTrack.js +20 -27
- package/dist/components/TextTrack.js.map +1 -1
- package/dist/components/Track.js +9 -6
- package/dist/components/Track.js.map +1 -1
- package/dist/components/dialogs/FeatureDialog.js +27 -23
- package/dist/components/dialogs/FeatureDialog.js.map +1 -1
- package/dist/components/dialogs/TrackInfoDialog.js +1 -1
- package/dist/components/dialogs/TrackInfoDialog.js.map +1 -1
- package/dist/components/header/ColorSchemeMenu.js +1 -1
- package/dist/components/header/ColorSchemeMenu.js.map +1 -1
- package/dist/components/header/FileMenu.js +1 -16
- package/dist/components/header/FileMenu.js.map +1 -1
- package/dist/components/header/Header.js +6 -5
- package/dist/components/header/Header.js.map +1 -1
- package/dist/components/header/HeaderInfoArea.js +1 -1
- package/dist/components/header/HeaderInfoArea.js.map +1 -1
- package/dist/components/header/HeaderStatusArea.js +1 -1
- package/dist/components/header/HeaderStatusArea.js.map +1 -1
- package/dist/components/header/MSASettingsMenu.js +1 -1
- package/dist/components/header/MSASettingsMenu.js.map +1 -1
- package/dist/components/header/MultiAlignmentSelector.js +0 -0
- package/dist/components/header/MultiAlignmentSelector.js.map +1 -1
- package/dist/components/header/TreeSettingsMenu.js +1 -1
- package/dist/components/header/TreeSettingsMenu.js.map +1 -1
- package/dist/components/header/ZoomControls.js +14 -12
- package/dist/components/header/ZoomControls.js.map +1 -1
- package/dist/components/header/ZoomMenu.js +1 -10
- package/dist/components/header/ZoomMenu.js.map +1 -1
- package/dist/components/header/settingsMenuItems.js +9 -1
- package/dist/components/header/settingsMenuItems.js.map +1 -1
- package/dist/components/{header/HeaderMenu.d.ts → msa/ColumnStats.d.ts} +3 -3
- package/dist/components/msa/ColumnStats.js +42 -0
- package/dist/components/msa/ColumnStats.js.map +1 -0
- package/dist/components/msa/DomainLegend.d.ts +6 -0
- package/dist/components/msa/DomainLegend.js +41 -0
- package/dist/components/msa/DomainLegend.js.map +1 -0
- package/dist/components/msa/MSACanvasBlock.js +41 -29
- package/dist/components/msa/MSACanvasBlock.js.map +1 -1
- package/dist/components/msa/MSAMouseoverCanvas.js +6 -12
- package/dist/components/msa/MSAMouseoverCanvas.js.map +1 -1
- package/dist/components/msa/MSAPanel.js +3 -1
- package/dist/components/msa/MSAPanel.js.map +1 -1
- package/dist/components/msa/renderBoxFeatureCanvasBlock.js +60 -14
- package/dist/components/msa/renderBoxFeatureCanvasBlock.js.map +1 -1
- package/dist/components/msa/renderMSABlock.js +3 -3
- package/dist/components/msa/renderMSABlock.js.map +1 -1
- package/dist/components/msa/renderMSAMouseover.js +33 -6
- package/dist/components/msa/renderMSAMouseover.js.map +1 -1
- package/dist/components/overlayColors.d.ts +3 -0
- package/dist/components/overlayColors.js +11 -0
- package/dist/components/overlayColors.js.map +1 -0
- package/dist/components/renderCtx.d.ts +1 -1
- package/dist/components/tracks/renderTracksSvg.d.ts +6 -3
- package/dist/components/tracks/renderTracksSvg.js +20 -11
- package/dist/components/tracks/renderTracksSvg.js.map +1 -1
- package/dist/components/tree/TreeCanvas.js +31 -38
- package/dist/components/tree/TreeCanvas.js.map +1 -1
- package/dist/components/tree/TreeCanvasBlock.js +12 -47
- package/dist/components/tree/TreeCanvasBlock.js.map +1 -1
- package/dist/components/tree/clickMap.d.ts +24 -0
- package/dist/components/tree/clickMap.js +33 -0
- package/dist/components/tree/clickMap.js.map +1 -0
- package/dist/components/tree/renderTreeCanvas.d.ts +1 -21
- package/dist/components/tree/renderTreeCanvas.js +8 -7
- package/dist/components/tree/renderTreeCanvas.js.map +1 -1
- package/dist/constants.d.ts +4 -0
- package/dist/constants.js +17 -0
- package/dist/constants.js.map +1 -1
- package/dist/createPaletteMap.d.ts +5 -2
- package/dist/createPaletteMap.js +15 -5
- package/dist/createPaletteMap.js.map +1 -1
- package/dist/fetchUtils.d.ts +2 -1
- package/dist/fetchUtils.js +3 -2
- package/dist/fetchUtils.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/launchInterProScan.d.ts +2 -0
- package/dist/launchInterProScan.js.map +1 -1
- package/dist/model/msaModel.d.ts +25 -2
- package/dist/model/msaModel.js +15 -3
- package/dist/model/msaModel.js.map +1 -1
- package/dist/model/treeModel.d.ts +46 -5
- package/dist/model/treeModel.js +20 -7
- package/dist/model/treeModel.js.map +1 -1
- package/dist/model.d.ts +641 -133
- package/dist/model.js +191 -77
- package/dist/model.js.map +1 -1
- package/dist/propertyConservation.d.ts +7 -0
- package/dist/propertyConservation.js +66 -0
- package/dist/propertyConservation.js.map +1 -0
- package/dist/renderToSvg.js +104 -74
- package/dist/renderToSvg.js.map +1 -1
- package/dist/types.d.ts +4 -1
- package/dist/{webpack.js → umd.js} +1 -1
- package/dist/umd.js.map +1 -0
- package/dist/useCanvasAutorun.d.ts +2 -0
- package/dist/useCanvasAutorun.js +23 -0
- package/dist/useCanvasAutorun.js.map +1 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +7 -8
- package/src/colorSchemes.docs.test.ts +55 -0
- package/src/colorSchemes.ts +12 -7
- package/src/columnStats.test.ts +94 -0
- package/src/components/ConservationTrack.tsx +17 -15
- package/src/components/MSAViewer.tsx +42 -18
- package/src/components/SequenceTextArea.tsx +1 -1
- package/src/components/TextTrack.tsx +7 -11
- package/src/components/Track.tsx +9 -6
- package/src/components/dialogs/FeatureDialog.tsx +62 -48
- package/src/components/dialogs/TrackInfoDialog.tsx +1 -1
- package/src/components/header/ColorSchemeMenu.tsx +1 -0
- package/src/components/header/FileMenu.tsx +1 -15
- package/src/components/header/Header.tsx +11 -8
- package/src/components/header/HeaderInfoArea.tsx +1 -1
- package/src/components/header/HeaderStatusArea.tsx +1 -1
- package/src/components/header/MSASettingsMenu.tsx +1 -0
- package/src/components/header/MultiAlignmentSelector.tsx +0 -0
- package/src/components/header/TreeSettingsMenu.tsx +1 -0
- package/src/components/header/ZoomControls.tsx +24 -17
- package/src/components/header/ZoomMenu.tsx +1 -10
- package/src/components/header/settingsMenuItems.ts +9 -1
- package/src/components/msa/ColumnStats.tsx +54 -0
- package/src/components/msa/DomainLegend.tsx +78 -0
- package/src/components/msa/MSACanvasBlock.tsx +46 -25
- package/src/components/msa/MSAMouseoverCanvas.tsx +10 -13
- package/src/components/msa/MSAPanel.tsx +2 -0
- package/src/components/msa/renderBoxFeatureCanvasBlock.ts +75 -17
- package/src/components/msa/renderMSABlock.ts +3 -3
- package/src/components/msa/renderMSAMouseover.ts +34 -6
- package/src/components/overlayColors.ts +12 -0
- package/src/components/renderCtx.ts +1 -0
- package/src/components/tracks/renderTracksSvg.ts +25 -11
- package/src/components/tree/TreeCanvas.tsx +47 -54
- package/src/components/tree/TreeCanvasBlock.tsx +10 -68
- package/src/components/tree/clickMap.ts +55 -0
- package/src/components/tree/renderTreeCanvas.ts +10 -34
- package/src/constants.ts +19 -0
- package/src/createPaletteMap.ts +18 -5
- package/src/fetchUtils.ts +6 -3
- package/src/index.ts +1 -1
- package/src/launchInterProScan.ts +3 -1
- package/src/model/msaModel.ts +21 -3
- package/src/model/treeModel.ts +21 -7
- package/src/model.ts +221 -119
- package/src/propertyConservation.test.ts +47 -0
- package/src/propertyConservation.ts +73 -0
- package/src/renderDomainLegendSvg.test.tsx +134 -0
- package/src/renderToSvg.tsx +210 -147
- package/src/segmentOverlay.test.ts +70 -0
- package/src/stripDefaultSnapshot.test.ts +54 -0
- package/src/types.ts +7 -1
- package/src/useCanvasAutorun.ts +28 -0
- package/src/version.ts +1 -1
- package/bundle/index.js.LICENSE.txt +0 -100
- package/dist/components/dialogs/SettingsDialog.d.ts +0 -7
- package/dist/components/dialogs/SettingsDialog.js +0 -100
- package/dist/components/dialogs/SettingsDialog.js.map +0 -1
- package/dist/components/header/HeaderMenu.js +0 -27
- package/dist/components/header/HeaderMenu.js.map +0 -1
- package/dist/components/util.d.ts +0 -1
- package/dist/components/util.js +0 -7
- package/dist/components/util.js.map +0 -1
- package/dist/webpack.js.map +0 -1
- package/src/components/dialogs/SettingsDialog.tsx +0 -234
- package/src/components/header/HeaderMenu.tsx +0 -38
- package/src/components/util.ts +0 -9
- /package/dist/{webpack.d.ts → umd.d.ts} +0 -0
- /package/src/{webpack.ts → umd.ts} +0 -0
|
@@ -7,14 +7,16 @@ import type { RenderCtx } from '../renderCtx.ts'
|
|
|
7
7
|
|
|
8
8
|
export function drawConservationBars({
|
|
9
9
|
ctx,
|
|
10
|
-
|
|
10
|
+
values,
|
|
11
|
+
color,
|
|
11
12
|
colWidth,
|
|
12
13
|
trackHeight,
|
|
13
14
|
offsetX,
|
|
14
15
|
blockSize,
|
|
15
16
|
}: {
|
|
16
17
|
ctx: RenderCtx
|
|
17
|
-
|
|
18
|
+
values: number[]
|
|
19
|
+
color: string
|
|
18
20
|
colWidth: number
|
|
19
21
|
trackHeight: number
|
|
20
22
|
offsetX: number
|
|
@@ -26,15 +28,23 @@ export function drawConservationBars({
|
|
|
26
28
|
colWidth,
|
|
27
29
|
})
|
|
28
30
|
|
|
29
|
-
ctx.fillStyle =
|
|
30
|
-
for (let i = xStart; i < xEnd && i <
|
|
31
|
-
const value =
|
|
31
|
+
ctx.fillStyle = color
|
|
32
|
+
for (let i = xStart; i < xEnd && i < values.length; i++) {
|
|
33
|
+
const value = values[i]!
|
|
32
34
|
const barHeight = value * trackHeight
|
|
33
35
|
const x = i * colWidth
|
|
34
36
|
ctx.fillRect(x, trackHeight - barHeight, colWidth, barHeight)
|
|
35
37
|
}
|
|
36
38
|
}
|
|
37
39
|
|
|
40
|
+
// the bar-track values live on model getters (kept off the plain track object so
|
|
41
|
+
// the live canvas autorun stays reactive); select the right one by track id
|
|
42
|
+
export function barTrackValues(model: MsaViewModel, trackId: string) {
|
|
43
|
+
return trackId === 'property-conservation'
|
|
44
|
+
? model.propertyConservation
|
|
45
|
+
: model.conservation
|
|
46
|
+
}
|
|
47
|
+
|
|
38
48
|
export function drawTextTrackContent({
|
|
39
49
|
ctx,
|
|
40
50
|
data,
|
|
@@ -65,13 +75,13 @@ export function drawTextTrackContent({
|
|
|
65
75
|
|
|
66
76
|
for (let i = 0; str && i < str.length; i++) {
|
|
67
77
|
const letter = str[i]!
|
|
68
|
-
const
|
|
78
|
+
const upper = letter.toUpperCase()
|
|
69
79
|
if (bgColor) {
|
|
70
80
|
const x = (xStart + i) * colWidth
|
|
71
|
-
ctx.fillStyle =
|
|
81
|
+
ctx.fillStyle = colorScheme[upper] || 'white'
|
|
72
82
|
ctx.fillRect(x, 0, colWidth, rowHeight)
|
|
73
83
|
if (rowHeight >= 10 && colWidth >= rowHeight / 2) {
|
|
74
|
-
ctx.fillStyle = contrastScheme[
|
|
84
|
+
ctx.fillStyle = contrastScheme[upper] || 'black'
|
|
75
85
|
ctx.fillText(letter, x + colWidth / 2, rowHeight / 2 + 1)
|
|
76
86
|
}
|
|
77
87
|
}
|
|
@@ -81,6 +91,7 @@ export function drawTextTrackContent({
|
|
|
81
91
|
export function renderConservationTrack({
|
|
82
92
|
model,
|
|
83
93
|
ctx,
|
|
94
|
+
track,
|
|
84
95
|
offsetX,
|
|
85
96
|
offsetY,
|
|
86
97
|
trackHeight,
|
|
@@ -89,13 +100,14 @@ export function renderConservationTrack({
|
|
|
89
100
|
}: {
|
|
90
101
|
model: MsaViewModel
|
|
91
102
|
ctx: RenderCtx
|
|
103
|
+
track: BasicTrack
|
|
92
104
|
offsetX: number
|
|
93
105
|
offsetY: number
|
|
94
106
|
trackHeight: number
|
|
95
107
|
blockSizeXOverride?: number
|
|
96
108
|
highResScaleFactorOverride?: number
|
|
97
109
|
}) {
|
|
98
|
-
const { blockSize, colWidth, highResScaleFactor
|
|
110
|
+
const { blockSize, colWidth, highResScaleFactor } = model
|
|
99
111
|
const bx = blockSizeXOverride ?? blockSize
|
|
100
112
|
const k = highResScaleFactorOverride ?? highResScaleFactor
|
|
101
113
|
|
|
@@ -105,7 +117,8 @@ export function renderConservationTrack({
|
|
|
105
117
|
|
|
106
118
|
drawConservationBars({
|
|
107
119
|
ctx,
|
|
108
|
-
|
|
120
|
+
values: barTrackValues(model, track.model.id),
|
|
121
|
+
color: track.model.barColor ?? 'gray',
|
|
109
122
|
colWidth,
|
|
110
123
|
trackHeight,
|
|
111
124
|
offsetX,
|
|
@@ -191,10 +204,11 @@ export function renderAllTracks({
|
|
|
191
204
|
for (const track of turnedOnTracks) {
|
|
192
205
|
const trackHeight = track.model.height
|
|
193
206
|
|
|
194
|
-
if (track.model.
|
|
207
|
+
if (track.model.barColor !== undefined) {
|
|
195
208
|
renderConservationTrack({
|
|
196
209
|
model,
|
|
197
210
|
ctx,
|
|
211
|
+
track,
|
|
198
212
|
offsetX,
|
|
199
213
|
offsetY: currentY,
|
|
200
214
|
trackHeight,
|
|
@@ -1,21 +1,18 @@
|
|
|
1
|
-
import React, { useCallback,
|
|
1
|
+
import React, { useCallback, useRef } from 'react'
|
|
2
2
|
|
|
3
3
|
import { isAlive } from '@jbrowse/mobx-state-tree'
|
|
4
|
-
import { autorun } from 'mobx'
|
|
5
4
|
import { observer } from 'mobx-react'
|
|
6
5
|
|
|
7
6
|
import TreeCanvasBlock from './TreeCanvasBlock.tsx'
|
|
8
7
|
import { padding } from './renderTreeCanvas.ts'
|
|
8
|
+
import { useCanvasAutorun } from '../../useCanvasAutorun.ts'
|
|
9
9
|
import { useWheelScroll } from '../../useWheelScroll.ts'
|
|
10
|
+
import { referenceColor, treeHoverColor } from '../overlayColors.ts'
|
|
10
11
|
|
|
11
12
|
import type { MsaViewModel } from '../../model.ts'
|
|
12
13
|
|
|
13
|
-
const referenceColor = 'rgba(0,128,255,0.3)'
|
|
14
|
-
const treeHoverColor = 'rgba(255,165,0,0.2)'
|
|
15
|
-
|
|
16
14
|
const TreeCanvas = observer(function ({ model }: { model: MsaViewModel }) {
|
|
17
15
|
const ref = useRef<HTMLDivElement>(null)
|
|
18
|
-
const mouseoverRef = useRef<HTMLCanvasElement>(null)
|
|
19
16
|
const { treeWidth, height, blocksY, treeAreaWidth } = model
|
|
20
17
|
const onScrollY = useCallback(
|
|
21
18
|
(d: number) => {
|
|
@@ -25,58 +22,54 @@ const TreeCanvas = observer(function ({ model }: { model: MsaViewModel }) {
|
|
|
25
22
|
)
|
|
26
23
|
const { onMouseDown, onMouseUp } = useWheelScroll({ ref, onScrollY })
|
|
27
24
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
ctx.resetTransform()
|
|
44
|
-
ctx.clearRect(0, 0, w, h)
|
|
45
|
-
|
|
46
|
-
const leafByName = new Map(
|
|
47
|
-
leaves.map(leaf => [leaf.data.name, leaf]),
|
|
48
|
-
)
|
|
49
|
-
const fillRow = (name: string) => {
|
|
50
|
-
const leaf = leafByName.get(name)
|
|
51
|
-
if (leaf) {
|
|
52
|
-
ctx.fillRect(0, leaf.x! + sy - rowHeight / 2, w, rowHeight)
|
|
53
|
-
}
|
|
54
|
-
}
|
|
25
|
+
const mouseoverRef = useCanvasAutorun(
|
|
26
|
+
ctx => {
|
|
27
|
+
if (isAlive(model)) {
|
|
28
|
+
const {
|
|
29
|
+
relativeTo,
|
|
30
|
+
leaves,
|
|
31
|
+
rowHeight,
|
|
32
|
+
hoveredTreeNode,
|
|
33
|
+
treeAreaWidth: w,
|
|
34
|
+
height: h,
|
|
35
|
+
scrollY: sy,
|
|
36
|
+
mouseOverRowName,
|
|
37
|
+
} = model
|
|
38
|
+
ctx.resetTransform()
|
|
39
|
+
ctx.clearRect(0, 0, w, h)
|
|
55
40
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
41
|
+
const leafByName = new Map(leaves.map(leaf => [leaf.data.name, leaf]))
|
|
42
|
+
const fillRow = (name: string) => {
|
|
43
|
+
const leaf = leafByName.get(name)
|
|
44
|
+
if (leaf) {
|
|
45
|
+
ctx.fillRect(0, leaf.x! + sy - rowHeight / 2, w, rowHeight)
|
|
46
|
+
}
|
|
47
|
+
}
|
|
60
48
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
}
|
|
66
|
-
}
|
|
49
|
+
if (relativeTo) {
|
|
50
|
+
ctx.fillStyle = referenceColor
|
|
51
|
+
fillRow(relativeTo)
|
|
52
|
+
}
|
|
67
53
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
) {
|
|
73
|
-
ctx.fillStyle = treeHoverColor
|
|
74
|
-
fillRow(mouseOverRowName)
|
|
75
|
-
}
|
|
54
|
+
if (hoveredTreeNode) {
|
|
55
|
+
ctx.fillStyle = treeHoverColor
|
|
56
|
+
for (const descendantName of hoveredTreeNode.descendantNames) {
|
|
57
|
+
fillRow(descendantName)
|
|
76
58
|
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (
|
|
62
|
+
mouseOverRowName &&
|
|
63
|
+
mouseOverRowName !== relativeTo &&
|
|
64
|
+
!hoveredTreeNode?.descendantNames.includes(mouseOverRowName)
|
|
65
|
+
) {
|
|
66
|
+
ctx.fillStyle = treeHoverColor
|
|
67
|
+
fillRow(mouseOverRowName)
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
[model],
|
|
72
|
+
)
|
|
80
73
|
|
|
81
74
|
return (
|
|
82
75
|
<div
|
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
import React, { useEffect, useRef, useState } from 'react'
|
|
2
2
|
|
|
3
|
+
import { makeStyles } from '@jbrowse/core/util/tss-react'
|
|
3
4
|
import { useTheme } from '@mui/material'
|
|
4
|
-
import Flatbush from 'flatbush'
|
|
5
|
-
import { autorun } from 'mobx'
|
|
6
5
|
import { observer } from 'mobx-react'
|
|
7
|
-
import { makeStyles } from 'tss-react/mui'
|
|
8
6
|
|
|
9
7
|
import TreeBranchMenu from './TreeBranchMenu.tsx'
|
|
10
8
|
import TreeNodeMenu from './TreeNodeMenu.tsx'
|
|
9
|
+
import { ClickMapIndex } from './clickMap.ts'
|
|
11
10
|
import { padding, renderTreeCanvas } from './renderTreeCanvas.ts'
|
|
11
|
+
import { useCanvasAutorun } from '../../useCanvasAutorun.ts'
|
|
12
12
|
|
|
13
|
+
import type { ClickEntry } from './clickMap.ts'
|
|
13
14
|
import type { MsaViewModel } from '../../model.ts'
|
|
14
15
|
|
|
15
16
|
const useStyles = makeStyles()(theme => ({
|
|
@@ -36,55 +37,6 @@ interface TooltipData {
|
|
|
36
37
|
y: number
|
|
37
38
|
}
|
|
38
39
|
|
|
39
|
-
interface ClickEntry {
|
|
40
|
-
name: string
|
|
41
|
-
id: string
|
|
42
|
-
branch?: boolean
|
|
43
|
-
minX: number
|
|
44
|
-
maxX: number
|
|
45
|
-
minY: number
|
|
46
|
-
maxY: number
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
class ClickMapIndex {
|
|
50
|
-
private flatbush: Flatbush | null = null
|
|
51
|
-
private entries: ClickEntry[] = []
|
|
52
|
-
|
|
53
|
-
clear() {
|
|
54
|
-
this.flatbush = null
|
|
55
|
-
this.entries = []
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
insert(entry: ClickEntry) {
|
|
59
|
-
this.entries.push(entry)
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
finish() {
|
|
63
|
-
if (this.entries.length === 0) {
|
|
64
|
-
this.flatbush = null
|
|
65
|
-
} else {
|
|
66
|
-
this.flatbush = new Flatbush(this.entries.length)
|
|
67
|
-
for (const entry of this.entries) {
|
|
68
|
-
this.flatbush.add(entry.minX, entry.minY, entry.maxX, entry.maxY)
|
|
69
|
-
}
|
|
70
|
-
this.flatbush.finish()
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
search(box: {
|
|
75
|
-
minX: number
|
|
76
|
-
maxX: number
|
|
77
|
-
minY: number
|
|
78
|
-
maxY: number
|
|
79
|
-
}): ClickEntry[] {
|
|
80
|
-
return (
|
|
81
|
-
this.flatbush
|
|
82
|
-
?.search(box.minX, box.minY, box.maxX, box.maxY)
|
|
83
|
-
.map(i => this.entries[i]!) ?? []
|
|
84
|
-
)
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
40
|
const TreeCanvasBlock = observer(function ({
|
|
89
41
|
model,
|
|
90
42
|
offsetY,
|
|
@@ -94,7 +46,6 @@ const TreeCanvasBlock = observer(function ({
|
|
|
94
46
|
}) {
|
|
95
47
|
const { classes } = useStyles()
|
|
96
48
|
const theme = useTheme()
|
|
97
|
-
const ref = useRef<HTMLCanvasElement>(null)
|
|
98
49
|
const clickMap = useRef(new ClickMapIndex())
|
|
99
50
|
const mouseoverRef = useRef<HTMLCanvasElement>(null)
|
|
100
51
|
const [branchMenu, setBranchMenu] = useState<TooltipData>()
|
|
@@ -113,20 +64,10 @@ const TreeCanvasBlock = observer(function ({
|
|
|
113
64
|
const w2 = width * highResScaleFactor
|
|
114
65
|
const h2 = height * highResScaleFactor
|
|
115
66
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
if (!ctx) {
|
|
119
|
-
return
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
return autorun(() => {
|
|
67
|
+
const ref = useCanvasAutorun(
|
|
68
|
+
ctx => {
|
|
123
69
|
ctx.resetTransform()
|
|
124
|
-
ctx.clearRect(
|
|
125
|
-
0,
|
|
126
|
-
0,
|
|
127
|
-
(treeAreaWidth + padding) * highResScaleFactor,
|
|
128
|
-
blockSize * highResScaleFactor,
|
|
129
|
-
)
|
|
70
|
+
ctx.clearRect(0, 0, w2, h2)
|
|
130
71
|
renderTreeCanvas({
|
|
131
72
|
ctx,
|
|
132
73
|
model,
|
|
@@ -134,8 +75,9 @@ const TreeCanvasBlock = observer(function ({
|
|
|
134
75
|
clickMap: clickMap.current,
|
|
135
76
|
theme,
|
|
136
77
|
})
|
|
137
|
-
}
|
|
138
|
-
|
|
78
|
+
},
|
|
79
|
+
[model, blockSize, highResScaleFactor, treeAreaWidth, offsetY, theme],
|
|
80
|
+
)
|
|
139
81
|
|
|
140
82
|
useEffect(() => {
|
|
141
83
|
const ctx = mouseoverRef.current?.getContext('2d')
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import Flatbush from 'flatbush'
|
|
2
|
+
|
|
3
|
+
export interface ClickEntry {
|
|
4
|
+
name: string
|
|
5
|
+
id: string
|
|
6
|
+
branch?: boolean
|
|
7
|
+
minX: number
|
|
8
|
+
maxX: number
|
|
9
|
+
minY: number
|
|
10
|
+
maxY: number
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
interface Box {
|
|
14
|
+
minX: number
|
|
15
|
+
maxX: number
|
|
16
|
+
minY: number
|
|
17
|
+
maxY: number
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Spatial index of clickable tree elements (leaf labels, node bubbles,
|
|
21
|
+
// collapsed-clade triangles). Entries are collected during a render pass, then
|
|
22
|
+
// finish() builds the Flatbush index queried by hit-testing.
|
|
23
|
+
export class ClickMapIndex {
|
|
24
|
+
private flatbush: Flatbush | null = null
|
|
25
|
+
private entries: ClickEntry[] = []
|
|
26
|
+
|
|
27
|
+
clear() {
|
|
28
|
+
this.flatbush = null
|
|
29
|
+
this.entries = []
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
insert(entry: ClickEntry) {
|
|
33
|
+
this.entries.push(entry)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
finish() {
|
|
37
|
+
if (this.entries.length === 0) {
|
|
38
|
+
this.flatbush = null
|
|
39
|
+
} else {
|
|
40
|
+
this.flatbush = new Flatbush(this.entries.length)
|
|
41
|
+
for (const entry of this.entries) {
|
|
42
|
+
this.flatbush.add(entry.minX, entry.minY, entry.maxX, entry.maxY)
|
|
43
|
+
}
|
|
44
|
+
this.flatbush.finish()
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
search(box: Box): ClickEntry[] {
|
|
49
|
+
return (
|
|
50
|
+
this.flatbush
|
|
51
|
+
?.search(box.minX, box.minY, box.maxX, box.maxY)
|
|
52
|
+
.map(i => this.entries[i]!) ?? []
|
|
53
|
+
)
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
} from '../../hierarchy.ts'
|
|
6
6
|
import { setFontSize } from '../../setFontSize.ts'
|
|
7
7
|
|
|
8
|
+
import type { ClickMapIndex } from './clickMap.ts'
|
|
8
9
|
import type { HierarchyNode } from '../../hierarchy.ts'
|
|
9
10
|
import type { MsaViewModel } from '../../model.ts'
|
|
10
11
|
import type { RenderCtx } from '../renderCtx.ts'
|
|
@@ -16,6 +17,12 @@ const extendBounds = 5
|
|
|
16
17
|
const radius = 2.5
|
|
17
18
|
const d = radius * 2
|
|
18
19
|
|
|
20
|
+
// whether a node's row-center y falls within the block being drawn, padded by
|
|
21
|
+
// extendBounds so bubbles/labels/triangles straddling the edge still render
|
|
22
|
+
function inYBlock(y: number, offsetY: number, by: number) {
|
|
23
|
+
return y > offsetY - extendBounds && y < offsetY + by + extendBounds
|
|
24
|
+
}
|
|
25
|
+
|
|
19
26
|
// Calculate node x-coordinate for both phylogram (with branch lengths) and cladogram (topology only) modes
|
|
20
27
|
// For cladograms: x = (maxDepthToLeaf - nodeDepthToLeaf) / maxDepthToLeaf * maxWidth
|
|
21
28
|
// This positions: leaves at maxWidth (rightmost), root at 0 (leftmost), internal nodes proportionally in between
|
|
@@ -36,28 +43,6 @@ function getNodeX(
|
|
|
36
43
|
return ((maxDepthToLeaf - depthToLeaf) / maxDepthToLeaf) * maxBranchLen
|
|
37
44
|
}
|
|
38
45
|
|
|
39
|
-
interface ClickEntry {
|
|
40
|
-
name: string
|
|
41
|
-
id: string
|
|
42
|
-
branch?: boolean
|
|
43
|
-
minX: number
|
|
44
|
-
maxX: number
|
|
45
|
-
minY: number
|
|
46
|
-
maxY: number
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
interface ClickMapIndex {
|
|
50
|
-
clear(): void
|
|
51
|
-
insert(entry: ClickEntry): void
|
|
52
|
-
finish(): void
|
|
53
|
-
search(box: {
|
|
54
|
-
minX: number
|
|
55
|
-
maxX: number
|
|
56
|
-
minY: number
|
|
57
|
-
maxY: number
|
|
58
|
-
}): ClickEntry[]
|
|
59
|
-
}
|
|
60
|
-
|
|
61
46
|
export function renderTree({
|
|
62
47
|
offsetY,
|
|
63
48
|
ctx,
|
|
@@ -137,8 +122,7 @@ export function renderCollapsedTriangles({
|
|
|
137
122
|
if (collapsed.includes(id)) {
|
|
138
123
|
const apexX = getNodeX(node, showBranchLen, maxBranchLen, maxDepthToLeaf)
|
|
139
124
|
const y = node.x!
|
|
140
|
-
const inBlock =
|
|
141
|
-
y > offsetY - extendBounds && y < offsetY + by + extendBounds
|
|
125
|
+
const inBlock = inYBlock(y, offsetY, by)
|
|
142
126
|
// in cladogram mode the hidden tips align at the right edge, otherwise use
|
|
143
127
|
// the branch-length extent recorded in the model's hierarchy getter
|
|
144
128
|
const baseX = showBranchLen
|
|
@@ -214,11 +198,7 @@ export function renderNodeBubbles({
|
|
|
214
198
|
const { data } = node
|
|
215
199
|
const y = node.x!
|
|
216
200
|
const { id, name } = data
|
|
217
|
-
if (
|
|
218
|
-
node.height >= 1 &&
|
|
219
|
-
y > offsetY - extendBounds &&
|
|
220
|
-
y < offsetY + by + extendBounds
|
|
221
|
-
) {
|
|
201
|
+
if (node.height >= 1 && inYBlock(y, offsetY, by)) {
|
|
222
202
|
const isCollapsed = collapsed.includes(id)
|
|
223
203
|
ctx.strokeStyle = 'black'
|
|
224
204
|
ctx.fillStyle = isCollapsed ? 'black' : 'white'
|
|
@@ -296,11 +276,7 @@ export function renderTreeLabels({
|
|
|
296
276
|
// a collapsed clade is drawn as a triangle + tip count; suppress its leaf
|
|
297
277
|
// label when the "name" is just the auto-generated internal-node id
|
|
298
278
|
const isAnonymousCollapsed = collapsed.includes(id) && name === id
|
|
299
|
-
if (
|
|
300
|
-
!isAnonymousCollapsed &&
|
|
301
|
-
y > offsetY - extendBounds &&
|
|
302
|
-
y < offsetY + by + extendBounds
|
|
303
|
-
) {
|
|
279
|
+
if (!isAnonymousCollapsed && inYBlock(y, offsetY, by)) {
|
|
304
280
|
// note: +rowHeight/4 matches with -rowHeight/4 in msa
|
|
305
281
|
const yp = y + fontSize / 4
|
|
306
282
|
let xp = 0
|
package/src/constants.ts
CHANGED
|
@@ -9,6 +9,24 @@ export const defaultShowDomains = false
|
|
|
9
9
|
export const defaultHideGaps = true
|
|
10
10
|
export const defaultAllowedGappyness = 100
|
|
11
11
|
export const defaultSubFeatureRows = false
|
|
12
|
+
// row height for each stacked feature when subFeatureRows is on
|
|
13
|
+
export const subFeatureRowHeight = 4
|
|
14
|
+
|
|
15
|
+
// Feature types that are ordinal *segments* of a single transcript (exons and
|
|
16
|
+
// the like) rather than categorical domains. Their identity is their position,
|
|
17
|
+
// so the overlay alternates two shades to mark boundaries and labels them by
|
|
18
|
+
// number, instead of assigning every one a distinct hue + legend row.
|
|
19
|
+
export const segmentFeatureTypes = new Set([
|
|
20
|
+
'exon',
|
|
21
|
+
'CDS',
|
|
22
|
+
'five_prime_UTR',
|
|
23
|
+
'three_prime_UTR',
|
|
24
|
+
'UTR',
|
|
25
|
+
'intron',
|
|
26
|
+
])
|
|
27
|
+
|
|
28
|
+
// the two shades alternated across adjacent segments (exons)
|
|
29
|
+
export const segmentShades = ['#9fb6d4', '#d4dcea']
|
|
12
30
|
export const defaultDrawMsaLetters = true
|
|
13
31
|
export const defaultScrollZoom = false
|
|
14
32
|
|
|
@@ -22,6 +40,7 @@ export const maxCellSize = 80
|
|
|
22
40
|
// MSA model defaults
|
|
23
41
|
export const defaultBgColor = true
|
|
24
42
|
export const defaultColorSchemeName = 'maeditor'
|
|
43
|
+
export const defaultShowColumnStats = true
|
|
25
44
|
|
|
26
45
|
// Tree model defaults
|
|
27
46
|
export const defaultDrawLabels = true
|
package/src/createPaletteMap.ts
CHANGED
|
@@ -1,11 +1,24 @@
|
|
|
1
|
+
import { colord } from 'colord'
|
|
2
|
+
|
|
1
3
|
import palettes from './ggplotPalettes.ts'
|
|
2
4
|
|
|
3
5
|
/**
|
|
4
|
-
* Creates a map from keys to colors
|
|
5
|
-
*
|
|
6
|
+
* Creates a map from keys to colors. Up to the largest ggplot palette (8 keys)
|
|
7
|
+
* we use the hand-picked ggplot colors; beyond that — e.g. a gene-structure
|
|
8
|
+
* overlay with one feature per exon — we generate evenly spaced HSL hues so
|
|
9
|
+
* every key still gets a distinct, stable color instead of running off the end
|
|
10
|
+
* of the palette.
|
|
6
11
|
*/
|
|
7
12
|
export function createPaletteMap(keys: string[]) {
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
|
|
13
|
+
const n = keys.length
|
|
14
|
+
if (n <= palettes.length) {
|
|
15
|
+
const palette = palettes[n - 1]!
|
|
16
|
+
return Object.fromEntries(keys.map((key, i) => [key, palette[i]!]))
|
|
17
|
+
}
|
|
18
|
+
return Object.fromEntries(
|
|
19
|
+
keys.map((key, i) => [
|
|
20
|
+
key,
|
|
21
|
+
colord({ h: (i * 360) / n, s: 60, l: 60 }).toHex(),
|
|
22
|
+
]),
|
|
23
|
+
)
|
|
11
24
|
}
|
package/src/fetchUtils.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import { fetchAndMaybeUnzipText } from '@jbrowse/core/util'
|
|
1
|
+
import { fetchAndMaybeUnzipText, statusMessageText } from '@jbrowse/core/util'
|
|
2
|
+
|
|
3
|
+
import type { RpcStatus } from '@jbrowse/core/util'
|
|
2
4
|
|
|
3
5
|
export interface FetchStatus {
|
|
4
6
|
msg: string
|
|
@@ -10,7 +12,7 @@ type Filehandle = Parameters<typeof fetchAndMaybeUnzipText>[0]
|
|
|
10
12
|
|
|
11
13
|
type ProgressFetcher = (
|
|
12
14
|
loc: Filehandle,
|
|
13
|
-
opts: { signal: AbortSignal; statusCallback: (
|
|
15
|
+
opts: { signal: AbortSignal; statusCallback: (status: RpcStatus) => void },
|
|
14
16
|
) => Promise<string>
|
|
15
17
|
|
|
16
18
|
/**
|
|
@@ -28,7 +30,8 @@ export async function fetchTextWithProgress(
|
|
|
28
30
|
try {
|
|
29
31
|
return await fetcher(loc, {
|
|
30
32
|
signal: controller.signal,
|
|
31
|
-
statusCallback:
|
|
33
|
+
statusCallback: status => {
|
|
34
|
+
const msg = statusMessageText(status)
|
|
32
35
|
setStatus(
|
|
33
36
|
msg
|
|
34
37
|
? {
|
package/src/index.ts
CHANGED
|
@@ -9,7 +9,7 @@ export { renderToSvg } from './renderToSvg.tsx'
|
|
|
9
9
|
export { default as MSAView } from './components/Loading.tsx'
|
|
10
10
|
export { default as MSAViewer } from './components/MSAViewer.tsx'
|
|
11
11
|
export { type MsaViewModel, default as MSAModelF } from './model.ts'
|
|
12
|
-
export type { MSAParserType } from 'msa-parsers'
|
|
12
|
+
export type { MSAFormat, MSAParserType } from 'msa-parsers'
|
|
13
13
|
export type { HierarchyNode } from './hierarchy.ts'
|
|
14
14
|
export type { InterProScanResults } from './launchInterProScan.ts'
|
|
15
15
|
export type {
|
|
@@ -13,9 +13,11 @@ export interface InterProScanResults {
|
|
|
13
13
|
name: string
|
|
14
14
|
description: string
|
|
15
15
|
accession: string
|
|
16
|
+
// original GFF feature type (exon, CDS, ...) when sourced from GFF
|
|
17
|
+
featureType?: string
|
|
16
18
|
}
|
|
17
19
|
}
|
|
18
|
-
locations: { start: number; end: number }[]
|
|
20
|
+
locations: { start: number; end: number; strand?: number }[]
|
|
19
21
|
}[]
|
|
20
22
|
xref: { id: string }[]
|
|
21
23
|
}
|
package/src/model/msaModel.ts
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { types } from '@jbrowse/mobx-state-tree'
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
defaultBgColor,
|
|
5
|
+
defaultColorSchemeName,
|
|
6
|
+
defaultShowColumnStats,
|
|
7
|
+
} from '../constants.ts'
|
|
4
8
|
|
|
5
9
|
import type { MSAFormat } from 'msa-parsers'
|
|
6
10
|
|
|
@@ -16,13 +20,20 @@ export function MSAModelF() {
|
|
|
16
20
|
* #property
|
|
17
21
|
* draw MSA tiles with a background color
|
|
18
22
|
*/
|
|
19
|
-
bgColor: defaultBgColor,
|
|
23
|
+
bgColor: types.stripDefault(types.boolean, defaultBgColor),
|
|
20
24
|
|
|
21
25
|
/**
|
|
22
26
|
* #property
|
|
23
27
|
* default color scheme name
|
|
24
28
|
*/
|
|
25
|
-
colorSchemeName: defaultColorSchemeName,
|
|
29
|
+
colorSchemeName: types.stripDefault(types.string, defaultColorSchemeName),
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* #property
|
|
33
|
+
* show a per-column statistics tooltip (consensus, conservation, gaps,
|
|
34
|
+
* residue distribution) while hovering the alignment
|
|
35
|
+
*/
|
|
36
|
+
showColumnStats: types.stripDefault(types.boolean, defaultShowColumnStats),
|
|
26
37
|
|
|
27
38
|
/**
|
|
28
39
|
* #property
|
|
@@ -49,6 +60,13 @@ export function MSAModelF() {
|
|
|
49
60
|
self.bgColor = arg
|
|
50
61
|
},
|
|
51
62
|
|
|
63
|
+
/**
|
|
64
|
+
* #action
|
|
65
|
+
*/
|
|
66
|
+
setShowColumnStats(arg: boolean) {
|
|
67
|
+
self.showColumnStats = arg
|
|
68
|
+
},
|
|
69
|
+
|
|
52
70
|
/**
|
|
53
71
|
* #action
|
|
54
72
|
* force a specific MSA parser, or pass undefined to auto-detect
|