react-msaview 5.1.0 → 5.2.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 +35 -135
- package/bundle/index.js.map +1 -1
- package/dist/calculateBlocks.d.ts +4 -10
- package/dist/calculateBlocks.js +12 -20
- package/dist/calculateBlocks.js.map +1 -1
- package/dist/components/ConservationTrack.js +18 -24
- package/dist/components/ConservationTrack.js.map +1 -1
- package/dist/components/TextTrack.js +24 -34
- package/dist/components/TextTrack.js.map +1 -1
- package/dist/components/dialogs/ExportSVGDialog.js +20 -20
- package/dist/components/dialogs/ExportSVGDialog.js.map +1 -1
- package/dist/components/dialogs/InterProScanDialog.js +6 -1
- package/dist/components/dialogs/InterProScanDialog.js.map +1 -1
- package/dist/components/header/FileMenu.js +2 -1
- package/dist/components/header/FileMenu.js.map +1 -1
- package/dist/components/header/Header.js +1 -1
- package/dist/components/header/settingsMenuItems.js +24 -8
- package/dist/components/header/settingsMenuItems.js.map +1 -1
- package/dist/components/minimap/Minimap.js +1 -6
- package/dist/components/minimap/Minimap.js.map +1 -1
- package/dist/components/minimap/MinimapSVG.js +4 -8
- package/dist/components/minimap/MinimapSVG.js.map +1 -1
- package/dist/components/msa/getVisibleLeaves.d.ts +6 -0
- package/dist/components/msa/getVisibleLeaves.js +9 -0
- package/dist/components/msa/getVisibleLeaves.js.map +1 -0
- package/dist/components/msa/renderBoxFeatureCanvasBlock.d.ts +2 -1
- package/dist/components/msa/renderBoxFeatureCanvasBlock.js +5 -6
- package/dist/components/msa/renderBoxFeatureCanvasBlock.js.map +1 -1
- package/dist/components/msa/renderMSABlock.d.ts +2 -1
- package/dist/components/msa/renderMSABlock.js +98 -84
- package/dist/components/msa/renderMSABlock.js.map +1 -1
- package/dist/components/msa/renderMSAMouseover.js +3 -3
- package/dist/components/msa/renderMSAMouseover.js.map +1 -1
- package/dist/components/msa/visibleColRange.d.ts +8 -0
- package/dist/components/msa/visibleColRange.js +10 -0
- package/dist/components/msa/visibleColRange.js.map +1 -0
- package/dist/components/renderCtx.d.ts +7 -0
- package/dist/components/renderCtx.js +2 -0
- package/dist/components/renderCtx.js.map +1 -0
- package/dist/components/tracks/renderTracksSvg.d.ts +6 -5
- package/dist/components/tracks/renderTracksSvg.js +11 -4
- package/dist/components/tracks/renderTracksSvg.js.map +1 -1
- package/dist/components/tree/TreeCanvas.js.map +1 -1
- package/dist/components/tree/TreeCanvasBlock.js +2 -10
- package/dist/components/tree/TreeCanvasBlock.js.map +1 -1
- package/dist/components/tree/renderTreeCanvas.d.ts +5 -4
- package/dist/components/tree/renderTreeCanvas.js +16 -24
- package/dist/components/tree/renderTreeCanvas.js.map +1 -1
- package/dist/components/util.d.ts +0 -14
- package/dist/components/util.js +0 -58
- package/dist/components/util.js.map +1 -1
- package/dist/hierarchy.d.ts +2 -0
- package/dist/hierarchy.js +19 -0
- package/dist/hierarchy.js.map +1 -1
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -1
- package/dist/model.d.ts +26 -30
- package/dist/model.js +74 -82
- package/dist/model.js.map +1 -1
- package/dist/renderToSvg.d.ts +1 -1
- package/dist/renderToSvg.js +20 -26
- package/dist/renderToSvg.js.map +1 -1
- package/dist/rowCoordinateCalculations.d.ts +0 -9
- package/dist/rowCoordinateCalculations.js +0 -20
- package/dist/rowCoordinateCalculations.js.map +1 -1
- package/dist/util.d.ts +1 -0
- package/dist/util.js +23 -1
- package/dist/util.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +5 -5
- package/src/calculateBlocks.ts +18 -51
- package/src/components/ConservationTrack.tsx +18 -27
- package/src/components/TextTrack.tsx +30 -38
- package/src/components/dialogs/ExportSVGDialog.tsx +27 -17
- package/src/components/dialogs/InterProScanDialog.tsx +6 -1
- package/src/components/header/FileMenu.tsx +2 -1
- package/src/components/header/Header.tsx +1 -1
- package/src/components/header/settingsMenuItems.ts +24 -8
- package/src/components/minimap/Minimap.tsx +1 -6
- package/src/components/minimap/MinimapSVG.tsx +4 -8
- package/src/components/msa/getVisibleLeaves.ts +21 -0
- package/src/components/msa/renderBoxFeatureCanvasBlock.ts +9 -9
- package/src/components/msa/renderMSABlock.ts +119 -118
- package/src/components/msa/renderMSAMouseover.ts +2 -3
- package/src/components/msa/visibleColRange.ts +17 -0
- package/src/components/renderCtx.ts +29 -0
- package/src/components/tracks/renderTracksSvg.ts +18 -9
- package/src/components/tree/TreeCanvas.tsx +3 -1
- package/src/components/tree/TreeCanvasBlock.tsx +2 -12
- package/src/components/tree/renderTreeCanvas.ts +25 -28
- package/src/components/util.ts +0 -81
- package/src/hierarchy.ts +27 -0
- package/src/index.ts +7 -0
- package/src/model.ts +81 -98
- package/src/renderToSvg.tsx +23 -31
- package/src/rowCoordinateCalculations.test.ts +0 -51
- package/src/rowCoordinateCalculations.ts +0 -23
- package/src/util.ts +25 -1
- package/src/version.ts +1 -1
|
@@ -33,15 +33,6 @@ export declare function visibleColToGlobalCol(blanks: number[], visibleCol: numb
|
|
|
33
33
|
* @returns The visible column index, or undefined if the column is hidden
|
|
34
34
|
*/
|
|
35
35
|
export declare function globalColToVisibleCol(blanks: number[], globalCol: number): number | undefined;
|
|
36
|
-
/**
|
|
37
|
-
* Convert a global column index to a row-specific sequence position.
|
|
38
|
-
* This counts non-gap characters up to the given global column.
|
|
39
|
-
*
|
|
40
|
-
* @param seq - The row's sequence string (including gaps)
|
|
41
|
-
* @param globalCol - The global column index
|
|
42
|
-
* @returns The sequence position (count of non-gap characters before this column)
|
|
43
|
-
*/
|
|
44
|
-
export declare function globalColToSeqPos(seq: string, globalCol: number): number;
|
|
45
36
|
/**
|
|
46
37
|
* Convert a visible column to a row-specific sequence position.
|
|
47
38
|
* Returns undefined if the position is a gap in the sequence.
|
|
@@ -75,26 +75,6 @@ export function globalColToVisibleCol(blanks, globalCol) {
|
|
|
75
75
|
const blanksBefore = left;
|
|
76
76
|
return globalCol - blanksBefore;
|
|
77
77
|
}
|
|
78
|
-
/**
|
|
79
|
-
* Convert a global column index to a row-specific sequence position.
|
|
80
|
-
* This counts non-gap characters up to the given global column.
|
|
81
|
-
*
|
|
82
|
-
* @param seq - The row's sequence string (including gaps)
|
|
83
|
-
* @param globalCol - The global column index
|
|
84
|
-
* @returns The sequence position (count of non-gap characters before this column)
|
|
85
|
-
*/
|
|
86
|
-
export function globalColToSeqPos(seq, globalCol) {
|
|
87
|
-
let seqPos = 0;
|
|
88
|
-
let currentCol = 0;
|
|
89
|
-
const seqLen = seq.length;
|
|
90
|
-
while (currentCol < globalCol && currentCol < seqLen) {
|
|
91
|
-
if (!isBlank(seq[currentCol])) {
|
|
92
|
-
seqPos++;
|
|
93
|
-
}
|
|
94
|
-
currentCol++;
|
|
95
|
-
}
|
|
96
|
-
return seqPos;
|
|
97
|
-
}
|
|
98
78
|
/**
|
|
99
79
|
* Convert a visible column to a row-specific sequence position.
|
|
100
80
|
* Returns undefined if the position is a gap in the sequence.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rowCoordinateCalculations.js","sourceRoot":"","sources":["../src/rowCoordinateCalculations.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAEnC;;;;;;;;;;;;;;;;GAgBG;AAEH;;;;;;;GAOG;AACH,MAAM,UAAU,qBAAqB,CAAC,MAAgB,EAAE,UAAkB;IACxE,IAAI,iBAAiB,GAAG,CAAC,CAAA;IACzB,IAAI,eAAe,GAAG,CAAC,CAAA;IACvB,IAAI,SAAS,GAAG,CAAC,CAAA;IACjB,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,CAAA;IAE/B,gEAAgE;IAChE,OAAO,eAAe,GAAG,SAAS,IAAI,MAAM,CAAC,eAAe,CAAC,KAAK,SAAS,EAAE,CAAC;QAC5E,eAAe,EAAE,CAAA;QACjB,SAAS,EAAE,CAAA;IACb,CAAC;IAED,OAAO,iBAAiB,GAAG,UAAU,EAAE,CAAC;QACtC,iBAAiB,EAAE,CAAA;QACnB,SAAS,EAAE,CAAA;QAEX,4CAA4C;QAC5C,OACE,eAAe,GAAG,SAAS;YAC3B,MAAM,CAAC,eAAe,CAAC,KAAK,SAAS,EACrC,CAAC;YACD,eAAe,EAAE,CAAA;YACjB,SAAS,EAAE,CAAA;QACb,CAAC;IACH,CAAC;IAED,OAAO,SAAS,CAAA;AAClB,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,qBAAqB,CACnC,MAAgB,EAChB,SAAiB;IAEjB,iCAAiC;IACjC,2CAA2C;IAC3C,IAAI,IAAI,GAAG,CAAC,CAAA;IACZ,IAAI,KAAK,GAAG,MAAM,CAAC,MAAM,GAAG,CAAC,CAAA;IAC7B,OAAO,IAAI,IAAI,KAAK,EAAE,CAAC;QACrB,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAA;QAC1C,IAAI,MAAM,CAAC,GAAG,CAAC,KAAK,SAAS,EAAE,CAAC;YAC9B,OAAO,SAAS,CAAA,CAAC,mBAAmB;QACtC,CAAC;QACD,IAAI,MAAM,CAAC,GAAG,CAAE,GAAG,SAAS,EAAE,CAAC;YAC7B,IAAI,GAAG,GAAG,GAAG,CAAC,CAAA;QAChB,CAAC;aAAM,CAAC;YACN,KAAK,GAAG,GAAG,GAAG,CAAC,CAAA;QACjB,CAAC;IACH,CAAC;IAED,oEAAoE;IACpE,MAAM,YAAY,GAAG,IAAI,CAAA;IACzB,OAAO,SAAS,GAAG,YAAY,CAAA;AACjC,CAAC;AAED
|
|
1
|
+
{"version":3,"file":"rowCoordinateCalculations.js","sourceRoot":"","sources":["../src/rowCoordinateCalculations.ts"],"names":[],"mappings":"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"}
|
package/dist/util.d.ts
CHANGED
package/dist/util.js
CHANGED
|
@@ -1,6 +1,28 @@
|
|
|
1
1
|
import { colord, extend } from 'colord';
|
|
2
|
+
import a11yPlugin from 'colord/plugins/a11y';
|
|
2
3
|
import namesPlugin from 'colord/plugins/names';
|
|
3
|
-
extend([namesPlugin]);
|
|
4
|
+
extend([namesPlugin, a11yPlugin]);
|
|
5
|
+
const contrastAdjustCache = new Map();
|
|
6
|
+
// Adjusts a background-tile color until it meets WCAG AA (4.5:1) contrast
|
|
7
|
+
// against `bg`, so it can be used as letter text in letter-color mode.
|
|
8
|
+
// Darkens on light backgrounds, lightens on dark backgrounds.
|
|
9
|
+
export function adjustColorForContrast(color, bg) {
|
|
10
|
+
const cacheKey = `${color}|${bg}`;
|
|
11
|
+
const cached = contrastAdjustCache.get(cacheKey);
|
|
12
|
+
if (cached !== undefined) {
|
|
13
|
+
return cached;
|
|
14
|
+
}
|
|
15
|
+
let c = colord(color);
|
|
16
|
+
if (!c.isReadable(bg)) {
|
|
17
|
+
const bgIsDark = colord(bg).luminance() < 0.5;
|
|
18
|
+
for (let i = 0; i < 40 && !c.isReadable(bg); i++) {
|
|
19
|
+
c = bgIsDark ? c.lighten(0.05) : c.darken(0.05);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
const result = c.toHex();
|
|
23
|
+
contrastAdjustCache.set(cacheKey, result);
|
|
24
|
+
return result;
|
|
25
|
+
}
|
|
4
26
|
export function transform(obj, cb) {
|
|
5
27
|
return Object.fromEntries(Object.entries(obj).map(cb));
|
|
6
28
|
}
|
package/dist/util.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"util.js","sourceRoot":"","sources":["../src/util.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AACvC,OAAO,WAAW,MAAM,sBAAsB,CAAA;AAI9C,MAAM,CAAC,CAAC,WAAW,CAAC,CAAC,CAAA;
|
|
1
|
+
{"version":3,"file":"util.js","sourceRoot":"","sources":["../src/util.ts"],"names":[],"mappings":"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"}
|
package/dist/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const version = "5.
|
|
1
|
+
export declare const version = "5.2.0";
|
package/dist/version.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export const version = '5.
|
|
1
|
+
export const version = '5.2.0';
|
|
2
2
|
//# sourceMappingURL=version.js.map
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-msaview",
|
|
3
3
|
"author": "Colin",
|
|
4
|
-
"version": "5.
|
|
4
|
+
"version": "5.2.0",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"repository": {
|
|
@@ -38,12 +38,12 @@
|
|
|
38
38
|
"@types/node": "^25.9.1"
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"@mui/icons-material": "^
|
|
42
|
-
"@mui/material": "^
|
|
41
|
+
"@mui/icons-material": "^7.3.11",
|
|
42
|
+
"@mui/material": "^7.3.11",
|
|
43
43
|
"colord": "^2.9.3",
|
|
44
44
|
"flatbush": "^4.5.1",
|
|
45
|
-
"
|
|
46
|
-
"
|
|
45
|
+
"msa-parsers": "5.2.0",
|
|
46
|
+
"@jbrowse/svgcanvas": "5.2.0"
|
|
47
47
|
},
|
|
48
48
|
"scripts": {
|
|
49
49
|
"clean": "rimraf dist bundle",
|
package/src/calculateBlocks.ts
CHANGED
|
@@ -1,58 +1,25 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
// Returns the block offsets (in pixels) needed to cover the viewport along one
|
|
2
|
+
// axis. mapSize/viewportPos/viewportSize are all in pixels. The clamp pins the
|
|
3
|
+
// block set to the content edge so an over-scroll past the end still fills the
|
|
4
|
+
// viewport rather than leaving a gap.
|
|
5
|
+
export function calculateBlocks({
|
|
6
|
+
mapSize,
|
|
3
7
|
blockSize,
|
|
4
|
-
|
|
5
|
-
|
|
8
|
+
viewportPos,
|
|
9
|
+
viewportSize,
|
|
6
10
|
}: {
|
|
7
|
-
|
|
11
|
+
mapSize: number
|
|
8
12
|
blockSize: number
|
|
9
|
-
|
|
10
|
-
|
|
13
|
+
viewportPos: number
|
|
14
|
+
viewportSize: number
|
|
11
15
|
}) {
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
)
|
|
16
|
+
const clamped = Math.max(0, Math.min(viewportPos, mapSize - viewportSize))
|
|
17
|
+
const minTile = Math.floor(clamped / blockSize)
|
|
18
|
+
const maxTile = Math.floor((clamped + viewportSize - 1) / blockSize)
|
|
16
19
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
(clampedViewportY + viewportHeight - 1) / blockSize,
|
|
21
|
-
)
|
|
22
|
-
|
|
23
|
-
// Generate list of visible tiles
|
|
24
|
-
const blocksY = []
|
|
25
|
-
for (let tileY = minTileY; tileY <= maxTileY; tileY++) {
|
|
26
|
-
blocksY.push(tileY * blockSize)
|
|
20
|
+
const blocks = []
|
|
21
|
+
for (let tile = minTile; tile <= maxTile; tile++) {
|
|
22
|
+
blocks.push(tile * blockSize)
|
|
27
23
|
}
|
|
28
|
-
|
|
29
|
-
return blocksY
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export function blocksX({
|
|
33
|
-
mapWidth,
|
|
34
|
-
blockSize,
|
|
35
|
-
viewportX,
|
|
36
|
-
viewportWidth,
|
|
37
|
-
}: {
|
|
38
|
-
mapWidth: number
|
|
39
|
-
blockSize: number
|
|
40
|
-
viewportX: number
|
|
41
|
-
viewportWidth: number
|
|
42
|
-
}) {
|
|
43
|
-
const clampedViewportX = Math.max(
|
|
44
|
-
0,
|
|
45
|
-
Math.min(viewportX, mapWidth - viewportWidth),
|
|
46
|
-
)
|
|
47
|
-
|
|
48
|
-
const minTileX = Math.floor(clampedViewportX / blockSize)
|
|
49
|
-
const maxTileX = Math.floor(
|
|
50
|
-
(clampedViewportX + viewportWidth - 1) / blockSize,
|
|
51
|
-
)
|
|
52
|
-
const blocksX = []
|
|
53
|
-
for (let tileX = minTileX; tileX <= maxTileX; tileX++) {
|
|
54
|
-
blocksX.push(tileX * blockSize)
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
return blocksX
|
|
24
|
+
return blocks
|
|
58
25
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import React, { useEffect, useRef } from 'react'
|
|
2
2
|
|
|
3
3
|
import { ResizeHandle } from '@jbrowse/core/ui'
|
|
4
|
+
import { autorun } from 'mobx'
|
|
4
5
|
import { observer } from 'mobx-react'
|
|
5
6
|
|
|
6
7
|
import { drawConservationBars } from './tracks/renderTracksSvg.ts'
|
|
@@ -17,42 +18,32 @@ const ConservationBlock = observer(function ({
|
|
|
17
18
|
offsetX: number
|
|
18
19
|
trackHeight: number
|
|
19
20
|
}) {
|
|
20
|
-
const { blockSize, scrollX,
|
|
21
|
-
model
|
|
21
|
+
const { blockSize, scrollX, highResScaleFactor } = model
|
|
22
22
|
|
|
23
23
|
const ref = useRef<HTMLCanvasElement>(null)
|
|
24
24
|
|
|
25
25
|
useEffect(() => {
|
|
26
|
-
|
|
27
|
-
return
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const ctx = ref.current.getContext('2d')
|
|
26
|
+
const ctx = ref.current?.getContext('2d')
|
|
31
27
|
if (!ctx) {
|
|
32
28
|
return
|
|
33
29
|
}
|
|
30
|
+
return autorun(() => {
|
|
31
|
+
const { blockSize, colWidth, highResScaleFactor, conservation } = model
|
|
32
|
+
ctx.resetTransform()
|
|
33
|
+
ctx.scale(highResScaleFactor, highResScaleFactor)
|
|
34
|
+
ctx.clearRect(0, 0, blockSize, trackHeight)
|
|
35
|
+
ctx.translate(-offsetX, 0)
|
|
34
36
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
colWidth,
|
|
44
|
-
trackHeight,
|
|
45
|
-
offsetX,
|
|
46
|
-
blockSize,
|
|
37
|
+
drawConservationBars({
|
|
38
|
+
ctx,
|
|
39
|
+
conservation,
|
|
40
|
+
colWidth,
|
|
41
|
+
trackHeight,
|
|
42
|
+
offsetX,
|
|
43
|
+
blockSize,
|
|
44
|
+
})
|
|
47
45
|
})
|
|
48
|
-
}, [
|
|
49
|
-
blockSize,
|
|
50
|
-
colWidth,
|
|
51
|
-
trackHeight,
|
|
52
|
-
offsetX,
|
|
53
|
-
highResScaleFactor,
|
|
54
|
-
conservation,
|
|
55
|
-
])
|
|
46
|
+
}, [model, offsetX, trackHeight])
|
|
56
47
|
|
|
57
48
|
return (
|
|
58
49
|
<canvas
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import React, { useEffect, useRef } from 'react'
|
|
2
2
|
|
|
3
|
+
import { autorun } from 'mobx'
|
|
3
4
|
import { observer } from 'mobx-react'
|
|
4
5
|
|
|
5
6
|
import { useColorContrast } from '../useColorContrast.ts'
|
|
@@ -20,10 +21,7 @@ const AnnotationBlock = observer(function ({
|
|
|
20
21
|
const {
|
|
21
22
|
blockSize,
|
|
22
23
|
scrollX,
|
|
23
|
-
bgColor,
|
|
24
24
|
colorScheme: modelColorScheme,
|
|
25
|
-
colWidth,
|
|
26
|
-
fontSize,
|
|
27
25
|
rowHeight,
|
|
28
26
|
highResScaleFactor,
|
|
29
27
|
} = model
|
|
@@ -31,49 +29,43 @@ const AnnotationBlock = observer(function ({
|
|
|
31
29
|
model: { customColorScheme, data },
|
|
32
30
|
} = track
|
|
33
31
|
|
|
34
|
-
const colorScheme = customColorScheme
|
|
32
|
+
const colorScheme = customColorScheme ?? modelColorScheme
|
|
35
33
|
const ref = useRef<HTMLCanvasElement>(null)
|
|
36
34
|
const { contrastScheme } = useColorContrast(colorScheme)
|
|
37
35
|
useEffect(() => {
|
|
38
|
-
|
|
39
|
-
return
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
const ctx = ref.current.getContext('2d')
|
|
36
|
+
const ctx = ref.current?.getContext('2d')
|
|
43
37
|
if (!ctx) {
|
|
44
38
|
return
|
|
45
39
|
}
|
|
40
|
+
return autorun(() => {
|
|
41
|
+
const {
|
|
42
|
+
blockSize,
|
|
43
|
+
bgColor,
|
|
44
|
+
colWidth,
|
|
45
|
+
fontSize,
|
|
46
|
+
rowHeight,
|
|
47
|
+
highResScaleFactor,
|
|
48
|
+
} = model
|
|
49
|
+
ctx.resetTransform()
|
|
50
|
+
ctx.scale(highResScaleFactor, highResScaleFactor)
|
|
51
|
+
ctx.clearRect(0, 0, blockSize, rowHeight)
|
|
52
|
+
ctx.translate(-offsetX, 0)
|
|
53
|
+
ctx.textAlign = 'center'
|
|
54
|
+
ctx.font = ctx.font.replace(/\d+px/, `${fontSize}px`)
|
|
46
55
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
contrastScheme,
|
|
59
|
-
bgColor,
|
|
60
|
-
colWidth,
|
|
61
|
-
rowHeight,
|
|
62
|
-
offsetX,
|
|
63
|
-
blockSize,
|
|
56
|
+
drawTextTrackContent({
|
|
57
|
+
ctx,
|
|
58
|
+
data,
|
|
59
|
+
colorScheme,
|
|
60
|
+
contrastScheme,
|
|
61
|
+
bgColor,
|
|
62
|
+
colWidth,
|
|
63
|
+
rowHeight,
|
|
64
|
+
offsetX,
|
|
65
|
+
blockSize,
|
|
66
|
+
})
|
|
64
67
|
})
|
|
65
|
-
}, [
|
|
66
|
-
fontSize,
|
|
67
|
-
bgColor,
|
|
68
|
-
blockSize,
|
|
69
|
-
colWidth,
|
|
70
|
-
rowHeight,
|
|
71
|
-
offsetX,
|
|
72
|
-
contrastScheme,
|
|
73
|
-
colorScheme,
|
|
74
|
-
highResScaleFactor,
|
|
75
|
-
data,
|
|
76
|
-
])
|
|
68
|
+
}, [model, offsetX, contrastScheme, colorScheme, data])
|
|
77
69
|
return (
|
|
78
70
|
<canvas
|
|
79
71
|
ref={ref}
|
|
@@ -28,13 +28,21 @@ export default function ExportSVGDialog({
|
|
|
28
28
|
}) {
|
|
29
29
|
const [includeMinimap, setIncludeMinimap] = useState(true)
|
|
30
30
|
const [includeTracks, setIncludeTracks] = useState(true)
|
|
31
|
-
const [exportType, setExportType] = useState
|
|
31
|
+
const [exportType, setExportType] = useState<'entire' | 'viewport'>(
|
|
32
|
+
'viewport',
|
|
33
|
+
)
|
|
32
34
|
const [error, setError] = useState<unknown>()
|
|
33
35
|
const theme = useTheme()
|
|
34
|
-
const {
|
|
36
|
+
const {
|
|
37
|
+
totalWidth,
|
|
38
|
+
totalHeight,
|
|
39
|
+
treeAreaWidth,
|
|
40
|
+
turnedOnTracks,
|
|
41
|
+
totalTrackAreaHeight,
|
|
42
|
+
} = model
|
|
35
43
|
const hasTracks = turnedOnTracks.length > 0
|
|
36
44
|
const entireWidth = totalWidth + treeAreaWidth
|
|
37
|
-
const entireHeight = totalHeight
|
|
45
|
+
const entireHeight = totalHeight + (includeTracks ? totalTrackAreaHeight : 0)
|
|
38
46
|
const isLargeExport =
|
|
39
47
|
exportType === 'entire' && (entireWidth > 10000 || entireHeight > 10000)
|
|
40
48
|
return (
|
|
@@ -71,7 +79,10 @@ export default function ExportSVGDialog({
|
|
|
71
79
|
<RadioGroup
|
|
72
80
|
value={exportType}
|
|
73
81
|
onChange={event => {
|
|
74
|
-
|
|
82
|
+
const { value } = event.target
|
|
83
|
+
if (value === 'entire' || value === 'viewport') {
|
|
84
|
+
setExportType(value)
|
|
85
|
+
}
|
|
75
86
|
}}
|
|
76
87
|
>
|
|
77
88
|
<FormControlLabel
|
|
@@ -99,22 +110,21 @@ export default function ExportSVGDialog({
|
|
|
99
110
|
variant="contained"
|
|
100
111
|
color="primary"
|
|
101
112
|
onClick={() => {
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
})
|
|
113
|
+
void model
|
|
114
|
+
.exportSVG({
|
|
115
|
+
theme,
|
|
116
|
+
includeMinimap:
|
|
117
|
+
exportType === 'entire' ? false : includeMinimap,
|
|
118
|
+
includeTracks: hasTracks && includeTracks,
|
|
119
|
+
exportType,
|
|
120
|
+
})
|
|
121
|
+
.then(() => {
|
|
112
122
|
onClose()
|
|
113
|
-
}
|
|
123
|
+
})
|
|
124
|
+
.catch((e: unknown) => {
|
|
114
125
|
console.error(e)
|
|
115
126
|
setError(e)
|
|
116
|
-
}
|
|
117
|
-
})()
|
|
127
|
+
})
|
|
118
128
|
}}
|
|
119
129
|
>
|
|
120
130
|
Submit
|
|
@@ -258,7 +258,12 @@ const InterProScanDialog = observer(function ({
|
|
|
258
258
|
onProgress: arg => {
|
|
259
259
|
model.setStatus(
|
|
260
260
|
arg
|
|
261
|
-
? {
|
|
261
|
+
? {
|
|
262
|
+
...arg,
|
|
263
|
+
onCancel: () => {
|
|
264
|
+
controller.abort()
|
|
265
|
+
},
|
|
266
|
+
}
|
|
262
267
|
: undefined,
|
|
263
268
|
)
|
|
264
269
|
},
|
|
@@ -3,6 +3,7 @@ import React, { lazy } from 'react'
|
|
|
3
3
|
import CascadingMenuButton from '@jbrowse/core/ui/CascadingMenuButton'
|
|
4
4
|
import Assignment from '@mui/icons-material/Assignment'
|
|
5
5
|
import FolderOpen from '@mui/icons-material/FolderOpen'
|
|
6
|
+
import Menu from '@mui/icons-material/Menu'
|
|
6
7
|
import PhotoCamera from '@mui/icons-material/PhotoCamera'
|
|
7
8
|
import Settings from '@mui/icons-material/Settings'
|
|
8
9
|
import { observer } from 'mobx-react'
|
|
@@ -76,7 +77,7 @@ const FileMenu = observer(({ model }: { model: MsaViewModel }) => {
|
|
|
76
77
|
anchorOrigin={{ vertical: 'bottom', horizontal: 'left' }}
|
|
77
78
|
transformOrigin={{ vertical: 'top', horizontal: 'left' }}
|
|
78
79
|
>
|
|
79
|
-
<
|
|
80
|
+
<Menu />
|
|
80
81
|
</CascadingMenuButton>
|
|
81
82
|
)
|
|
82
83
|
})
|
|
@@ -23,7 +23,7 @@ const AboutDialog = lazy(() => import('../dialogs/AboutDialog.tsx'))
|
|
|
23
23
|
const Header = observer(function ({ model }: { model: MsaViewModel }) {
|
|
24
24
|
const [ref, { height }] = useMeasure()
|
|
25
25
|
useEffect(() => {
|
|
26
|
-
model.setHeaderHeight(height
|
|
26
|
+
model.setHeaderHeight(height ?? 0)
|
|
27
27
|
}, [model, height])
|
|
28
28
|
return (
|
|
29
29
|
<div ref={ref} style={{ display: 'flex' }}>
|
|
@@ -8,19 +8,25 @@ export function msaSettingsMenuItems(model: MsaViewModel): MenuItem[] {
|
|
|
8
8
|
label: 'Draw letters',
|
|
9
9
|
type: 'checkbox',
|
|
10
10
|
checked: drawMsaLetters,
|
|
11
|
-
onClick: () => {
|
|
11
|
+
onClick: () => {
|
|
12
|
+
model.setDrawMsaLetters(!drawMsaLetters)
|
|
13
|
+
},
|
|
12
14
|
},
|
|
13
15
|
{
|
|
14
16
|
label: 'Color letters instead of background of tiles',
|
|
15
17
|
type: 'checkbox',
|
|
16
18
|
checked: !bgColor,
|
|
17
|
-
onClick: () => {
|
|
19
|
+
onClick: () => {
|
|
20
|
+
model.setBgColor(!bgColor)
|
|
21
|
+
},
|
|
18
22
|
},
|
|
19
23
|
{
|
|
20
24
|
label: 'Enable hiding gappy columns?',
|
|
21
25
|
type: 'checkbox',
|
|
22
26
|
checked: hideGaps,
|
|
23
|
-
onClick: () => {
|
|
27
|
+
onClick: () => {
|
|
28
|
+
model.setHideGaps(!hideGaps)
|
|
29
|
+
},
|
|
24
30
|
},
|
|
25
31
|
]
|
|
26
32
|
}
|
|
@@ -38,31 +44,41 @@ export function treeSettingsMenuItems(model: MsaViewModel): MenuItem[] {
|
|
|
38
44
|
label: 'Show branch length',
|
|
39
45
|
type: 'checkbox',
|
|
40
46
|
checked: showBranchLen,
|
|
41
|
-
onClick: () => {
|
|
47
|
+
onClick: () => {
|
|
48
|
+
model.setShowBranchLen(!showBranchLen)
|
|
49
|
+
},
|
|
42
50
|
},
|
|
43
51
|
{
|
|
44
52
|
label: 'Show tree',
|
|
45
53
|
type: 'checkbox',
|
|
46
54
|
checked: drawTree,
|
|
47
|
-
onClick: () => {
|
|
55
|
+
onClick: () => {
|
|
56
|
+
model.setDrawTree(!drawTree)
|
|
57
|
+
},
|
|
48
58
|
},
|
|
49
59
|
{
|
|
50
60
|
label: 'Draw clickable bubbles on tree branches',
|
|
51
61
|
type: 'checkbox',
|
|
52
62
|
checked: drawNodeBubbles,
|
|
53
|
-
onClick: () => {
|
|
63
|
+
onClick: () => {
|
|
64
|
+
model.setDrawNodeBubbles(!drawNodeBubbles)
|
|
65
|
+
},
|
|
54
66
|
},
|
|
55
67
|
{
|
|
56
68
|
label: 'Tree labels align right',
|
|
57
69
|
type: 'checkbox',
|
|
58
70
|
checked: labelsAlignRight,
|
|
59
|
-
onClick: () => {
|
|
71
|
+
onClick: () => {
|
|
72
|
+
model.setLabelsAlignRight(!labelsAlignRight)
|
|
73
|
+
},
|
|
60
74
|
},
|
|
61
75
|
{
|
|
62
76
|
label: 'Draw labels',
|
|
63
77
|
type: 'checkbox',
|
|
64
78
|
checked: drawLabels,
|
|
65
|
-
onClick: () => {
|
|
79
|
+
onClick: () => {
|
|
80
|
+
model.setDrawLabels(!drawLabels)
|
|
81
|
+
},
|
|
66
82
|
},
|
|
67
83
|
]
|
|
68
84
|
}
|
|
@@ -69,12 +69,7 @@ const Minimap = observer(function ({ model }: { model: MsaViewModel }) {
|
|
|
69
69
|
<svg height={polygonHeight} style={{ width: '100%' }}>
|
|
70
70
|
<polygon
|
|
71
71
|
fill={fill}
|
|
72
|
-
points={
|
|
73
|
-
[s + w, 0],
|
|
74
|
-
[s, 0],
|
|
75
|
-
[0, polygonHeight],
|
|
76
|
-
[msaAreaWidth, polygonHeight],
|
|
77
|
-
].toString()}
|
|
72
|
+
points={`${s + w},0 ${s},0 0,${polygonHeight} ${msaAreaWidth},${polygonHeight}`}
|
|
78
73
|
/>
|
|
79
74
|
</svg>
|
|
80
75
|
</div>
|
|
@@ -14,13 +14,14 @@ const MinimapSVG = observer(({ model }: { model: MsaViewModel }) => {
|
|
|
14
14
|
} = model
|
|
15
15
|
|
|
16
16
|
const BAR_HEIGHT = 12
|
|
17
|
-
const H2 = H -
|
|
17
|
+
const H2 = H - BAR_HEIGHT
|
|
18
18
|
|
|
19
19
|
const unit = W / numColumns / colWidth
|
|
20
20
|
const left = -scrollX
|
|
21
21
|
const right = left + W
|
|
22
22
|
const s = left * unit
|
|
23
23
|
const e = right * unit
|
|
24
|
+
const w = Math.max(e - s, 20)
|
|
24
25
|
const fillColor = 'rgb(66, 119, 127)'
|
|
25
26
|
const fillOpacity = 0.3
|
|
26
27
|
|
|
@@ -37,7 +38,7 @@ const MinimapSVG = observer(({ model }: { model: MsaViewModel }) => {
|
|
|
37
38
|
<rect
|
|
38
39
|
x={Math.max(0, s)}
|
|
39
40
|
y={0}
|
|
40
|
-
width={
|
|
41
|
+
width={w}
|
|
41
42
|
height={BAR_HEIGHT}
|
|
42
43
|
fill={fillColor}
|
|
43
44
|
fillOpacity={fillOpacity}
|
|
@@ -47,12 +48,7 @@ const MinimapSVG = observer(({ model }: { model: MsaViewModel }) => {
|
|
|
47
48
|
<polygon
|
|
48
49
|
fill={fillColor}
|
|
49
50
|
fillOpacity={fillOpacity}
|
|
50
|
-
points={
|
|
51
|
-
[e, 0],
|
|
52
|
-
[s, 0],
|
|
53
|
-
[0, H2],
|
|
54
|
-
[W, H2],
|
|
55
|
-
].toString()}
|
|
51
|
+
points={`${s + w},0 ${s},0 0,${H2} ${W},${H2}`}
|
|
56
52
|
/>
|
|
57
53
|
</g>
|
|
58
54
|
</>
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { MsaViewModel } from '../../model.ts'
|
|
2
|
+
|
|
3
|
+
// The slice of leaves whose rows intersect a block of height blockSizeY at
|
|
4
|
+
// vertical offset offsetY, padded by one row on each side.
|
|
5
|
+
export function getVisibleLeaves({
|
|
6
|
+
model,
|
|
7
|
+
offsetY,
|
|
8
|
+
blockSizeY,
|
|
9
|
+
}: {
|
|
10
|
+
model: MsaViewModel
|
|
11
|
+
offsetY: number
|
|
12
|
+
blockSizeY: number
|
|
13
|
+
}) {
|
|
14
|
+
const { leaves, rowHeight } = model
|
|
15
|
+
const yStart = Math.max(0, Math.floor((offsetY - rowHeight) / rowHeight))
|
|
16
|
+
const yEnd = Math.max(
|
|
17
|
+
0,
|
|
18
|
+
Math.ceil((offsetY + blockSizeY + rowHeight) / rowHeight),
|
|
19
|
+
)
|
|
20
|
+
return leaves.slice(yStart, yEnd)
|
|
21
|
+
}
|