react-msaview 6.0.0 → 6.1.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/bundle/index.js +98 -98
- package/bundle/index.js.map +4 -4
- package/dist/columnCounts.d.ts +7 -0
- package/dist/columnCounts.js +18 -0
- package/dist/columnCounts.js.map +1 -1
- package/dist/components/DragHandle.d.ts +25 -0
- package/dist/components/DragHandle.js +49 -0
- package/dist/components/DragHandle.js.map +1 -0
- package/dist/components/ResizeHandles.d.ts +4 -2
- package/dist/components/ResizeHandles.js +29 -32
- package/dist/components/ResizeHandles.js.map +1 -1
- package/dist/components/VerticalScrollbar.js +2 -2
- package/dist/components/VerticalScrollbar.js.map +1 -1
- package/dist/components/dialogs/InterProScanDialog.js +52 -129
- package/dist/components/dialogs/InterProScanDialog.js.map +1 -1
- package/dist/components/header/settingsMenuItems.js +15 -0
- package/dist/components/header/settingsMenuItems.js.map +1 -1
- package/dist/components/minimap/Minimap.js +32 -4
- package/dist/components/minimap/Minimap.js.map +1 -1
- package/dist/components/msa/MSACanvas.js +8 -2
- package/dist/components/msa/MSACanvas.js.map +1 -1
- package/dist/components/msa/MSACanvasBlock.js +12 -3
- package/dist/components/msa/MSACanvasBlock.js.map +1 -1
- package/dist/components/msa/msaRaster.d.ts +63 -0
- package/dist/components/msa/msaRaster.js +252 -0
- package/dist/components/msa/msaRaster.js.map +1 -0
- package/dist/components/msa/renderMSABlock.d.ts +2 -1
- package/dist/components/msa/renderMSABlock.js +13 -21
- package/dist/components/msa/renderMSABlock.js.map +1 -1
- package/dist/components/msa/tileColor.d.ts +6 -0
- package/dist/components/msa/tileColor.js +35 -0
- package/dist/components/msa/tileColor.js.map +1 -0
- package/dist/components/renderCtx.d.ts +1 -1
- package/dist/components/tracks/TrackBlocks.d.ts +14 -0
- package/dist/components/tracks/TrackBlocks.js +65 -0
- package/dist/components/tracks/TrackBlocks.js.map +1 -0
- package/dist/components/tracks/{renderTracksSvg.d.ts → drawTracks.d.ts} +22 -10
- package/dist/components/tracks/drawTracks.js +193 -0
- package/dist/components/tracks/drawTracks.js.map +1 -0
- package/dist/components/tree/TreeCanvas.js +8 -2
- package/dist/components/tree/TreeCanvas.js.map +1 -1
- package/dist/components/tree/TreeCanvasBlock.js +7 -4
- package/dist/components/tree/TreeCanvasBlock.js.map +1 -1
- package/dist/components/tree/renderTreeCanvas.js +5 -2
- package/dist/components/tree/renderTreeCanvas.js.map +1 -1
- package/dist/constants.d.ts +3 -0
- package/dist/constants.js +8 -0
- package/dist/constants.js.map +1 -1
- package/dist/model/DataModel.d.ts +1 -4
- package/dist/model/DataModel.js +8 -10
- package/dist/model/DataModel.js.map +1 -1
- package/dist/model.d.ts +36 -7
- package/dist/model.js +113 -58
- package/dist/model.js.map +1 -1
- package/dist/neighborJoining.js +34 -18
- package/dist/neighborJoining.js.map +1 -1
- package/dist/renderTestEnv.d.ts +43 -0
- package/dist/renderTestEnv.js +88 -0
- package/dist/renderTestEnv.js.map +1 -0
- package/dist/renderToSvg.js +1 -1
- package/dist/renderToSvg.js.map +1 -1
- package/dist/sequenceLogo.d.ts +28 -0
- package/dist/sequenceLogo.js +47 -0
- package/dist/sequenceLogo.js.map +1 -0
- package/dist/types.d.ts +2 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +3 -3
- package/src/columnCounts.test.ts +15 -0
- package/src/columnCounts.ts +19 -0
- package/src/components/DragHandle.tsx +81 -0
- package/src/components/ResizeHandles.tsx +46 -54
- package/src/components/VerticalScrollbar.tsx +3 -2
- package/src/components/dialogs/InterProScanDialog.tsx +54 -131
- package/src/components/header/settingsMenuItems.ts +18 -0
- package/src/components/minimap/Minimap.tsx +47 -4
- package/src/components/msa/MSACanvas.tsx +30 -10
- package/src/components/msa/MSACanvasBlock.tsx +13 -3
- package/src/components/msa/msaRaster.test.ts +145 -0
- package/src/components/msa/msaRaster.ts +350 -0
- package/src/components/msa/renderMSABlock.ts +15 -20
- package/src/components/msa/tileColor.ts +37 -0
- package/src/components/renderCtx.ts +2 -0
- package/src/components/tracks/TrackBlocks.tsx +111 -0
- package/src/components/tracks/drawTracks.ts +323 -0
- package/src/components/tree/TreeCanvas.tsx +16 -4
- package/src/components/tree/TreeCanvasBlock.tsx +18 -11
- package/src/components/tree/renderTreeCanvas.ts +5 -1
- package/src/constants.ts +10 -0
- package/src/impgRender.test.tsx +2 -57
- package/src/largeTree.test.ts +16 -2
- package/src/model/DataModel.ts +11 -11
- package/src/model.ts +128 -60
- package/src/neighborJoining.ts +36 -18
- package/src/renderDomainsSvg.test.tsx +2 -57
- package/src/renderMinimapSvg.test.tsx +2 -56
- package/src/renderSequenceLogoSvg.test.tsx +176 -0
- package/src/renderTestEnv.ts +96 -0
- package/src/renderToSvg.tsx +1 -1
- package/src/sequenceLogo.test.ts +88 -0
- package/src/sequenceLogo.ts +63 -0
- package/src/types.ts +7 -1
- package/src/version.ts +1 -1
- package/dist/components/ConservationTrack.d.ts +0 -8
- package/dist/components/ConservationTrack.js +0 -49
- package/dist/components/ConservationTrack.js.map +0 -1
- package/dist/components/ScrollbarThumb.d.ts +0 -14
- package/dist/components/ScrollbarThumb.js +0 -28
- package/dist/components/ScrollbarThumb.js.map +0 -1
- package/dist/components/TextTrack.d.ts +0 -8
- package/dist/components/TextTrack.js +0 -58
- package/dist/components/TextTrack.js.map +0 -1
- package/dist/components/tracks/renderTracksSvg.js +0 -126
- package/dist/components/tracks/renderTracksSvg.js.map +0 -1
- package/src/components/ConservationTrack.tsx +0 -99
- package/src/components/ScrollbarThumb.tsx +0 -48
- package/src/components/TextTrack.tsx +0 -103
- package/src/components/tracks/renderTracksSvg.ts +0 -247
|
@@ -1,53 +1,11 @@
|
|
|
1
|
-
import React, { useCallback
|
|
1
|
+
import React, { useCallback } from 'react'
|
|
2
2
|
|
|
3
3
|
import { observer } from 'mobx-react'
|
|
4
4
|
|
|
5
|
-
import
|
|
5
|
+
import DragHandle from './DragHandle.tsx'
|
|
6
6
|
|
|
7
7
|
import type { MsaViewModel } from '../model.ts'
|
|
8
|
-
|
|
9
|
-
const handleBg = 'rgba(200,200,200)'
|
|
10
|
-
const handleBgHover = 'rgba(150,150,150)'
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* A draggable divider. `getStart` is read at mousedown and handed back to
|
|
14
|
-
* `onDrag` alongside the pixel delta since, so each handle below only has to say
|
|
15
|
-
* which model dimension it resizes and where it sits.
|
|
16
|
-
*/
|
|
17
|
-
function ResizeHandle({
|
|
18
|
-
axis,
|
|
19
|
-
getStart,
|
|
20
|
-
onDrag,
|
|
21
|
-
style,
|
|
22
|
-
}: {
|
|
23
|
-
axis: 'x' | 'y'
|
|
24
|
-
getStart: () => number
|
|
25
|
-
onDrag: (delta: number, startValue: number) => void
|
|
26
|
-
style: React.CSSProperties
|
|
27
|
-
}) {
|
|
28
|
-
const [hovered, setHovered] = useState(false)
|
|
29
|
-
const { startDrag } = useDragScroll(axis, onDrag)
|
|
30
|
-
|
|
31
|
-
return (
|
|
32
|
-
<div
|
|
33
|
-
onMouseDown={event => {
|
|
34
|
-
startDrag(event, getStart())
|
|
35
|
-
}}
|
|
36
|
-
onMouseEnter={() => {
|
|
37
|
-
setHovered(true)
|
|
38
|
-
}}
|
|
39
|
-
onMouseLeave={() => {
|
|
40
|
-
setHovered(false)
|
|
41
|
-
}}
|
|
42
|
-
style={{
|
|
43
|
-
position: 'relative',
|
|
44
|
-
cursor: axis === 'x' ? 'ew-resize' : 'ns-resize',
|
|
45
|
-
background: hovered ? handleBgHover : handleBg,
|
|
46
|
-
...style,
|
|
47
|
-
}}
|
|
48
|
-
/>
|
|
49
|
-
)
|
|
50
|
-
}
|
|
8
|
+
import type { TrackKind } from '../types.ts'
|
|
51
9
|
|
|
52
10
|
export const VerticalResizeHandle = observer(function ({
|
|
53
11
|
model,
|
|
@@ -61,8 +19,9 @@ export const VerticalResizeHandle = observer(function ({
|
|
|
61
19
|
[model],
|
|
62
20
|
)
|
|
63
21
|
return (
|
|
64
|
-
<
|
|
22
|
+
<DragHandle
|
|
65
23
|
axis="x"
|
|
24
|
+
variant="resizer"
|
|
66
25
|
getStart={() => model.treeAreaWidth}
|
|
67
26
|
onDrag={onDrag}
|
|
68
27
|
style={{ width: model.resizeHandleWidth }}
|
|
@@ -82,8 +41,9 @@ export const HorizontalResizeHandle = observer(function ({
|
|
|
82
41
|
[model],
|
|
83
42
|
)
|
|
84
43
|
return (
|
|
85
|
-
<
|
|
44
|
+
<DragHandle
|
|
86
45
|
axis="y"
|
|
46
|
+
variant="resizer"
|
|
87
47
|
getStart={() => model.height}
|
|
88
48
|
onDrag={onDrag}
|
|
89
49
|
style={{ width: '100%', height: model.resizeHandleWidth }}
|
|
@@ -91,21 +51,53 @@ export const HorizontalResizeHandle = observer(function ({
|
|
|
91
51
|
)
|
|
92
52
|
})
|
|
93
53
|
|
|
94
|
-
|
|
54
|
+
// The height behind each resizable track kind. Both bar tracks read one height
|
|
55
|
+
// so conservation and property conservation stay directly comparable; the logo
|
|
56
|
+
// keeps its own, being taller by default. Text tracks are absent: they are one
|
|
57
|
+
// alignment row tall and follow rowHeight, so the zoom controls already size
|
|
58
|
+
// them.
|
|
59
|
+
const trackHeights: Partial<
|
|
60
|
+
Record<
|
|
61
|
+
TrackKind,
|
|
62
|
+
{
|
|
63
|
+
get: (model: MsaViewModel) => number
|
|
64
|
+
set: (model: MsaViewModel, height: number) => void
|
|
65
|
+
}
|
|
66
|
+
>
|
|
67
|
+
> = {
|
|
68
|
+
bar: {
|
|
69
|
+
get: model => model.conservationTrackHeight,
|
|
70
|
+
set: (model, height) => {
|
|
71
|
+
model.setConservationTrackHeight(height)
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
logo: {
|
|
75
|
+
get: model => model.sequenceLogoTrackHeight,
|
|
76
|
+
set: (model, height) => {
|
|
77
|
+
model.setSequenceLogoTrackHeight(height)
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export const TrackResizeHandle = observer(function ({
|
|
95
83
|
model,
|
|
84
|
+
kind,
|
|
96
85
|
}: {
|
|
97
86
|
model: MsaViewModel
|
|
87
|
+
kind: TrackKind
|
|
98
88
|
}) {
|
|
89
|
+
const height = trackHeights[kind]
|
|
99
90
|
const onDrag = useCallback(
|
|
100
91
|
(delta: number, startHeight: number) => {
|
|
101
|
-
model
|
|
92
|
+
height?.set(model, Math.max(10, startHeight + delta))
|
|
102
93
|
},
|
|
103
|
-
[model],
|
|
94
|
+
[model, height],
|
|
104
95
|
)
|
|
105
|
-
return (
|
|
106
|
-
<
|
|
96
|
+
return height ? (
|
|
97
|
+
<DragHandle
|
|
107
98
|
axis="y"
|
|
108
|
-
|
|
99
|
+
variant="resizer"
|
|
100
|
+
getStart={() => height.get(model)}
|
|
109
101
|
onDrag={onDrag}
|
|
110
102
|
style={{
|
|
111
103
|
position: 'absolute',
|
|
@@ -116,5 +108,5 @@ export const ConservationTrackResizeHandle = observer(function ({
|
|
|
116
108
|
zIndex: 1,
|
|
117
109
|
}}
|
|
118
110
|
/>
|
|
119
|
-
)
|
|
111
|
+
) : null
|
|
120
112
|
})
|
|
@@ -2,7 +2,7 @@ import React, { useCallback } from 'react'
|
|
|
2
2
|
|
|
3
3
|
import { observer } from 'mobx-react'
|
|
4
4
|
|
|
5
|
-
import
|
|
5
|
+
import DragHandle from './DragHandle.tsx'
|
|
6
6
|
|
|
7
7
|
import type { MsaViewModel } from '../model.ts'
|
|
8
8
|
|
|
@@ -32,8 +32,9 @@ const VerticalScrollbar = observer(({ model }: { model: MsaViewModel }) => {
|
|
|
32
32
|
boxSizing: 'border-box',
|
|
33
33
|
}}
|
|
34
34
|
>
|
|
35
|
-
<
|
|
35
|
+
<DragHandle
|
|
36
36
|
axis="y"
|
|
37
|
+
variant="thumb"
|
|
37
38
|
getStart={() => model.scrollY}
|
|
38
39
|
onDrag={onDrag}
|
|
39
40
|
style={{
|
|
@@ -10,6 +10,57 @@ import { launchInterProScan } from '../../launchInterProScan.ts'
|
|
|
10
10
|
|
|
11
11
|
import type { MsaViewModel } from '../../model.ts'
|
|
12
12
|
|
|
13
|
+
const FAMILIES = 'Families, domains, sites & repeats'
|
|
14
|
+
const FEATURES = 'Other sequence features'
|
|
15
|
+
const STRUCTURAL = 'Structural domains'
|
|
16
|
+
const OTHER = 'Other category'
|
|
17
|
+
|
|
18
|
+
// The analyses the EBI iprscan5 endpoint accepts, all on by default. Static, so
|
|
19
|
+
// it lives out here rather than being rebuilt on every render of the dialog.
|
|
20
|
+
const interProScanPrograms = [
|
|
21
|
+
{
|
|
22
|
+
name: 'NCBIfam',
|
|
23
|
+
category: FAMILIES,
|
|
24
|
+
description: 'NCBI RefSeq FAMs including TIGRFAMs',
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
name: 'SFLD',
|
|
28
|
+
category: FAMILIES,
|
|
29
|
+
description: 'Structure function linkage database',
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
name: 'Phobius',
|
|
33
|
+
category: FEATURES,
|
|
34
|
+
description:
|
|
35
|
+
'A combined transmembrane topology and signal peptide predictor',
|
|
36
|
+
},
|
|
37
|
+
{ name: 'SignalP', category: FEATURES },
|
|
38
|
+
{ name: 'SignalP_EUK', category: OTHER },
|
|
39
|
+
{ name: 'SignalP_GRAM_POSITIVE', category: OTHER },
|
|
40
|
+
{ name: 'SignalP_GRAM_NEGATIVE', category: OTHER },
|
|
41
|
+
{ name: 'SuperFamily', category: STRUCTURAL },
|
|
42
|
+
{ name: 'Panther', category: FAMILIES },
|
|
43
|
+
{ name: 'Gene3d', category: STRUCTURAL },
|
|
44
|
+
{ name: 'HAMAP', category: FAMILIES },
|
|
45
|
+
{ name: 'PrositeProfiles', category: FAMILIES },
|
|
46
|
+
{ name: 'PrositePatterns', category: FAMILIES },
|
|
47
|
+
{ name: 'Coils', category: FEATURES },
|
|
48
|
+
{ name: 'SMART', category: FAMILIES },
|
|
49
|
+
{
|
|
50
|
+
name: 'CDD',
|
|
51
|
+
category: FAMILIES,
|
|
52
|
+
description: 'Conserved Domains Database',
|
|
53
|
+
},
|
|
54
|
+
{ name: 'PRINTS', category: FAMILIES },
|
|
55
|
+
{ name: 'PfamA', category: FAMILIES },
|
|
56
|
+
{ name: 'MobiDBLite', category: FEATURES },
|
|
57
|
+
{ name: 'PIRSF', category: OTHER },
|
|
58
|
+
{ name: 'TMHMM', category: FEATURES },
|
|
59
|
+
{ name: 'AntiFam', category: OTHER },
|
|
60
|
+
{ name: 'FunFam', category: OTHER },
|
|
61
|
+
{ name: 'PIRSR', category: FAMILIES },
|
|
62
|
+
].map(program => ({ ...program, checked: true }))
|
|
63
|
+
|
|
13
64
|
const InterProScanDialog = observer(function ({
|
|
14
65
|
handleClose,
|
|
15
66
|
model,
|
|
@@ -17,134 +68,7 @@ const InterProScanDialog = observer(function ({
|
|
|
17
68
|
handleClose: () => void
|
|
18
69
|
model: MsaViewModel
|
|
19
70
|
}) {
|
|
20
|
-
const [vals, setVals] = useState(
|
|
21
|
-
{
|
|
22
|
-
name: 'NCBIfam',
|
|
23
|
-
description: 'NCBI RefSeq FAMs including TIGRFAMs',
|
|
24
|
-
category: 'Families, domains, sites & repeats',
|
|
25
|
-
checked: true,
|
|
26
|
-
},
|
|
27
|
-
{
|
|
28
|
-
name: 'SFLD',
|
|
29
|
-
description: 'Structure function linkage database',
|
|
30
|
-
category: 'Families, domains, sites & repeats',
|
|
31
|
-
checked: true,
|
|
32
|
-
},
|
|
33
|
-
{
|
|
34
|
-
name: 'Phobius',
|
|
35
|
-
checked: true,
|
|
36
|
-
description:
|
|
37
|
-
'A combined transmembrane topology and signal peptide predictor',
|
|
38
|
-
category: 'Other sequence features',
|
|
39
|
-
},
|
|
40
|
-
{
|
|
41
|
-
name: 'SignalP',
|
|
42
|
-
checked: true,
|
|
43
|
-
category: 'Other sequence features',
|
|
44
|
-
},
|
|
45
|
-
{
|
|
46
|
-
name: 'SignalP_EUK',
|
|
47
|
-
category: 'Other category',
|
|
48
|
-
checked: true,
|
|
49
|
-
},
|
|
50
|
-
{
|
|
51
|
-
name: 'SignalP_GRAM_POSITIVE',
|
|
52
|
-
category: 'Other category',
|
|
53
|
-
checked: true,
|
|
54
|
-
},
|
|
55
|
-
{
|
|
56
|
-
name: 'SignalP_GRAM_NEGATIVE',
|
|
57
|
-
checked: true,
|
|
58
|
-
category: 'Other category',
|
|
59
|
-
},
|
|
60
|
-
{
|
|
61
|
-
name: 'SuperFamily',
|
|
62
|
-
category: 'Structural domains',
|
|
63
|
-
checked: true,
|
|
64
|
-
},
|
|
65
|
-
{
|
|
66
|
-
name: 'Panther',
|
|
67
|
-
category: 'Families, domains, sites & repeats',
|
|
68
|
-
checked: true,
|
|
69
|
-
},
|
|
70
|
-
{
|
|
71
|
-
name: 'Gene3d',
|
|
72
|
-
category: 'Structural domains',
|
|
73
|
-
checked: true,
|
|
74
|
-
},
|
|
75
|
-
{
|
|
76
|
-
name: 'HAMAP',
|
|
77
|
-
category: 'Families, domains, sites & repeats',
|
|
78
|
-
checked: true,
|
|
79
|
-
},
|
|
80
|
-
{
|
|
81
|
-
name: 'PrositeProfiles',
|
|
82
|
-
category: 'Families, domains, sites & repeats',
|
|
83
|
-
checked: true,
|
|
84
|
-
},
|
|
85
|
-
{
|
|
86
|
-
name: 'PrositePatterns',
|
|
87
|
-
category: 'Families, domains, sites & repeats',
|
|
88
|
-
checked: true,
|
|
89
|
-
},
|
|
90
|
-
{
|
|
91
|
-
name: 'Coils',
|
|
92
|
-
category: 'Other sequence features',
|
|
93
|
-
checked: true,
|
|
94
|
-
},
|
|
95
|
-
{
|
|
96
|
-
name: 'SMART',
|
|
97
|
-
category: 'Families, domains, sites & repeats',
|
|
98
|
-
checked: true,
|
|
99
|
-
},
|
|
100
|
-
{
|
|
101
|
-
name: 'CDD',
|
|
102
|
-
description: 'Conserved Domains Database',
|
|
103
|
-
category: 'Families, domains, sites & repeats',
|
|
104
|
-
checked: true,
|
|
105
|
-
},
|
|
106
|
-
{
|
|
107
|
-
name: 'PRINTS',
|
|
108
|
-
category: 'Families, domains, sites & repeats',
|
|
109
|
-
checked: true,
|
|
110
|
-
},
|
|
111
|
-
{
|
|
112
|
-
name: 'PfamA',
|
|
113
|
-
category: 'Families, domains, sites & repeats',
|
|
114
|
-
checked: true,
|
|
115
|
-
},
|
|
116
|
-
{
|
|
117
|
-
name: 'MobiDBLite',
|
|
118
|
-
checked: true,
|
|
119
|
-
category: 'Other sequence features',
|
|
120
|
-
},
|
|
121
|
-
{
|
|
122
|
-
name: 'PIRSF',
|
|
123
|
-
checked: true,
|
|
124
|
-
category: 'Other category',
|
|
125
|
-
},
|
|
126
|
-
{
|
|
127
|
-
name: 'TMHMM',
|
|
128
|
-
checked: true,
|
|
129
|
-
category: 'Other sequence features',
|
|
130
|
-
},
|
|
131
|
-
|
|
132
|
-
{
|
|
133
|
-
name: 'AntiFam',
|
|
134
|
-
checked: true,
|
|
135
|
-
category: 'Other category',
|
|
136
|
-
},
|
|
137
|
-
{
|
|
138
|
-
name: 'FunFam',
|
|
139
|
-
checked: true,
|
|
140
|
-
category: 'Other category',
|
|
141
|
-
},
|
|
142
|
-
{
|
|
143
|
-
name: 'PIRSR',
|
|
144
|
-
checked: true,
|
|
145
|
-
category: 'Families, domains, sites & repeats',
|
|
146
|
-
},
|
|
147
|
-
])
|
|
71
|
+
const [vals, setVals] = useState(interProScanPrograms)
|
|
148
72
|
|
|
149
73
|
const programs = vals.filter(e => e.checked).map(e => e.name)
|
|
150
74
|
const [show, setShow] = useState(false)
|
|
@@ -197,12 +121,11 @@ const InterProScanDialog = observer(function ({
|
|
|
197
121
|
<tbody>
|
|
198
122
|
{vals
|
|
199
123
|
.toSorted((a, b) => a.category.localeCompare(b.category))
|
|
200
|
-
.map(({ name, checked, category }) => (
|
|
124
|
+
.map(({ name, checked, category, description }) => (
|
|
201
125
|
<tr key={name}>
|
|
202
126
|
<td>
|
|
203
127
|
<input
|
|
204
128
|
type="checkbox"
|
|
205
|
-
key={name}
|
|
206
129
|
checked={checked}
|
|
207
130
|
onChange={() => {
|
|
208
131
|
setVals(
|
|
@@ -215,7 +138,7 @@ const InterProScanDialog = observer(function ({
|
|
|
215
138
|
}}
|
|
216
139
|
/>
|
|
217
140
|
</td>
|
|
218
|
-
<td>{name}</td>
|
|
141
|
+
<td title={description}>{name}</td>
|
|
219
142
|
<td>{category}</td>
|
|
220
143
|
</tr>
|
|
221
144
|
))}
|
|
@@ -12,9 +12,27 @@ function toggle(label: string, checked: boolean, set: (arg: boolean) => void) {
|
|
|
12
12
|
}
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
+
// Every track the alignment can show, with its current state. Without this the
|
|
16
|
+
// "Close" item on a track's own dropdown is a one-way door -- there is nowhere
|
|
17
|
+
// else in the UI to turn a track back on.
|
|
18
|
+
function tracksSubMenu(model: MsaViewModel): MenuItem[] {
|
|
19
|
+
const shown = new Set(model.turnedOnTracks.map(t => t.model.id))
|
|
20
|
+
return model.tracks.map(({ model: { id, name } }) =>
|
|
21
|
+
toggle(name, shown.has(id), () => {
|
|
22
|
+
model.toggleTrack(id)
|
|
23
|
+
}),
|
|
24
|
+
)
|
|
25
|
+
}
|
|
26
|
+
|
|
15
27
|
export function msaSettingsMenuItems(model: MsaViewModel): MenuItem[] {
|
|
16
28
|
const { drawMsaLetters, hideGaps, bgColor, showColumnStats } = model
|
|
17
29
|
return [
|
|
30
|
+
{
|
|
31
|
+
label: 'Tracks',
|
|
32
|
+
type: 'subMenu' as const,
|
|
33
|
+
subMenu: tracksSubMenu(model),
|
|
34
|
+
},
|
|
35
|
+
{ type: 'divider' as const },
|
|
18
36
|
toggle('Show column statistics on hover', showColumnStats, arg => {
|
|
19
37
|
model.setShowColumnStats(arg)
|
|
20
38
|
}),
|
|
@@ -1,19 +1,56 @@
|
|
|
1
1
|
import React, { useCallback } from 'react'
|
|
2
2
|
|
|
3
|
+
import { useTheme } from '@mui/material'
|
|
3
4
|
import { observer } from 'mobx-react'
|
|
4
5
|
|
|
5
|
-
import
|
|
6
|
+
import { useCanvasAutorun } from '../../useCanvasAutorun.ts'
|
|
7
|
+
import DragHandle, { scrollbarThumbFill } from '../DragHandle.tsx'
|
|
8
|
+
import { msaThumbnail } from '../msa/msaRaster.ts'
|
|
6
9
|
import { MINIMAP_BAR_HEIGHT, getMinimapLayout } from './minimapLayout.ts'
|
|
7
10
|
|
|
8
11
|
import type { MsaViewModel } from '../../model.ts'
|
|
9
12
|
|
|
10
13
|
const Minimap = observer(function ({ model }: { model: MsaViewModel }) {
|
|
11
|
-
const { minimapHeight, msaCanvasWidth } = model
|
|
14
|
+
const { minimapHeight, msaCanvasWidth, highResScaleFactor } = model
|
|
15
|
+
const theme = useTheme()
|
|
12
16
|
const { unit, s, w, polygonHeight, polygonPoints } = getMinimapLayout(
|
|
13
17
|
model,
|
|
14
18
|
msaCanvasWidth,
|
|
15
19
|
)
|
|
16
20
|
|
|
21
|
+
// the whole alignment, sampled down to the bar: it says where the conserved
|
|
22
|
+
// blocks and the gappy stretches are, which is what a scroll aims at
|
|
23
|
+
const barWidth = Math.round(msaCanvasWidth * highResScaleFactor)
|
|
24
|
+
const barHeight = Math.round(MINIMAP_BAR_HEIGHT * highResScaleFactor)
|
|
25
|
+
const ref = useCanvasAutorun({
|
|
26
|
+
draw: ctx => {
|
|
27
|
+
ctx.resetTransform()
|
|
28
|
+
ctx.clearRect(0, 0, barWidth, barHeight)
|
|
29
|
+
const thumbnail = msaThumbnail({
|
|
30
|
+
model,
|
|
31
|
+
theme,
|
|
32
|
+
height: MINIMAP_BAR_HEIGHT,
|
|
33
|
+
})
|
|
34
|
+
if (thumbnail) {
|
|
35
|
+
ctx.imageSmoothingEnabled = true
|
|
36
|
+
ctx.drawImage(
|
|
37
|
+
thumbnail,
|
|
38
|
+
0,
|
|
39
|
+
0,
|
|
40
|
+
thumbnail.width,
|
|
41
|
+
thumbnail.height,
|
|
42
|
+
0,
|
|
43
|
+
0,
|
|
44
|
+
barWidth,
|
|
45
|
+
barHeight,
|
|
46
|
+
)
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
width: barWidth,
|
|
50
|
+
height: barHeight,
|
|
51
|
+
deps: [model, theme],
|
|
52
|
+
})
|
|
53
|
+
|
|
17
54
|
const onDrag = useCallback(
|
|
18
55
|
(delta: number, startScroll: number) => {
|
|
19
56
|
model.setScrollX(startScroll - delta / unit)
|
|
@@ -33,15 +70,21 @@ const Minimap = observer(function ({ model }: { model: MsaViewModel }) {
|
|
|
33
70
|
flexShrink: 0,
|
|
34
71
|
}}
|
|
35
72
|
>
|
|
36
|
-
<
|
|
73
|
+
<canvas
|
|
74
|
+
ref={ref}
|
|
75
|
+
width={barWidth}
|
|
76
|
+
height={barHeight}
|
|
37
77
|
style={{
|
|
78
|
+
display: 'block',
|
|
79
|
+
width: msaCanvasWidth,
|
|
38
80
|
height: MINIMAP_BAR_HEIGHT,
|
|
39
81
|
boxSizing: 'border-box',
|
|
40
82
|
border: '1px solid #555',
|
|
41
83
|
}}
|
|
42
84
|
/>
|
|
43
|
-
<
|
|
85
|
+
<DragHandle
|
|
44
86
|
axis="x"
|
|
87
|
+
variant="thumb"
|
|
45
88
|
getStart={() => model.scrollX}
|
|
46
89
|
onDrag={onDrag}
|
|
47
90
|
style={{
|
|
@@ -9,8 +9,16 @@ import MSACanvasBlock from './MSACanvasBlock.tsx'
|
|
|
9
9
|
import type { MsaViewModel } from '../../model.ts'
|
|
10
10
|
|
|
11
11
|
const MSACanvas = observer(function ({ model }: { model: MsaViewModel }) {
|
|
12
|
-
const {
|
|
13
|
-
|
|
12
|
+
const {
|
|
13
|
+
MSA,
|
|
14
|
+
msaFilehandle,
|
|
15
|
+
height,
|
|
16
|
+
msaCanvasWidth,
|
|
17
|
+
blocks2d,
|
|
18
|
+
scrollZoom,
|
|
19
|
+
scrollX,
|
|
20
|
+
scrollY,
|
|
21
|
+
} = model
|
|
14
22
|
const ref = useRef<HTMLDivElement>(null)
|
|
15
23
|
const onScrollX = useCallback(
|
|
16
24
|
(d: number) => {
|
|
@@ -59,14 +67,26 @@ const MSACanvas = observer(function ({ model }: { model: MsaViewModel }) {
|
|
|
59
67
|
}}
|
|
60
68
|
>
|
|
61
69
|
{!MSA && !msaFilehandle ? null : MSA ? (
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
+
// one transform for the whole block set, so a scroll frame is a
|
|
71
|
+
// compositor move rather than a style write per block
|
|
72
|
+
<div
|
|
73
|
+
style={{
|
|
74
|
+
position: 'absolute',
|
|
75
|
+
top: 0,
|
|
76
|
+
left: 0,
|
|
77
|
+
transform: `translate(${scrollX}px, ${scrollY}px)`,
|
|
78
|
+
willChange: 'transform',
|
|
79
|
+
}}
|
|
80
|
+
>
|
|
81
|
+
{blocks2d.map(([bx, by]) => (
|
|
82
|
+
<MSACanvasBlock
|
|
83
|
+
key={`${bx}_${by}`}
|
|
84
|
+
model={model}
|
|
85
|
+
offsetX={bx}
|
|
86
|
+
offsetY={by}
|
|
87
|
+
/>
|
|
88
|
+
))}
|
|
89
|
+
</div>
|
|
70
90
|
) : (
|
|
71
91
|
<Loading />
|
|
72
92
|
)}
|
|
@@ -6,6 +6,7 @@ import { observer } from 'mobx-react'
|
|
|
6
6
|
import { useCanvasAutorun } from '../../useCanvasAutorun.ts'
|
|
7
7
|
import { useColorContrast } from '../../useColorContrast.ts'
|
|
8
8
|
import ColumnStats from './ColumnStats.tsx'
|
|
9
|
+
import { drawMsaRaster, rasterSupported } from './msaRaster.ts'
|
|
9
10
|
import { renderBoxFeatureCanvasBlock } from './renderBoxFeatureCanvasBlock.ts'
|
|
10
11
|
import { renderMSABlock } from './renderMSABlock.ts'
|
|
11
12
|
import { useMsaBlockMouse } from './useMsaBlockMouse.ts'
|
|
@@ -21,7 +22,7 @@ const MSACanvasBlock = observer(function ({
|
|
|
21
22
|
offsetX: number
|
|
22
23
|
offsetY: number
|
|
23
24
|
}) {
|
|
24
|
-
const {
|
|
25
|
+
const { colorScheme, blockSize, highResScaleFactor } = model
|
|
25
26
|
const { theme, contrastScheme } = useColorContrast(colorScheme)
|
|
26
27
|
const { tooltipPoint, onMouseMove, onClick, onMouseLeave } = useMsaBlockMouse(
|
|
27
28
|
{
|
|
@@ -42,6 +43,14 @@ const MSACanvasBlock = observer(function ({
|
|
|
42
43
|
offsetY,
|
|
43
44
|
model,
|
|
44
45
|
})
|
|
46
|
+
// the alignment background comes from zoom-independent raster tiles; the
|
|
47
|
+
// domain overlay paints its own boxes instead, and letter-color mode has
|
|
48
|
+
// no background to paint
|
|
49
|
+
const rasterTiles =
|
|
50
|
+
!model.actuallyShowDomains && model.bgColor && rasterSupported()
|
|
51
|
+
if (rasterTiles) {
|
|
52
|
+
drawMsaRaster({ ctx, model, theme, offsetX, offsetY })
|
|
53
|
+
}
|
|
45
54
|
renderMSABlock({
|
|
46
55
|
ctx,
|
|
47
56
|
theme,
|
|
@@ -49,6 +58,7 @@ const MSACanvasBlock = observer(function ({
|
|
|
49
58
|
offsetY,
|
|
50
59
|
contrastScheme,
|
|
51
60
|
model,
|
|
61
|
+
rasterTiles,
|
|
52
62
|
})
|
|
53
63
|
},
|
|
54
64
|
width: canvasSize,
|
|
@@ -79,8 +89,8 @@ const MSACanvasBlock = observer(function ({
|
|
|
79
89
|
height={canvasSize}
|
|
80
90
|
style={{
|
|
81
91
|
position: 'absolute',
|
|
82
|
-
top:
|
|
83
|
-
left:
|
|
92
|
+
top: offsetY,
|
|
93
|
+
left: offsetX,
|
|
84
94
|
width: blockSize,
|
|
85
95
|
height: blockSize,
|
|
86
96
|
}}
|