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
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
// @vitest-environment jsdom
|
|
2
|
+
import { beforeAll, describe, expect, test } from 'vitest'
|
|
3
|
+
|
|
4
|
+
import MSAModelF from './model.ts'
|
|
5
|
+
|
|
6
|
+
beforeAll(() => {
|
|
7
|
+
HTMLCanvasElement.prototype.getContext = function () {
|
|
8
|
+
let font = '10px sans-serif'
|
|
9
|
+
return {
|
|
10
|
+
get font() {
|
|
11
|
+
return font
|
|
12
|
+
},
|
|
13
|
+
set font(v: string) {
|
|
14
|
+
font = v
|
|
15
|
+
},
|
|
16
|
+
measureText(t: string) {
|
|
17
|
+
const size = Number.parseFloat(font) || 10
|
|
18
|
+
return { width: t.length * size * 0.6 } as TextMetrics
|
|
19
|
+
},
|
|
20
|
+
} as unknown as CanvasRenderingContext2D
|
|
21
|
+
} as unknown as typeof HTMLCanvasElement.prototype.getContext
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
// col0 is invariant M; col3 varies V/V/A (all hydrophobic) so property
|
|
25
|
+
// conservation stays high while identity conservation drops
|
|
26
|
+
const protein = `>a
|
|
27
|
+
MKLVIL
|
|
28
|
+
>b
|
|
29
|
+
MRLVIL
|
|
30
|
+
>c
|
|
31
|
+
MKLAIL`
|
|
32
|
+
|
|
33
|
+
const dna = `>a
|
|
34
|
+
ACGTAC
|
|
35
|
+
>b
|
|
36
|
+
ACGTAG
|
|
37
|
+
>c
|
|
38
|
+
ACGAAC`
|
|
39
|
+
|
|
40
|
+
function make(msa: string, id: string) {
|
|
41
|
+
return MSAModelF().create({
|
|
42
|
+
id,
|
|
43
|
+
type: 'MsaView',
|
|
44
|
+
msaFormat: 'fasta',
|
|
45
|
+
data: { msa },
|
|
46
|
+
})
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
describe('property conservation track', () => {
|
|
50
|
+
test('present for protein alignments', () => {
|
|
51
|
+
const ids = make(protein, 'p').tracks.map(t => t.model.id)
|
|
52
|
+
expect(ids).toContain('conservation')
|
|
53
|
+
expect(ids).toContain('property-conservation')
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
test('omitted for nucleotide alignments', () => {
|
|
57
|
+
const ids = make(dna, 'd').tracks.map(t => t.model.id)
|
|
58
|
+
expect(ids).toContain('conservation')
|
|
59
|
+
expect(ids).not.toContain('property-conservation')
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
test('conservative-substitution column: property beats identity', () => {
|
|
63
|
+
const model = make(protein, 'pc')
|
|
64
|
+
// col3 = V/V/A: same property class, different identity
|
|
65
|
+
expect(model.propertyConservation[3]!).toBeGreaterThan(
|
|
66
|
+
model.conservation[3]!,
|
|
67
|
+
)
|
|
68
|
+
})
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
describe('mouseOverColumnStats', () => {
|
|
72
|
+
test('undefined when nothing hovered', () => {
|
|
73
|
+
expect(make(protein, 'm0').mouseOverColumnStats).toBeUndefined()
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
test('reports consensus, gaps and distribution for hovered column', () => {
|
|
77
|
+
const model = make(protein, 'm1')
|
|
78
|
+
model.setMousePos(0, 0)
|
|
79
|
+
const s = model.mouseOverColumnStats
|
|
80
|
+
expect(s?.consensusLetter).toBe('M')
|
|
81
|
+
expect(s?.consensusFraction).toBe(1)
|
|
82
|
+
expect(s?.gapFraction).toBe(0)
|
|
83
|
+
expect(s?.total).toBe(3)
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
test('mixed column reports the majority residue as consensus', () => {
|
|
87
|
+
const model = make(protein, 'm2')
|
|
88
|
+
// col1 = K/R/K -> consensus K at 2/3
|
|
89
|
+
model.setMousePos(1, 0)
|
|
90
|
+
const s = model.mouseOverColumnStats
|
|
91
|
+
expect(s?.consensusLetter).toBe('K')
|
|
92
|
+
expect(s?.consensusCount).toBe(2)
|
|
93
|
+
})
|
|
94
|
+
})
|
|
@@ -1,34 +1,33 @@
|
|
|
1
|
-
import React
|
|
1
|
+
import React from 'react'
|
|
2
2
|
|
|
3
|
-
import { autorun } from 'mobx'
|
|
4
3
|
import { observer } from 'mobx-react'
|
|
5
4
|
|
|
6
5
|
import { ConservationTrackResizeHandle } from './ResizeHandles.tsx'
|
|
7
|
-
import {
|
|
6
|
+
import { useCanvasAutorun } from '../useCanvasAutorun.ts'
|
|
7
|
+
import {
|
|
8
|
+
barTrackValues,
|
|
9
|
+
drawConservationBars,
|
|
10
|
+
} from './tracks/renderTracksSvg.ts'
|
|
8
11
|
|
|
9
12
|
import type { MsaViewModel } from '../model.ts'
|
|
10
13
|
import type { BasicTrack } from '../types.ts'
|
|
11
14
|
|
|
12
15
|
const ConservationBlock = observer(function ({
|
|
13
16
|
model,
|
|
17
|
+
track,
|
|
14
18
|
offsetX,
|
|
15
19
|
trackHeight,
|
|
16
20
|
}: {
|
|
17
21
|
model: MsaViewModel
|
|
22
|
+
track: BasicTrack
|
|
18
23
|
offsetX: number
|
|
19
24
|
trackHeight: number
|
|
20
25
|
}) {
|
|
21
26
|
const { blockSize, scrollX, highResScaleFactor } = model
|
|
22
27
|
|
|
23
|
-
const ref =
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
const ctx = ref.current?.getContext('2d')
|
|
27
|
-
if (!ctx) {
|
|
28
|
-
return
|
|
29
|
-
}
|
|
30
|
-
return autorun(() => {
|
|
31
|
-
const { blockSize, colWidth, highResScaleFactor, conservation } = model
|
|
28
|
+
const ref = useCanvasAutorun(
|
|
29
|
+
ctx => {
|
|
30
|
+
const { blockSize, colWidth, highResScaleFactor } = model
|
|
32
31
|
ctx.resetTransform()
|
|
33
32
|
ctx.scale(highResScaleFactor, highResScaleFactor)
|
|
34
33
|
ctx.clearRect(0, 0, blockSize, trackHeight)
|
|
@@ -36,14 +35,16 @@ const ConservationBlock = observer(function ({
|
|
|
36
35
|
|
|
37
36
|
drawConservationBars({
|
|
38
37
|
ctx,
|
|
39
|
-
|
|
38
|
+
values: barTrackValues(model, track.model.id),
|
|
39
|
+
color: track.model.barColor ?? 'gray',
|
|
40
40
|
colWidth,
|
|
41
41
|
trackHeight,
|
|
42
42
|
offsetX,
|
|
43
43
|
blockSize,
|
|
44
44
|
})
|
|
45
|
-
}
|
|
46
|
-
|
|
45
|
+
},
|
|
46
|
+
[model, track, offsetX, trackHeight],
|
|
47
|
+
)
|
|
47
48
|
|
|
48
49
|
return (
|
|
49
50
|
<canvas
|
|
@@ -83,6 +84,7 @@ const ConservationTrack = observer(function ({
|
|
|
83
84
|
<ConservationBlock
|
|
84
85
|
key={bx}
|
|
85
86
|
model={model}
|
|
87
|
+
track={track}
|
|
86
88
|
offsetX={bx}
|
|
87
89
|
trackHeight={trackHeight}
|
|
88
90
|
/>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { useEffect,
|
|
1
|
+
import React, { useEffect, useState } from 'react'
|
|
2
2
|
|
|
3
3
|
import { createJBrowseTheme } from '@jbrowse/core/ui/theme'
|
|
4
4
|
import useMeasure from '@jbrowse/core/util/useMeasure'
|
|
@@ -9,6 +9,8 @@ import Loading from './Loading.tsx'
|
|
|
9
9
|
|
|
10
10
|
import type { FileLocation as FileLocationType } from '@jbrowse/core/util/types'
|
|
11
11
|
|
|
12
|
+
const theme = createJBrowseTheme()
|
|
13
|
+
|
|
12
14
|
interface MSAViewerProps {
|
|
13
15
|
msa?: string
|
|
14
16
|
tree?: string
|
|
@@ -18,8 +20,20 @@ interface MSAViewerProps {
|
|
|
18
20
|
gffFilehandle?: FileLocationType
|
|
19
21
|
colorScheme?: string
|
|
20
22
|
height?: number
|
|
23
|
+
/** initial per-column pixel width (zoom level) */
|
|
24
|
+
colWidth?: number
|
|
25
|
+
/** initial per-row pixel height */
|
|
26
|
+
rowHeight?: number
|
|
27
|
+
/** alignment columns (0-based) to highlight with a persistent overlay */
|
|
28
|
+
highlightColumns?: number[]
|
|
21
29
|
/** row name to diff every other row against (matches render as ".") */
|
|
22
30
|
relativeTo?: string
|
|
31
|
+
/** draw the phylogenetic tree (default true); false leaves a labels-only gutter */
|
|
32
|
+
drawTree?: boolean
|
|
33
|
+
/** fixed width (px) of the tree/label area */
|
|
34
|
+
treeAreaWidth?: number
|
|
35
|
+
/** auto-size the tree/label area to the labels (used when drawTree is false) */
|
|
36
|
+
autoTreeAreaWidth?: boolean
|
|
23
37
|
}
|
|
24
38
|
|
|
25
39
|
export default function MSAViewer({
|
|
@@ -31,25 +45,35 @@ export default function MSAViewer({
|
|
|
31
45
|
gffFilehandle,
|
|
32
46
|
colorScheme,
|
|
33
47
|
height,
|
|
48
|
+
colWidth,
|
|
49
|
+
rowHeight,
|
|
50
|
+
highlightColumns,
|
|
34
51
|
relativeTo,
|
|
52
|
+
drawTree,
|
|
53
|
+
treeAreaWidth,
|
|
54
|
+
autoTreeAreaWidth,
|
|
35
55
|
}: MSAViewerProps) {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
}),
|
|
51
|
-
|
|
52
|
-
|
|
56
|
+
// lazy initializer: the model is created exactly once from the initial props
|
|
57
|
+
// (a stable MST instance — not a value safe to recompute, so not useMemo)
|
|
58
|
+
const [model] = useState(() =>
|
|
59
|
+
MSAModelF().create({
|
|
60
|
+
type: 'MsaView',
|
|
61
|
+
...(msa || tree || gff
|
|
62
|
+
? { data: { msa: msa ?? '', tree: tree ?? '', gff } }
|
|
63
|
+
: {}),
|
|
64
|
+
...(msaFilehandle ? { msaFilehandle } : {}),
|
|
65
|
+
...(treeFilehandle ? { treeFilehandle } : {}),
|
|
66
|
+
...(gffFilehandle ? { gffFilehandle } : {}),
|
|
67
|
+
...(colorScheme ? { colorSchemeName: colorScheme } : {}),
|
|
68
|
+
...(height ? { height } : {}),
|
|
69
|
+
...(colWidth ? { colWidth } : {}),
|
|
70
|
+
...(rowHeight ? { rowHeight } : {}),
|
|
71
|
+
...(highlightColumns ? { highlightColumns } : {}),
|
|
72
|
+
...(relativeTo ? { relativeTo } : {}),
|
|
73
|
+
...(drawTree !== undefined ? { drawTree } : {}),
|
|
74
|
+
...(treeAreaWidth ? { treeAreaWidth } : {}),
|
|
75
|
+
...(autoTreeAreaWidth ? { autoTreeAreaWidth } : {}),
|
|
76
|
+
}),
|
|
53
77
|
)
|
|
54
78
|
|
|
55
79
|
const [ref, { width }] = useMeasure()
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React, { useState } from 'react'
|
|
2
2
|
|
|
3
|
+
import { makeStyles } from '@jbrowse/core/util/tss-react'
|
|
3
4
|
import { Button, TextField } from '@mui/material'
|
|
4
|
-
import { makeStyles } from 'tss-react/mui'
|
|
5
5
|
|
|
6
6
|
import Checkbox2 from './Checkbox2.tsx'
|
|
7
7
|
import copy from '../vendor/copyToClipboard.ts'
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import React
|
|
1
|
+
import React from 'react'
|
|
2
2
|
|
|
3
|
-
import { autorun } from 'mobx'
|
|
4
3
|
import { observer } from 'mobx-react'
|
|
5
4
|
|
|
6
5
|
import { setFontSize } from '../setFontSize.ts'
|
|
6
|
+
import { useCanvasAutorun } from '../useCanvasAutorun.ts'
|
|
7
7
|
import { useColorContrast } from '../useColorContrast.ts'
|
|
8
8
|
import { drawTextTrackContent } from './tracks/renderTracksSvg.ts'
|
|
9
9
|
|
|
@@ -31,14 +31,9 @@ const AnnotationBlock = observer(function ({
|
|
|
31
31
|
} = track
|
|
32
32
|
|
|
33
33
|
const colorScheme = customColorScheme ?? modelColorScheme
|
|
34
|
-
const ref = useRef<HTMLCanvasElement>(null)
|
|
35
34
|
const { contrastScheme } = useColorContrast(colorScheme)
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
if (!ctx) {
|
|
39
|
-
return
|
|
40
|
-
}
|
|
41
|
-
return autorun(() => {
|
|
35
|
+
const ref = useCanvasAutorun(
|
|
36
|
+
ctx => {
|
|
42
37
|
const {
|
|
43
38
|
blockSize,
|
|
44
39
|
bgColor,
|
|
@@ -65,8 +60,9 @@ const AnnotationBlock = observer(function ({
|
|
|
65
60
|
offsetX,
|
|
66
61
|
blockSize,
|
|
67
62
|
})
|
|
68
|
-
}
|
|
69
|
-
|
|
63
|
+
},
|
|
64
|
+
[model, offsetX, contrastScheme, colorScheme, data],
|
|
65
|
+
)
|
|
70
66
|
return (
|
|
71
67
|
<canvas
|
|
72
68
|
ref={ref}
|
package/src/components/Track.tsx
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import React, { lazy, useCallback, useRef, useState } from 'react'
|
|
2
2
|
|
|
3
|
+
import { makeStyles } from '@jbrowse/core/util/tss-react'
|
|
3
4
|
import ArrowDropDownIcon from '@mui/icons-material/ArrowDropDown'
|
|
4
5
|
import { IconButton, Menu, MenuItem } from '@mui/material'
|
|
5
6
|
import { observer } from 'mobx-react'
|
|
6
|
-
import { makeStyles } from 'tss-react/mui'
|
|
7
7
|
|
|
8
8
|
import { useWheelScroll } from '../useWheelScroll.ts'
|
|
9
9
|
|
|
@@ -26,12 +26,15 @@ const TrackLabel = observer(function TrackLabel({
|
|
|
26
26
|
track: BasicTrack
|
|
27
27
|
}) {
|
|
28
28
|
const [anchorEl, setAnchorEl] = useState<HTMLButtonElement>()
|
|
29
|
-
const
|
|
29
|
+
const closeMenu = () => {
|
|
30
|
+
setAnchorEl(undefined)
|
|
31
|
+
}
|
|
32
|
+
const { drawLabels, fontSize, treeAreaWidth: width } = model
|
|
30
33
|
const {
|
|
31
34
|
model: { name, height },
|
|
32
35
|
} = track
|
|
33
36
|
const { classes } = useStyles()
|
|
34
|
-
const trackLabelHeight = Math.min(height,
|
|
37
|
+
const trackLabelHeight = Math.min(height, fontSize)
|
|
35
38
|
|
|
36
39
|
return (
|
|
37
40
|
<div
|
|
@@ -62,14 +65,14 @@ const TrackLabel = observer(function TrackLabel({
|
|
|
62
65
|
transitionDuration={0}
|
|
63
66
|
open
|
|
64
67
|
onClose={() => {
|
|
65
|
-
|
|
68
|
+
closeMenu()
|
|
66
69
|
}}
|
|
67
70
|
>
|
|
68
71
|
<MenuItem
|
|
69
72
|
dense
|
|
70
73
|
onClick={() => {
|
|
71
74
|
model.toggleTrack(track.model.id)
|
|
72
|
-
|
|
75
|
+
closeMenu()
|
|
73
76
|
}}
|
|
74
77
|
>
|
|
75
78
|
Close
|
|
@@ -81,7 +84,7 @@ const TrackLabel = observer(function TrackLabel({
|
|
|
81
84
|
TrackInfoDialog,
|
|
82
85
|
{ onClose, model: track.model },
|
|
83
86
|
])
|
|
84
|
-
|
|
87
|
+
closeMenu()
|
|
85
88
|
}}
|
|
86
89
|
>
|
|
87
90
|
Get info
|
|
@@ -1,11 +1,18 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
2
|
|
|
3
3
|
import { Dialog } from '@jbrowse/core/ui'
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
Button,
|
|
6
|
+
Checkbox,
|
|
7
|
+
DialogContent,
|
|
8
|
+
Table,
|
|
9
|
+
TableBody,
|
|
10
|
+
TableCell,
|
|
11
|
+
TableHead,
|
|
12
|
+
TableRow,
|
|
13
|
+
} from '@mui/material'
|
|
5
14
|
import { observer } from 'mobx-react'
|
|
6
15
|
|
|
7
|
-
import { getPalette } from '../../ggplotPalettes.ts'
|
|
8
|
-
|
|
9
16
|
import type { MsaViewModel } from '../../model.ts'
|
|
10
17
|
|
|
11
18
|
const Toggles = observer(function ({ model }: { model: MsaViewModel }) {
|
|
@@ -35,53 +42,60 @@ const Toggles = observer(function ({ model }: { model: MsaViewModel }) {
|
|
|
35
42
|
)
|
|
36
43
|
})
|
|
37
44
|
|
|
38
|
-
const
|
|
39
|
-
const { tidyInterProAnnotationTypes,
|
|
40
|
-
|
|
41
|
-
const
|
|
45
|
+
const FeatureTable = observer(function ({ model }: { model: MsaViewModel }) {
|
|
46
|
+
const { tidyInterProAnnotationTypes, tidyInterProAnnotations, fillPalette } =
|
|
47
|
+
model
|
|
48
|
+
const counts = new Map<string, number>()
|
|
49
|
+
for (const annot of tidyInterProAnnotations) {
|
|
50
|
+
counts.set(annot.accession, (counts.get(annot.accession) ?? 0) + 1)
|
|
51
|
+
}
|
|
42
52
|
return (
|
|
43
53
|
<>
|
|
44
54
|
<Toggles model={model} />
|
|
45
|
-
<
|
|
46
|
-
<
|
|
47
|
-
<
|
|
48
|
-
<
|
|
49
|
-
<
|
|
50
|
-
<
|
|
51
|
-
<
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
model.
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
55
|
+
<Table size="small">
|
|
56
|
+
<TableHead>
|
|
57
|
+
<TableRow>
|
|
58
|
+
<TableCell padding="checkbox" />
|
|
59
|
+
<TableCell>color</TableCell>
|
|
60
|
+
<TableCell>accession</TableCell>
|
|
61
|
+
<TableCell>name</TableCell>
|
|
62
|
+
<TableCell>count</TableCell>
|
|
63
|
+
<TableCell>description</TableCell>
|
|
64
|
+
</TableRow>
|
|
65
|
+
</TableHead>
|
|
66
|
+
<TableBody>
|
|
67
|
+
{[...tidyInterProAnnotationTypes.values()].map(
|
|
68
|
+
({ accession, name, description }) => (
|
|
69
|
+
<TableRow key={accession}>
|
|
70
|
+
<TableCell padding="checkbox">
|
|
71
|
+
<Checkbox
|
|
72
|
+
checked={model.featureFilters.get(accession) ?? false}
|
|
73
|
+
onChange={() => {
|
|
74
|
+
model.setFilter(
|
|
75
|
+
accession,
|
|
76
|
+
!model.featureFilters.get(accession),
|
|
77
|
+
)
|
|
78
|
+
}}
|
|
79
|
+
/>
|
|
80
|
+
</TableCell>
|
|
81
|
+
<TableCell>
|
|
82
|
+
<div
|
|
83
|
+
style={{
|
|
84
|
+
width: 20,
|
|
85
|
+
height: 20,
|
|
86
|
+
background: fillPalette[accession],
|
|
87
|
+
}}
|
|
88
|
+
/>
|
|
89
|
+
</TableCell>
|
|
90
|
+
<TableCell>{accession}</TableCell>
|
|
91
|
+
<TableCell>{name}</TableCell>
|
|
92
|
+
<TableCell>{counts.get(accession)}</TableCell>
|
|
93
|
+
<TableCell>{description}</TableCell>
|
|
94
|
+
</TableRow>
|
|
95
|
+
),
|
|
96
|
+
)}
|
|
97
|
+
</TableBody>
|
|
98
|
+
</Table>
|
|
85
99
|
</>
|
|
86
100
|
)
|
|
87
101
|
})
|
|
@@ -103,7 +117,7 @@ const FeatureTypeDialog = observer(function ({
|
|
|
103
117
|
maxWidth="xl"
|
|
104
118
|
>
|
|
105
119
|
<DialogContent>
|
|
106
|
-
<
|
|
120
|
+
<FeatureTable model={model} />
|
|
107
121
|
</DialogContent>
|
|
108
122
|
</Dialog>
|
|
109
123
|
)
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import React, { useState } from 'react'
|
|
2
2
|
|
|
3
3
|
import { Dialog } from '@jbrowse/core/ui'
|
|
4
|
+
import { makeStyles } from '@jbrowse/core/util/tss-react'
|
|
4
5
|
import { Button, DialogActions, DialogContent } from '@mui/material'
|
|
5
6
|
import { observer } from 'mobx-react'
|
|
6
|
-
import { makeStyles } from 'tss-react/mui'
|
|
7
7
|
|
|
8
8
|
import copy from '../../vendor/copyToClipboard.ts'
|
|
9
9
|
|
|
@@ -13,6 +13,7 @@ const ColorSchemeMenu = observer(function ({ model }: { model: MsaViewModel }) {
|
|
|
13
13
|
return (
|
|
14
14
|
<CascadingMenuButton
|
|
15
15
|
data-testid="color_scheme_menu"
|
|
16
|
+
tooltip="Color scheme"
|
|
16
17
|
closeAfterItemClick
|
|
17
18
|
anchorOrigin={{ vertical: 'bottom', horizontal: 'left' }}
|
|
18
19
|
transformOrigin={{ vertical: 'top', horizontal: 'left' }}
|
|
@@ -5,7 +5,6 @@ import Assignment from '@mui/icons-material/Assignment'
|
|
|
5
5
|
import FolderOpen from '@mui/icons-material/FolderOpen'
|
|
6
6
|
import Menu from '@mui/icons-material/Menu'
|
|
7
7
|
import PhotoCamera from '@mui/icons-material/PhotoCamera'
|
|
8
|
-
import Settings from '@mui/icons-material/Settings'
|
|
9
8
|
import { observer } from 'mobx-react'
|
|
10
9
|
|
|
11
10
|
import { getDomainMenu } from './getDomainsMenu.ts'
|
|
@@ -14,12 +13,12 @@ import type { MsaViewModel } from '../../model.ts'
|
|
|
14
13
|
|
|
15
14
|
const MetadataDialog = lazy(() => import('../dialogs/MetadataDialog.tsx'))
|
|
16
15
|
const ExportSVGDialog = lazy(() => import('../dialogs/ExportSVGDialog.tsx'))
|
|
17
|
-
const SettingsDialog = lazy(() => import('../dialogs/SettingsDialog.tsx'))
|
|
18
16
|
|
|
19
17
|
const FileMenu = observer(({ model }: { model: MsaViewModel }) => {
|
|
20
18
|
return (
|
|
21
19
|
<CascadingMenuButton
|
|
22
20
|
data-testid="file_menu"
|
|
21
|
+
tooltip="File"
|
|
23
22
|
menuItems={[
|
|
24
23
|
{
|
|
25
24
|
label: 'Return to import form',
|
|
@@ -41,19 +40,6 @@ const FileMenu = observer(({ model }: { model: MsaViewModel }) => {
|
|
|
41
40
|
])
|
|
42
41
|
},
|
|
43
42
|
},
|
|
44
|
-
{
|
|
45
|
-
label: 'More settings',
|
|
46
|
-
icon: Settings,
|
|
47
|
-
onClick: () => {
|
|
48
|
-
model.queueDialog(onClose => [
|
|
49
|
-
SettingsDialog,
|
|
50
|
-
{
|
|
51
|
-
model,
|
|
52
|
-
onClose,
|
|
53
|
-
},
|
|
54
|
-
])
|
|
55
|
-
},
|
|
56
|
-
},
|
|
57
43
|
{
|
|
58
44
|
label: 'Domain settings',
|
|
59
45
|
type: 'subMenu',
|
|
@@ -2,7 +2,7 @@ import React, { lazy, useEffect } from 'react'
|
|
|
2
2
|
|
|
3
3
|
import useMeasure from '@jbrowse/core/util/useMeasure'
|
|
4
4
|
import Help from '@mui/icons-material/Help'
|
|
5
|
-
import { IconButton } from '@mui/material'
|
|
5
|
+
import { IconButton, Tooltip } from '@mui/material'
|
|
6
6
|
import { observer } from 'mobx-react'
|
|
7
7
|
|
|
8
8
|
import ColorSchemeMenu from './ColorSchemeMenu.tsx'
|
|
@@ -40,13 +40,16 @@ const Header = observer(function ({ model }: { model: MsaViewModel }) {
|
|
|
40
40
|
<HeaderInfoArea model={model} />
|
|
41
41
|
<Spacer />
|
|
42
42
|
<HeaderStatusArea model={model} />
|
|
43
|
-
<
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
43
|
+
<Tooltip title="About">
|
|
44
|
+
<IconButton
|
|
45
|
+
aria-label="About"
|
|
46
|
+
onClick={() => {
|
|
47
|
+
model.queueDialog(onClose => [AboutDialog, { onClose }])
|
|
48
|
+
}}
|
|
49
|
+
>
|
|
50
|
+
<Help />
|
|
51
|
+
</IconButton>
|
|
52
|
+
</Tooltip>
|
|
50
53
|
</div>
|
|
51
54
|
)
|
|
52
55
|
})
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
2
|
|
|
3
|
+
import { makeStyles } from '@jbrowse/core/util/tss-react'
|
|
3
4
|
import { Typography } from '@mui/material'
|
|
4
5
|
import { observer } from 'mobx-react'
|
|
5
|
-
import { makeStyles } from 'tss-react/mui'
|
|
6
6
|
|
|
7
7
|
import type { MsaViewModel } from '../../model.ts'
|
|
8
8
|
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
2
|
|
|
3
3
|
import { LoadingEllipses } from '@jbrowse/core/ui'
|
|
4
|
+
import { makeStyles } from '@jbrowse/core/util/tss-react'
|
|
4
5
|
import { Button, Typography } from '@mui/material'
|
|
5
6
|
import { observer } from 'mobx-react'
|
|
6
|
-
import { makeStyles } from 'tss-react/mui'
|
|
7
7
|
|
|
8
8
|
import type { MsaViewModel } from '../../model.ts'
|
|
9
9
|
|
|
@@ -12,6 +12,7 @@ const MSASettingsMenu = observer(function ({ model }: { model: MsaViewModel }) {
|
|
|
12
12
|
return (
|
|
13
13
|
<CascadingMenuButton
|
|
14
14
|
data-testid="msa_settings_menu"
|
|
15
|
+
tooltip="Alignment settings"
|
|
15
16
|
closeAfterItemClick={false}
|
|
16
17
|
anchorOrigin={{ vertical: 'bottom', horizontal: 'left' }}
|
|
17
18
|
transformOrigin={{ vertical: 'top', horizontal: 'left' }}
|
|
Binary file
|
|
@@ -17,6 +17,7 @@ const TreeSettingsMenu = observer(function ({
|
|
|
17
17
|
return (
|
|
18
18
|
<CascadingMenuButton
|
|
19
19
|
data-testid="tree_settings_menu"
|
|
20
|
+
tooltip="Tree settings"
|
|
20
21
|
closeAfterItemClick={false}
|
|
21
22
|
anchorOrigin={{ vertical: 'bottom', horizontal: 'left' }}
|
|
22
23
|
transformOrigin={{ vertical: 'top', horizontal: 'left' }}
|