react-msaview 6.1.1 → 6.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 +92 -92
- package/bundle/index.js.map +4 -4
- package/dist/components/dialogs/ExportSVGDialog.js +48 -20
- package/dist/components/dialogs/ExportSVGDialog.js.map +1 -1
- package/dist/components/getVisibleLeaves.d.ts +8 -0
- package/dist/components/getVisibleLeaves.js +13 -6
- package/dist/components/getVisibleLeaves.js.map +1 -1
- package/dist/components/minimap/Minimap.js +1 -1
- package/dist/components/minimap/Minimap.js.map +1 -1
- package/dist/components/minimap/MinimapSVG.d.ts +4 -4
- package/dist/components/minimap/MinimapSVG.js +16 -10
- package/dist/components/minimap/MinimapSVG.js.map +1 -1
- package/dist/components/minimap/minimapLayout.d.ts +5 -8
- package/dist/components/minimap/minimapLayout.js +10 -11
- package/dist/components/minimap/minimapLayout.js.map +1 -1
- package/dist/components/msa/msaRaster.d.ts +32 -2
- package/dist/components/msa/msaRaster.js +93 -15
- package/dist/components/msa/msaRaster.js.map +1 -1
- package/dist/{renderTestEnv.d.ts → headlessRenderEnv.d.ts} +15 -13
- package/dist/{renderTestEnv.js → headlessRenderEnv.js} +22 -18
- package/dist/headlessRenderEnv.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/renderToSvg.js +115 -36
- package/dist/renderToSvg.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +3 -3
- package/src/components/dialogs/ExportSVGDialog.tsx +55 -20
- package/src/components/getVisibleLeaves.ts +25 -9
- package/src/components/minimap/Minimap.tsx +1 -4
- package/src/components/minimap/MinimapSVG.tsx +50 -35
- package/src/components/minimap/minimapLayout.test.ts +16 -5
- package/src/components/minimap/minimapLayout.ts +10 -11
- package/src/components/msa/msaRaster.ts +124 -17
- package/src/{renderTestEnv.ts → headlessRenderEnv.ts} +23 -19
- package/src/impgRender.test.tsx +2 -2
- package/src/index.ts +6 -0
- package/src/renderDomainsSvg.test.tsx +2 -2
- package/src/renderMinimapSvg.test.tsx +29 -15
- package/src/renderRasterSvg.test.tsx +216 -0
- package/src/renderSequenceLogoSvg.test.tsx +6 -4
- package/src/renderToSvg.test.tsx +79 -0
- package/src/renderToSvg.tsx +201 -65
- package/src/version.ts +1 -1
- package/dist/renderTestEnv.js.map +0 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
2
|
+
* DOM shims for driving `renderToSvg` outside a browser -- the SVG-export
|
|
3
|
+
* tests, the README figure generator, and react-msaview-cli all need them.
|
|
4
4
|
*
|
|
5
5
|
* jsdom implements neither DOMMatrix nor a canvas 2D context, and
|
|
6
6
|
* `@jbrowse/svgcanvas` needs both: it tracks the current transform as a real
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* leaves the rest undefined -- every emitted transform then carries `NaN` in its
|
|
13
13
|
* x components, which no assertion on a drawn x-position can survive.
|
|
14
14
|
*/
|
|
15
|
-
export class
|
|
15
|
+
export class HeadlessDOMMatrix {
|
|
16
16
|
a;
|
|
17
17
|
b;
|
|
18
18
|
c;
|
|
@@ -29,7 +29,7 @@ export class TestDOMMatrix {
|
|
|
29
29
|
this.f = f;
|
|
30
30
|
}
|
|
31
31
|
multiply(o) {
|
|
32
|
-
return new
|
|
32
|
+
return new HeadlessDOMMatrix([
|
|
33
33
|
this.a * o.a + this.c * o.b,
|
|
34
34
|
this.b * o.a + this.d * o.b,
|
|
35
35
|
this.a * o.c + this.c * o.d,
|
|
@@ -39,13 +39,13 @@ export class TestDOMMatrix {
|
|
|
39
39
|
]);
|
|
40
40
|
}
|
|
41
41
|
translate(x, y = 0) {
|
|
42
|
-
return this.multiply(new
|
|
42
|
+
return this.multiply(new HeadlessDOMMatrix([1, 0, 0, 1, x, y]));
|
|
43
43
|
}
|
|
44
44
|
scale(x, y = x) {
|
|
45
|
-
return this.multiply(new
|
|
45
|
+
return this.multiply(new HeadlessDOMMatrix([x, 0, 0, y, 0, 0]));
|
|
46
46
|
}
|
|
47
47
|
}
|
|
48
|
-
export class
|
|
48
|
+
export class HeadlessDOMPoint {
|
|
49
49
|
x;
|
|
50
50
|
y;
|
|
51
51
|
constructor(x = 0, y = 0) {
|
|
@@ -53,22 +53,26 @@ export class TestDOMPoint {
|
|
|
53
53
|
this.y = y;
|
|
54
54
|
}
|
|
55
55
|
matrixTransform(m) {
|
|
56
|
-
return new
|
|
56
|
+
return new HeadlessDOMPoint(m.a * this.x + m.c * this.y + m.e, m.b * this.x + m.d * this.y + m.f);
|
|
57
57
|
}
|
|
58
58
|
}
|
|
59
59
|
/**
|
|
60
|
-
* Install the shims
|
|
60
|
+
* Install the shims, into `globalThis` by default. A Node caller that builds
|
|
61
|
+
* its own jsdom passes that window instead, because Node has no global
|
|
62
|
+
* HTMLCanvasElement to patch.
|
|
61
63
|
*
|
|
62
64
|
* `measureText` reports 0.6em per character, close enough to a real sans-serif
|
|
63
|
-
* average for layout
|
|
64
|
-
*
|
|
65
|
+
* average for layout and, more usefully, exactly predictable: a caller can
|
|
66
|
+
* compute the width a renderer will see rather than hardcoding a number
|
|
65
67
|
* measured from one machine's fonts.
|
|
66
68
|
*/
|
|
67
|
-
export function
|
|
69
|
+
export function installHeadlessRenderEnv(win = globalThis) {
|
|
68
70
|
const g = globalThis;
|
|
69
|
-
g.DOMMatrix =
|
|
70
|
-
g.DOMPoint =
|
|
71
|
-
|
|
71
|
+
g.DOMMatrix = HeadlessDOMMatrix;
|
|
72
|
+
g.DOMPoint = HeadlessDOMPoint;
|
|
73
|
+
const canvas = win
|
|
74
|
+
.HTMLCanvasElement;
|
|
75
|
+
canvas.prototype.getContext = function () {
|
|
72
76
|
let font = '10px sans-serif';
|
|
73
77
|
return {
|
|
74
78
|
get font() {
|
|
@@ -78,11 +82,11 @@ export function installRenderTestEnv() {
|
|
|
78
82
|
font = v;
|
|
79
83
|
},
|
|
80
84
|
measureText: (t) => ({
|
|
81
|
-
width: t.length * (Number.parseFloat(font) || 10) *
|
|
85
|
+
width: t.length * (Number.parseFloat(font) || 10) * CHAR_WIDTH_RATIO,
|
|
82
86
|
}),
|
|
83
87
|
};
|
|
84
88
|
};
|
|
85
89
|
}
|
|
86
90
|
/** the per-character width factor `measureText` above reports */
|
|
87
|
-
export const
|
|
88
|
-
//# sourceMappingURL=
|
|
91
|
+
export const CHAR_WIDTH_RATIO = 0.6;
|
|
92
|
+
//# sourceMappingURL=headlessRenderEnv.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"headlessRenderEnv.js","sourceRoot":"","sources":["../src/headlessRenderEnv.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,MAAM,OAAO,iBAAiB;IAC5B,CAAC,CAAQ;IACT,CAAC,CAAQ;IACT,CAAC,CAAQ;IACT,CAAC,CAAQ;IACT,CAAC,CAAQ;IACT,CAAC,CAAQ;IAET,YAAY,IAAe;QACzB,MAAM,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,GAAG,IAAI,IAAI,EAAE,CAAA;QAC7D,IAAI,CAAC,CAAC,GAAG,CAAC,CAAA;QACV,IAAI,CAAC,CAAC,GAAG,CAAC,CAAA;QACV,IAAI,CAAC,CAAC,GAAG,CAAC,CAAA;QACV,IAAI,CAAC,CAAC,GAAG,CAAC,CAAA;QACV,IAAI,CAAC,CAAC,GAAG,CAAC,CAAA;QACV,IAAI,CAAC,CAAC,GAAG,CAAC,CAAA;IACZ,CAAC;IAED,QAAQ,CAAC,CAAoB;QAC3B,OAAO,IAAI,iBAAiB,CAAC;YAC3B,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;YAC3B,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;YAC3B,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;YAC3B,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;YAC3B,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;YACpC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;SACrC,CAAC,CAAA;IACJ,CAAC;IAED,SAAS,CAAC,CAAS,EAAE,CAAC,GAAG,CAAC;QACxB,OAAO,IAAI,CAAC,QAAQ,CAAC,IAAI,iBAAiB,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAA;IACjE,CAAC;IAED,KAAK,CAAC,CAAS,EAAE,CAAC,GAAG,CAAC;QACpB,OAAO,IAAI,CAAC,QAAQ,CAAC,IAAI,iBAAiB,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAA;IACjE,CAAC;CACF;AAED,MAAM,OAAO,gBAAgB;IAElB,CAAC;IACD,CAAC;IAFV,YACS,CAAC,GAAG,CAAC,EACL,CAAC,GAAG,CAAC;iBADL,CAAC;iBACD,CAAC;IACP,CAAC;IAEJ,eAAe,CAAC,CAAoB;QAClC,OAAO,IAAI,gBAAgB,CACzB,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EACjC,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAClC,CAAA;IACH,CAAC;CACF;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,wBAAwB,CAAC,GAAG,GAAY,UAAU;IAChE,MAAM,CAAC,GAAG,UAAqC,CAAA;IAC/C,CAAC,CAAC,SAAS,GAAG,iBAAiB,CAAA;IAC/B,CAAC,CAAC,QAAQ,GAAG,gBAAgB,CAAA;IAC7B,MAAM,MAAM,GAAI,GAAuD;SACpE,iBAAiB,CAAA;IACpB,MAAM,CAAC,SAAS,CAAC,UAAU,GAAG;QAC5B,IAAI,IAAI,GAAG,iBAAiB,CAAA;QAC5B,OAAO;YACL,IAAI,IAAI;gBACN,OAAO,IAAI,CAAA;YACb,CAAC;YACD,IAAI,IAAI,CAAC,CAAS;gBAChB,IAAI,GAAG,CAAC,CAAA;YACV,CAAC;YACD,WAAW,EAAE,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC;gBAC3B,KAAK,EAAE,CAAC,CAAC,MAAM,GAAG,CAAC,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,GAAG,gBAAgB;aACrE,CAAC;SACoC,CAAA;IAC1C,CAA6D,CAAA;AAC/D,CAAC;AAED,iEAAiE;AACjE,MAAM,CAAC,MAAM,gBAAgB,GAAG,GAAG,CAAA"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { renderToSvg } from './renderToSvg.tsx';
|
|
2
|
+
export { CHAR_WIDTH_RATIO, installHeadlessRenderEnv, } from './headlessRenderEnv.ts';
|
|
2
3
|
export { default as MSAView } from './components/Loading.tsx';
|
|
3
4
|
export { default as MSAViewer } from './components/MSAViewer.tsx';
|
|
4
5
|
export { type MsaViewModel, default as MSAModelF } from './model.ts';
|
package/dist/index.js
CHANGED
|
@@ -6,6 +6,9 @@
|
|
|
6
6
|
// members they rely on (e.g. mouseCol/setMousePos, setHighlightedColumns,
|
|
7
7
|
// seqPosToVisibleCol/visibleColToSeqPos) are flagged inline in model.ts.
|
|
8
8
|
export { renderToSvg } from './renderToSvg.js';
|
|
9
|
+
// renderToSvg outside a browser needs DOM bits jsdom omits; react-msaview-cli
|
|
10
|
+
// and the README figure generator both drive it that way
|
|
11
|
+
export { CHAR_WIDTH_RATIO, installHeadlessRenderEnv, } from './headlessRenderEnv.js';
|
|
9
12
|
export { default as MSAView } from './components/Loading.js';
|
|
10
13
|
export { default as MSAViewer } from './components/MSAViewer.js';
|
|
11
14
|
export { default as MSAModelF } from './model.js';
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,yDAAyD;AACzD,+EAA+E;AAC/E,6EAA6E;AAC7E,gFAAgF;AAChF,0EAA0E;AAC1E,yEAAyE;AACzE,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAmB,CAAA;AAC/C,OAAO,EAAE,OAAO,IAAI,OAAO,EAAE,MAAM,yBAA0B,CAAA;AAC7D,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,2BAA4B,CAAA;AACjE,OAAO,EAAqB,OAAO,IAAI,SAAS,EAAE,MAAM,YAAY,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,yDAAyD;AACzD,+EAA+E;AAC/E,6EAA6E;AAC7E,gFAAgF;AAChF,0EAA0E;AAC1E,yEAAyE;AACzE,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAmB,CAAA;AAC/C,8EAA8E;AAC9E,yDAAyD;AACzD,OAAO,EACL,gBAAgB,EAChB,wBAAwB,GACzB,MAAM,wBAAwB,CAAA;AAC/B,OAAO,EAAE,OAAO,IAAI,OAAO,EAAE,MAAM,yBAA0B,CAAA;AAC7D,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,2BAA4B,CAAA;AACjE,OAAO,EAAqB,OAAO,IAAI,SAAS,EAAE,MAAM,YAAY,CAAA"}
|
package/dist/renderToSvg.js
CHANGED
|
@@ -2,9 +2,12 @@
|
|
|
2
2
|
import React from 'react';
|
|
3
3
|
import { renderToStaticMarkup } from '@jbrowse/core/util';
|
|
4
4
|
import { when } from 'mobx';
|
|
5
|
+
import { visibleRowRange } from './components/getVisibleLeaves.js';
|
|
5
6
|
import MinimapSVG from './components/minimap/MinimapSVG.js';
|
|
7
|
+
import { rasterImageHref, rasterSupported } from './components/msa/msaRaster.js';
|
|
6
8
|
import { renderBoxFeatureCanvasBlock } from './components/msa/renderBoxFeatureCanvasBlock.js';
|
|
7
9
|
import { renderMSABlock } from './components/msa/renderMSABlock.js';
|
|
10
|
+
import { visibleColRange } from './components/msa/visibleColRange.js';
|
|
8
11
|
import { renderAllTracks } from './components/tracks/drawTracks.js';
|
|
9
12
|
import { renderTreeCanvas } from './components/tree/renderTreeCanvas.js';
|
|
10
13
|
import { colorContrast } from './util.js';
|
|
@@ -15,26 +18,35 @@ const LEGEND_ROW_H = 16;
|
|
|
15
18
|
const LEGEND_SWATCH = 12;
|
|
16
19
|
const LEGEND_FONT = 12;
|
|
17
20
|
const LEGEND_CHAR_W = 7;
|
|
21
|
+
const LEGEND_MAX_W = 360;
|
|
22
|
+
const LEGEND_TEXT_X = LEGEND_PAD + LEGEND_SWATCH + 6;
|
|
18
23
|
function getLegendWidth(model) {
|
|
19
|
-
const
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
return Math.min(360, Math.max(120, w));
|
|
24
|
+
const maxLen = model.visibleDomainTypes.reduce((a, d) => Math.max(a, d.name.length), 0);
|
|
25
|
+
const w = LEGEND_TEXT_X + LEGEND_PAD + maxLen * LEGEND_CHAR_W;
|
|
26
|
+
return Math.min(LEGEND_MAX_W, Math.max(120, w));
|
|
23
27
|
}
|
|
24
28
|
function getLayout(model, opts) {
|
|
25
|
-
const {
|
|
29
|
+
const { scrollX, scrollY, totalWidth, totalHeight, treeAreaWidth, totalTrackAreaHeight, minimapHeight, msaAreaHeight, msaCanvasWidth, showHorizontalScrollbar, } = model;
|
|
26
30
|
const trackHeight = opts.includeTracks ? totalTrackAreaHeight : 0;
|
|
27
31
|
// the minimap reflects the live viewport scroll position, so it's only
|
|
28
|
-
// meaningful for a viewport export, never for the entire alignment
|
|
29
|
-
|
|
32
|
+
// meaningful for a viewport export, never for the entire alignment -- and
|
|
33
|
+
// only when the live view shows one at all, since a minimap over an alignment
|
|
34
|
+
// that already fits marks a viewport wider than the bar
|
|
35
|
+
const includeMinimap = opts.exportType === 'viewport' &&
|
|
36
|
+
!!opts.includeMinimap &&
|
|
37
|
+
showHorizontalScrollbar;
|
|
30
38
|
const legendWidth = model.actuallyShowDomains && model.visibleDomainTypes.length > 0
|
|
31
39
|
? getLegendWidth(model)
|
|
32
40
|
: 0;
|
|
33
|
-
// width stays content-only
|
|
34
|
-
//
|
|
41
|
+
// width stays content-only; the legend occupies an extra column added at the
|
|
42
|
+
// svg root in MsaSvg. The viewport export takes the alignment canvas's own
|
|
43
|
+
// size rather than the widget's -- the widget box also covers the header, the
|
|
44
|
+
// resize handle and the scrollbars, and rendering that box exports rows and
|
|
45
|
+
// columns the scrollbars hide on screen
|
|
35
46
|
return opts.exportType === 'entire'
|
|
36
47
|
? {
|
|
37
48
|
width: totalWidth + treeAreaWidth,
|
|
49
|
+
msaAreaWidth: totalWidth,
|
|
38
50
|
height: totalHeight + trackHeight,
|
|
39
51
|
contentHeight: totalHeight,
|
|
40
52
|
trackHeight,
|
|
@@ -44,9 +56,10 @@ function getLayout(model, opts) {
|
|
|
44
56
|
legendWidth,
|
|
45
57
|
}
|
|
46
58
|
: {
|
|
47
|
-
width,
|
|
48
|
-
|
|
49
|
-
|
|
59
|
+
width: msaCanvasWidth + treeAreaWidth,
|
|
60
|
+
msaAreaWidth: msaCanvasWidth,
|
|
61
|
+
height: msaAreaHeight + (includeMinimap ? minimapHeight : 0) + trackHeight,
|
|
62
|
+
contentHeight: msaAreaHeight,
|
|
50
63
|
trackHeight,
|
|
51
64
|
offsetX: -scrollX,
|
|
52
65
|
offsetY: -scrollY,
|
|
@@ -64,38 +77,43 @@ function MsaSvg({ model, theme, Context, layout, }) {
|
|
|
64
77
|
const { treeAreaWidth, minimapHeight } = model;
|
|
65
78
|
const totalWidth = width + legendWidth;
|
|
66
79
|
const legendTop = includeMinimap ? minimapHeight : 0;
|
|
80
|
+
const contrastScheme = colorContrast(model.colorScheme, theme);
|
|
81
|
+
const props = { Context, model, theme, layout, contrastScheme };
|
|
67
82
|
const body = (React.createElement(React.Fragment, null,
|
|
68
|
-
trackHeight > 0 ?
|
|
83
|
+
trackHeight > 0 ? React.createElement(TrackRendering, { ...props }) : null,
|
|
69
84
|
React.createElement("g", { transform: trackHeight > 0 ? `translate(0 ${trackHeight})` : undefined },
|
|
70
|
-
React.createElement(CoreRendering, {
|
|
85
|
+
React.createElement(CoreRendering, { ...props }))));
|
|
71
86
|
return (React.createElement("svg", { width: totalWidth, height: height, xmlns: "http://www.w3.org/2000/svg", xmlnsXlink: "http://www.w3.org/1999/xlink", viewBox: `0 0 ${totalWidth} ${height}` },
|
|
72
|
-
React.createElement("rect", { width: "100%", height: "100%", fill:
|
|
87
|
+
React.createElement("rect", { width: "100%", height: "100%", fill: theme.palette.background.default }),
|
|
73
88
|
includeMinimap ? (React.createElement(React.Fragment, null,
|
|
74
89
|
React.createElement("g", { transform: `translate(${treeAreaWidth} 0)` },
|
|
75
|
-
React.createElement(MinimapSVG, { model: model,
|
|
90
|
+
React.createElement(MinimapSVG, { model: model, theme: theme })),
|
|
76
91
|
React.createElement("g", { transform: `translate(0 ${minimapHeight})` }, body))) : (body),
|
|
77
92
|
legendWidth > 0 ? (React.createElement("g", { transform: `translate(${width} ${legendTop})` },
|
|
78
|
-
React.createElement(LegendSVG, { model: model, width: legendWidth }))) : null));
|
|
93
|
+
React.createElement(LegendSVG, { model: model, theme: theme, width: legendWidth }))) : null));
|
|
79
94
|
}
|
|
80
95
|
// the domain color key drawn as a reserved column to the right of the
|
|
81
96
|
// alignment, mirroring the on-screen AnnotationLegend overlay
|
|
82
|
-
function LegendSVG({ model, width }) {
|
|
97
|
+
function LegendSVG({ model, theme, width, }) {
|
|
83
98
|
const { visibleDomainTypes, fillPalette, strokePalette } = model;
|
|
84
99
|
const boxHeight = LEGEND_PAD * 2 + visibleDomainTypes.length * LEGEND_ROW_H;
|
|
100
|
+
// the column is capped at LEGEND_MAX_W, so a name too long for it has to be
|
|
101
|
+
// clipped here -- text that overruns the reserved width runs off the figure
|
|
102
|
+
const maxChars = Math.floor((width - LEGEND_TEXT_X - LEGEND_PAD) / LEGEND_CHAR_W);
|
|
85
103
|
return (React.createElement("g", null,
|
|
86
|
-
React.createElement("rect", { x: 0, y: 0, width: width - 4, height: boxHeight, fill:
|
|
104
|
+
React.createElement("rect", { x: 0, y: 0, width: width - 4, height: boxHeight, fill: theme.palette.background.paper, stroke: "#ccc", rx: 2 }),
|
|
87
105
|
visibleDomainTypes.map((d, i) => {
|
|
88
106
|
const y = LEGEND_PAD + i * LEGEND_ROW_H;
|
|
89
107
|
return (React.createElement("g", { key: d.accession },
|
|
90
108
|
React.createElement("rect", { x: LEGEND_PAD, y: y, width: LEGEND_SWATCH, height: LEGEND_SWATCH, fill: fillPalette[d.accession], stroke: strokePalette[d.accession] }),
|
|
91
|
-
React.createElement("text", { x:
|
|
109
|
+
React.createElement("text", { x: LEGEND_TEXT_X, y: y + LEGEND_SWATCH - 1, fontSize: LEGEND_FONT, fill: theme.palette.text.primary }, d.name.length > maxChars
|
|
110
|
+
? `${d.name.slice(0, Math.max(1, maxChars - 1))}…`
|
|
111
|
+
: d.name)));
|
|
92
112
|
})));
|
|
93
113
|
}
|
|
94
|
-
function CoreRendering({ model, theme, layout, Context, }) {
|
|
95
|
-
const { contentHeight, offsetX, offsetY,
|
|
96
|
-
const { treeAreaWidth,
|
|
97
|
-
const msaAreaWidth = width - treeAreaWidth;
|
|
98
|
-
const contrastScheme = colorContrast(colorScheme, theme);
|
|
114
|
+
function CoreRendering({ model, theme, layout, Context, contrastScheme, }) {
|
|
115
|
+
const { contentHeight, offsetX, offsetY, msaAreaWidth } = layout;
|
|
116
|
+
const { treeAreaWidth, id } = model;
|
|
99
117
|
const treeCtx = new Context(treeAreaWidth, contentHeight);
|
|
100
118
|
renderTreeCanvas({
|
|
101
119
|
model,
|
|
@@ -105,6 +123,7 @@ function CoreRendering({ model, theme, layout, Context, }) {
|
|
|
105
123
|
blockSizeYOverride: contentHeight,
|
|
106
124
|
highResScaleFactorOverride: 1,
|
|
107
125
|
});
|
|
126
|
+
const raster = rasterBackground({ model, theme, layout });
|
|
108
127
|
const msaCtx = new Context(msaAreaWidth, contentHeight);
|
|
109
128
|
renderBoxFeatureCanvasBlock({
|
|
110
129
|
model,
|
|
@@ -127,16 +146,54 @@ function CoreRendering({ model, theme, layout, Context, }) {
|
|
|
127
146
|
blockSizeXOverride: msaAreaWidth,
|
|
128
147
|
blockSizeYOverride: contentHeight,
|
|
129
148
|
highResScaleFactorOverride: 1,
|
|
149
|
+
rasterTiles: !!raster,
|
|
130
150
|
});
|
|
131
151
|
return (React.createElement(React.Fragment, null,
|
|
132
152
|
React.createElement(ClipGroup, { clipId: `tree-${id}`, width: treeAreaWidth, height: contentHeight, ctx: treeCtx }),
|
|
133
|
-
React.createElement(ClipGroup, { clipId: `msa-${id}`, width: msaAreaWidth, height: contentHeight, transform: `translate(${treeAreaWidth} 0)`, ctx: msaCtx })));
|
|
153
|
+
React.createElement(ClipGroup, { clipId: `msa-${id}`, width: msaAreaWidth, height: contentHeight, transform: `translate(${treeAreaWidth} 0)`, ctx: msaCtx, underlay: raster })));
|
|
134
154
|
}
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
155
|
+
/**
|
|
156
|
+
* The alignment background as a single <image>, on the same terms the live
|
|
157
|
+
* canvas uses it: no domain overlay painting its own boxes, background coloring
|
|
158
|
+
* on, and a canvas we can actually read back.
|
|
159
|
+
*
|
|
160
|
+
* One node replaces the <rect>-per-cell the vector path emits, which is what
|
|
161
|
+
* lets an export of a real alignment finish at all. The image is one pixel per
|
|
162
|
+
* cell drawn across the cells' own rectangle, so it upscales by whole cells --
|
|
163
|
+
* image-rendering keeps those edges hard rather than smearing them, making it
|
|
164
|
+
* pixel-for-pixel what the rects drew.
|
|
165
|
+
*/
|
|
166
|
+
function rasterBackground({ model, theme, layout, }) {
|
|
167
|
+
const { contentHeight, offsetX, offsetY, msaAreaWidth } = layout;
|
|
168
|
+
const { colWidth, rowHeight, actuallyShowDomains, bgColor } = model;
|
|
169
|
+
if (actuallyShowDomains || !bgColor || !rasterSupported()) {
|
|
170
|
+
return undefined;
|
|
171
|
+
}
|
|
172
|
+
const { xStart, xEnd } = visibleColRange({
|
|
173
|
+
offsetX,
|
|
174
|
+
blockWidth: msaAreaWidth,
|
|
175
|
+
colWidth,
|
|
176
|
+
});
|
|
177
|
+
const { yStart, yEnd } = visibleRowRange({
|
|
178
|
+
model,
|
|
179
|
+
offsetY,
|
|
180
|
+
blockSizeY: contentHeight,
|
|
181
|
+
});
|
|
182
|
+
const numCols = Math.min(xEnd, model.numColumns) - xStart;
|
|
183
|
+
const numRows = Math.min(yEnd, model.leaves.length) - yStart;
|
|
184
|
+
const href = rasterImageHref({
|
|
185
|
+
model,
|
|
186
|
+
theme,
|
|
187
|
+
col0: xStart,
|
|
188
|
+
row0: yStart,
|
|
189
|
+
numCols,
|
|
190
|
+
numRows,
|
|
191
|
+
});
|
|
192
|
+
return href ? (React.createElement("image", { href: href, x: xStart * colWidth - offsetX, y: yStart * rowHeight - offsetY, width: numCols * colWidth, height: numRows * rowHeight, imageRendering: "pixelated", preserveAspectRatio: "none" })) : undefined;
|
|
193
|
+
}
|
|
194
|
+
function TrackRendering({ model, theme, layout, Context, contrastScheme, }) {
|
|
195
|
+
const { trackHeight, offsetX, msaAreaWidth } = layout;
|
|
196
|
+
const { treeAreaWidth, id } = model;
|
|
140
197
|
const ctx = new Context(msaAreaWidth, trackHeight);
|
|
141
198
|
renderAllTracks({
|
|
142
199
|
model,
|
|
@@ -147,16 +204,38 @@ function TrackRendering({ model, theme, layout, Context, }) {
|
|
|
147
204
|
blockSizeXOverride: msaAreaWidth,
|
|
148
205
|
highResScaleFactorOverride: 1,
|
|
149
206
|
});
|
|
150
|
-
return (React.createElement(
|
|
151
|
-
React.createElement(
|
|
207
|
+
return (React.createElement(React.Fragment, null,
|
|
208
|
+
React.createElement(TrackLabelsSVG, { model: model, theme: theme }),
|
|
209
|
+
React.createElement("g", { transform: `translate(${treeAreaWidth} 0)` },
|
|
210
|
+
React.createElement(ClipGroup, { clipId: `tracks-${id}`, width: msaAreaWidth, height: trackHeight, ctx: ctx }))));
|
|
211
|
+
}
|
|
212
|
+
// Each track's name in the tree-width column beside it, where the live view puts
|
|
213
|
+
// it (see TrackLabel). Without these the figure has an unlabeled bar chart and
|
|
214
|
+
// an unlabeled logo, and nothing says which is which.
|
|
215
|
+
function TrackLabelsSVG({ model, theme, }) {
|
|
216
|
+
const { turnedOnTracks, treeAreaWidth, fontSize, drawLabels } = model;
|
|
217
|
+
if (!drawLabels) {
|
|
218
|
+
return null;
|
|
219
|
+
}
|
|
220
|
+
let y = 0;
|
|
221
|
+
return (React.createElement(React.Fragment, null, turnedOnTracks.map(track => {
|
|
222
|
+
const { id, name, height } = track.model;
|
|
223
|
+
const size = Math.min(height, fontSize);
|
|
224
|
+
const baseline = y + height / 2 + size / 3;
|
|
225
|
+
y += height;
|
|
226
|
+
return size < 5 ? null : (React.createElement("text", { key: id, x: treeAreaWidth - 4, y: baseline, fontSize: size, textAnchor: "end", fill: theme.palette.text.primary }, name));
|
|
227
|
+
})));
|
|
152
228
|
}
|
|
153
229
|
// Clips a svgcanvas Context to its box and injects its markup verbatim (it is
|
|
154
|
-
// already serialized SVG, not React).
|
|
155
|
-
|
|
230
|
+
// already serialized SVG, not React). `underlay` draws beneath that markup,
|
|
231
|
+
// inside the same clip, for the parts of a layer React emits directly.
|
|
232
|
+
function ClipGroup({ clipId, width, height, transform, ctx, underlay, }) {
|
|
156
233
|
return (React.createElement(React.Fragment, null,
|
|
157
234
|
React.createElement("defs", null,
|
|
158
235
|
React.createElement("clipPath", { id: clipId },
|
|
159
236
|
React.createElement("rect", { x: 0, y: 0, width: width, height: height }))),
|
|
160
|
-
React.createElement("g", { clipPath: `url(#${clipId})`, transform: transform
|
|
237
|
+
React.createElement("g", { clipPath: `url(#${clipId})`, transform: transform },
|
|
238
|
+
underlay,
|
|
239
|
+
React.createElement("g", { dangerouslySetInnerHTML: { __html: ctx.getSvg().innerHTML } }))));
|
|
161
240
|
}
|
|
162
241
|
//# sourceMappingURL=renderToSvg.js.map
|
package/dist/renderToSvg.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"renderToSvg.js","sourceRoot":"","sources":["../src/renderToSvg.tsx"],"names":[],"mappings":"AAAA,yDAAyD;AACzD,OAAO,KAAK,MAAM,OAAO,CAAA;AAEzB,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAA;AACzD,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAE3B,OAAO,UAAU,MAAM,oCAAqC,CAAA;AAC5D,OAAO,EAAE,2BAA2B,EAAE,MAAM,iDAAiD,CAAA;AAC7F,OAAO,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAA;AACnE,OAAO,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAA;AACnE,OAAO,EAAE,gBAAgB,EAAE,MAAM,uCAAuC,CAAA;AACxE,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAA;AAazC,8EAA8E;AAC9E,iEAAiE;AACjE,MAAM,UAAU,GAAG,CAAC,CAAA;AACpB,MAAM,YAAY,GAAG,EAAE,CAAA;AACvB,MAAM,aAAa,GAAG,EAAE,CAAA;AACxB,MAAM,WAAW,GAAG,EAAE,CAAA;AACtB,MAAM,aAAa,GAAG,CAAC,CAAA;AAEvB,
|
|
1
|
+
{"version":3,"file":"renderToSvg.js","sourceRoot":"","sources":["../src/renderToSvg.tsx"],"names":[],"mappings":"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"}
|
package/dist/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const version = "6.
|
|
1
|
+
export declare const version = "6.2.0";
|
package/dist/version.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export const version = '6.
|
|
1
|
+
export const version = '6.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": "6.
|
|
4
|
+
"version": "6.2.0",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"repository": {
|
|
@@ -43,8 +43,8 @@
|
|
|
43
43
|
"@gmod/newick": "^1.0.0",
|
|
44
44
|
"colord": "^2.9.3",
|
|
45
45
|
"flatbush": "^4.6.2",
|
|
46
|
-
"@jbrowse/svgcanvas": "6.
|
|
47
|
-
"msa-parsers": "6.
|
|
46
|
+
"@jbrowse/svgcanvas": "6.2.0",
|
|
47
|
+
"msa-parsers": "6.2.0"
|
|
48
48
|
},
|
|
49
49
|
"scripts": {
|
|
50
50
|
"clean": "rimraf dist bundle",
|
|
@@ -4,6 +4,7 @@ import { Dialog, ErrorMessage } from '@jbrowse/core/ui'
|
|
|
4
4
|
import {
|
|
5
5
|
Alert,
|
|
6
6
|
Button,
|
|
7
|
+
CircularProgress,
|
|
7
8
|
DialogActions,
|
|
8
9
|
DialogContent,
|
|
9
10
|
FormControl,
|
|
@@ -19,6 +20,16 @@ import Checkbox2 from '../Checkbox2.tsx'
|
|
|
19
20
|
|
|
20
21
|
import type { MsaViewModel } from '../../model.ts'
|
|
21
22
|
|
|
23
|
+
// Run after the browser has painted. A requestAnimationFrame callback still
|
|
24
|
+
// runs *before* the frame it was queued for, so one alone hands the main thread
|
|
25
|
+
// to the render with the spinner not yet on screen; the second fires once that
|
|
26
|
+
// frame is out.
|
|
27
|
+
function afterPaint(fn: () => void) {
|
|
28
|
+
requestAnimationFrame(() => {
|
|
29
|
+
requestAnimationFrame(fn)
|
|
30
|
+
})
|
|
31
|
+
}
|
|
32
|
+
|
|
22
33
|
export default function ExportSVGDialog({
|
|
23
34
|
model,
|
|
24
35
|
onClose,
|
|
@@ -32,6 +43,7 @@ export default function ExportSVGDialog({
|
|
|
32
43
|
'viewport',
|
|
33
44
|
)
|
|
34
45
|
const [error, setError] = useState<unknown>()
|
|
46
|
+
const [exporting, setExporting] = useState(false)
|
|
35
47
|
const theme = useTheme()
|
|
36
48
|
const {
|
|
37
49
|
totalWidth,
|
|
@@ -39,12 +51,20 @@ export default function ExportSVGDialog({
|
|
|
39
51
|
treeAreaWidth,
|
|
40
52
|
turnedOnTracks,
|
|
41
53
|
totalTrackAreaHeight,
|
|
54
|
+
numColumns,
|
|
55
|
+
leaves,
|
|
56
|
+
showMsaLetters,
|
|
42
57
|
} = model
|
|
43
58
|
const hasTracks = turnedOnTracks.length > 0
|
|
44
59
|
const entireWidth = totalWidth + treeAreaWidth
|
|
45
60
|
const entireHeight = totalHeight + (includeTracks ? totalTrackAreaHeight : 0)
|
|
46
|
-
|
|
47
|
-
|
|
61
|
+
// the background is one raster image whatever its size, so what the figure
|
|
62
|
+
// actually costs is its residue letters: each is an svg element of its own.
|
|
63
|
+
// Past roughly this many the export runs to tens of seconds and can exhaust
|
|
64
|
+
// the tab's memory outright
|
|
65
|
+
const glyphs =
|
|
66
|
+
showMsaLetters && exportType === 'entire' ? numColumns * leaves.length : 0
|
|
67
|
+
const isLargeExport = glyphs > 100_000
|
|
48
68
|
return (
|
|
49
69
|
<Dialog
|
|
50
70
|
onClose={() => {
|
|
@@ -100,8 +120,11 @@ export default function ExportSVGDialog({
|
|
|
100
120
|
</div>
|
|
101
121
|
{isLargeExport ? (
|
|
102
122
|
<Alert severity="warning" style={{ marginTop: 8 }}>
|
|
103
|
-
The entire MSA is
|
|
104
|
-
{Math.round(entireHeight)} pixels
|
|
123
|
+
The entire MSA is {Math.round(entireWidth)}x
|
|
124
|
+
{Math.round(entireHeight)} pixels and draws{' '}
|
|
125
|
+
{Math.round(glyphs / 1000)}k residue letters, each its own SVG
|
|
126
|
+
element. Export may be slow or fail — zooming out far enough to hide
|
|
127
|
+
the letters exports the same alignment as an image instead.
|
|
105
128
|
</Alert>
|
|
106
129
|
) : null}
|
|
107
130
|
</DialogContent>
|
|
@@ -109,25 +132,37 @@ export default function ExportSVGDialog({
|
|
|
109
132
|
<Button
|
|
110
133
|
variant="contained"
|
|
111
134
|
color="primary"
|
|
135
|
+
// rendering runs on the main thread and a large figure holds it for
|
|
136
|
+
// seconds; without this the button stays live through the freeze and
|
|
137
|
+
// a second click starts a second export
|
|
138
|
+
disabled={exporting}
|
|
139
|
+
startIcon={
|
|
140
|
+
exporting ? <CircularProgress size={16} color="inherit" /> : null
|
|
141
|
+
}
|
|
112
142
|
onClick={() => {
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
143
|
+
setExporting(true)
|
|
144
|
+
afterPaint(() => {
|
|
145
|
+
void model
|
|
146
|
+
.exportSVG({
|
|
147
|
+
theme,
|
|
148
|
+
includeMinimap,
|
|
149
|
+
includeTracks: hasTracks && includeTracks,
|
|
150
|
+
exportType,
|
|
151
|
+
})
|
|
152
|
+
.then(() => {
|
|
153
|
+
onClose()
|
|
154
|
+
})
|
|
155
|
+
.catch((e: unknown) => {
|
|
156
|
+
console.error(e)
|
|
157
|
+
setError(e)
|
|
158
|
+
})
|
|
159
|
+
.finally(() => {
|
|
160
|
+
setExporting(false)
|
|
161
|
+
})
|
|
162
|
+
})
|
|
128
163
|
}}
|
|
129
164
|
>
|
|
130
|
-
Submit
|
|
165
|
+
{exporting ? 'Exporting…' : 'Submit'}
|
|
131
166
|
</Button>
|
|
132
167
|
<Button
|
|
133
168
|
variant="contained"
|
|
@@ -1,7 +1,28 @@
|
|
|
1
1
|
import type { MsaViewModel } from '../model.ts'
|
|
2
2
|
|
|
3
|
-
// The
|
|
4
|
-
// vertical offset offsetY, padded by one row on each side.
|
|
3
|
+
// The half-open range of rows [yStart, yEnd) whose leaves intersect a block of
|
|
4
|
+
// height blockSizeY at vertical offset offsetY, padded by one row on each side.
|
|
5
|
+
// Y-axis counterpart to visibleColRange.
|
|
6
|
+
export function visibleRowRange({
|
|
7
|
+
model,
|
|
8
|
+
offsetY,
|
|
9
|
+
blockSizeY,
|
|
10
|
+
}: {
|
|
11
|
+
model: MsaViewModel
|
|
12
|
+
offsetY: number
|
|
13
|
+
blockSizeY: number
|
|
14
|
+
}) {
|
|
15
|
+
const { rowHeight } = model
|
|
16
|
+
return {
|
|
17
|
+
yStart: Math.max(0, Math.floor((offsetY - rowHeight) / rowHeight)),
|
|
18
|
+
yEnd: Math.max(
|
|
19
|
+
0,
|
|
20
|
+
Math.ceil((offsetY + blockSizeY + rowHeight) / rowHeight),
|
|
21
|
+
),
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// The leaves in that range.
|
|
5
26
|
export function getVisibleLeaves({
|
|
6
27
|
model,
|
|
7
28
|
offsetY,
|
|
@@ -11,11 +32,6 @@ export function getVisibleLeaves({
|
|
|
11
32
|
offsetY: number
|
|
12
33
|
blockSizeY: number
|
|
13
34
|
}) {
|
|
14
|
-
const {
|
|
15
|
-
|
|
16
|
-
const yEnd = Math.max(
|
|
17
|
-
0,
|
|
18
|
-
Math.ceil((offsetY + blockSizeY + rowHeight) / rowHeight),
|
|
19
|
-
)
|
|
20
|
-
return leaves.slice(yStart, yEnd)
|
|
35
|
+
const { yStart, yEnd } = visibleRowRange({ model, offsetY, blockSizeY })
|
|
36
|
+
return model.leaves.slice(yStart, yEnd)
|
|
21
37
|
}
|
|
@@ -13,10 +13,7 @@ import type { MsaViewModel } from '../../model.ts'
|
|
|
13
13
|
const Minimap = observer(function ({ model }: { model: MsaViewModel }) {
|
|
14
14
|
const { minimapHeight, msaCanvasWidth, highResScaleFactor } = model
|
|
15
15
|
const theme = useTheme()
|
|
16
|
-
const { unit, s, w, polygonHeight, polygonPoints } = getMinimapLayout(
|
|
17
|
-
model,
|
|
18
|
-
msaCanvasWidth,
|
|
19
|
-
)
|
|
16
|
+
const { unit, s, w, polygonHeight, polygonPoints } = getMinimapLayout(model)
|
|
20
17
|
|
|
21
18
|
// the whole alignment, sampled down to the bar: it says where the conserved
|
|
22
19
|
// blocks and the gappy stretches are, which is what a scroll aims at
|