@jbrowse/plugin-linear-genome-view 5.0.0-beta.1 → 5.0.0-beta.10
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/esm/FeatureTrack/configSchema.d.ts +142 -50
- package/esm/LaunchLinearGenomeView/index.js +2 -13
- package/esm/LinearGenomeView/afterAttach.d.ts +5 -5
- package/esm/LinearGenomeView/afterAttach.js +73 -59
- package/esm/LinearGenomeView/components/CenterLine.js +5 -3
- package/esm/LinearGenomeView/components/CrisprGuidePanel.js +3 -5
- package/esm/LinearGenomeView/components/ExportSvgDialog.d.ts +3 -2
- package/esm/LinearGenomeView/components/ExportSvgDialog.js +6 -2
- package/esm/LinearGenomeView/components/GetSequenceDialog.js +2 -2
- package/esm/LinearGenomeView/components/Header.js +11 -1
- package/esm/LinearGenomeView/components/HeaderClearHighlightButton.js +2 -2
- package/esm/LinearGenomeView/components/HeaderPanControls.js +2 -1
- package/esm/LinearGenomeView/components/HeaderZoomControls.js +4 -34
- package/esm/LinearGenomeView/components/Highlight.js +7 -7
- package/esm/LinearGenomeView/components/HighlightBand.js +2 -2
- package/esm/LinearGenomeView/components/HighlightChip.d.ts +1 -2
- package/esm/LinearGenomeView/components/HighlightChip.js +5 -7
- package/esm/LinearGenomeView/components/HoverPositionHighlight.js +3 -3
- package/esm/LinearGenomeView/components/ImportForm.js +2 -2
- package/esm/LinearGenomeView/components/LinearGenomeView.js +3 -3
- package/esm/LinearGenomeView/components/LinearGenomeViewContainer.js +3 -4
- package/esm/LinearGenomeView/components/MiniControls.js +3 -2
- package/esm/LinearGenomeView/components/OverviewHighlight.js +3 -3
- package/esm/LinearGenomeView/components/OverviewHighlightBand.js +2 -3
- package/esm/LinearGenomeView/components/OverviewRubberband.js +20 -45
- package/esm/LinearGenomeView/components/OverviewScalebar.js +4 -5
- package/esm/LinearGenomeView/components/OverviewScalebarPolygon.js +5 -3
- package/esm/LinearGenomeView/components/PaddingBlocks.d.ts +1 -1
- package/esm/LinearGenomeView/components/PaddingBlocks.js +14 -9
- package/esm/LinearGenomeView/components/RangeSelectOverlay.js +5 -6
- package/esm/LinearGenomeView/components/ResizeAllTracksHandle.d.ts +5 -0
- package/esm/LinearGenomeView/components/ResizeAllTracksHandle.js +71 -0
- package/esm/LinearGenomeView/components/Rubberband.d.ts +1 -2
- package/esm/LinearGenomeView/components/Rubberband.js +3 -2
- package/esm/LinearGenomeView/components/SVGHighlightBand.js +2 -3
- package/esm/LinearGenomeView/components/Scalebar.d.ts +2 -6
- package/esm/LinearGenomeView/components/Scalebar.js +8 -5
- package/esm/LinearGenomeView/components/ScalebarRefNameLabels.js +49 -34
- package/esm/LinearGenomeView/components/SearchBox.js +8 -7
- package/esm/LinearGenomeView/components/SearchResultsDialog.d.ts +2 -1
- package/esm/LinearGenomeView/components/SearchResultsDialog.js +2 -2
- package/esm/LinearGenomeView/components/SearchResultsTable.d.ts +2 -1
- package/esm/LinearGenomeView/components/SearchResultsTable.js +12 -29
- package/esm/LinearGenomeView/components/SequenceSearchDialog.d.ts +2 -4
- package/esm/LinearGenomeView/components/TrackContainer.js +63 -9
- package/esm/LinearGenomeView/components/TrackLabel.js +10 -3
- package/esm/LinearGenomeView/components/TrackLabelButtons.js +2 -2
- package/esm/LinearGenomeView/components/TrackLabelDragHandle.js +2 -2
- package/esm/LinearGenomeView/components/TrackLabelMenu.js +3 -2
- package/esm/LinearGenomeView/components/TrackRenderingContainer.d.ts +4 -1
- package/esm/LinearGenomeView/components/TrackRenderingContainer.js +11 -23
- package/esm/LinearGenomeView/components/TracksContainer.js +10 -22
- package/esm/LinearGenomeView/components/VerticalGuide.d.ts +1 -2
- package/esm/LinearGenomeView/components/VerticalGuide.js +3 -16
- package/esm/LinearGenomeView/components/ZoomButton.js +6 -1
- package/esm/LinearGenomeView/components/ZoomTransform.js +4 -0
- package/esm/LinearGenomeView/components/fetchSequence.d.ts +1 -2
- package/esm/LinearGenomeView/components/recentLocationsMenu.js +10 -0
- package/esm/LinearGenomeView/components/searchModes.d.ts +1 -1
- package/esm/LinearGenomeView/components/searchModes.js +1 -1
- package/esm/LinearGenomeView/components/useRangeSelect.d.ts +0 -3
- package/esm/LinearGenomeView/components/useRangeSelect.js +20 -59
- package/esm/LinearGenomeView/components/useSideScroll.d.ts +16 -2
- package/esm/LinearGenomeView/components/useSideScroll.js +153 -103
- package/esm/LinearGenomeView/components/util.d.ts +4 -0
- package/esm/LinearGenomeView/components/util.js +4 -2
- package/esm/LinearGenomeView/consts.d.ts +2 -0
- package/esm/LinearGenomeView/consts.js +3 -1
- package/esm/LinearGenomeView/containingLgv.d.ts +21 -0
- package/esm/LinearGenomeView/containingLgv.js +23 -0
- package/esm/LinearGenomeView/headerFit.d.ts +4 -2
- package/esm/LinearGenomeView/headerFit.js +20 -7
- package/esm/LinearGenomeView/index.d.ts +1 -5
- package/esm/LinearGenomeView/index.js +10 -7
- package/esm/LinearGenomeView/keyboardHandler.d.ts +2 -0
- package/esm/LinearGenomeView/keyboardHandler.js +15 -1
- package/esm/LinearGenomeView/launchKeys.d.ts +2 -0
- package/esm/LinearGenomeView/launchKeys.js +27 -0
- package/esm/LinearGenomeView/lazyDialogs.d.ts +2 -4
- package/esm/LinearGenomeView/lazyDialogs.js +1 -0
- package/esm/LinearGenomeView/linkedViewSync.d.ts +3 -11
- package/esm/LinearGenomeView/linkedViewSync.js +27 -43
- package/esm/LinearGenomeView/menuItems.d.ts +16 -18
- package/esm/LinearGenomeView/menuItems.js +242 -151
- package/esm/LinearGenomeView/model.d.ts +2009 -200
- package/esm/LinearGenomeView/model.js +545 -395
- package/esm/LinearGenomeView/sharedScaleContainer.d.ts +1 -1
- package/esm/LinearGenomeView/showRegionsWithUndo.js +7 -1
- package/esm/LinearGenomeView/stickyChrome.d.ts +5 -9
- package/esm/LinearGenomeView/stickyChrome.js +5 -9
- package/esm/LinearGenomeView/svgcomponents/SVGGridlines.js +4 -4
- package/esm/LinearGenomeView/svgcomponents/SVGHeader.d.ts +4 -1
- package/esm/LinearGenomeView/svgcomponents/SVGHeader.js +8 -21
- package/esm/LinearGenomeView/svgcomponents/SVGHighlights.js +8 -8
- package/esm/LinearGenomeView/svgcomponents/SVGHighlightsOverlay.js +3 -3
- package/esm/LinearGenomeView/svgcomponents/SVGLinearGenomeView.js +7 -11
- package/esm/LinearGenomeView/svgcomponents/SVGRegionSeparators.js +2 -5
- package/esm/LinearGenomeView/svgcomponents/SVGRowHeader.js +5 -6
- package/esm/LinearGenomeView/svgcomponents/SVGRuler.js +9 -10
- package/esm/LinearGenomeView/svgcomponents/SVGScalebar.js +3 -3
- package/esm/LinearGenomeView/svgcomponents/SVGStackedRow.d.ts +27 -0
- package/esm/LinearGenomeView/svgcomponents/SVGStackedRow.js +12 -0
- package/esm/LinearGenomeView/svgcomponents/SVGTrackLabel.js +15 -6
- package/esm/LinearGenomeView/svgcomponents/SVGTracks.d.ts +4 -4
- package/esm/LinearGenomeView/svgcomponents/SVGTracks.js +6 -5
- package/esm/LinearGenomeView/svgcomponents/SVGView.d.ts +1 -1
- package/esm/LinearGenomeView/svgcomponents/SVGView.js +1 -1
- package/esm/LinearGenomeView/svgcomponents/figureContract.js +14 -14
- package/esm/LinearGenomeView/svgcomponents/renderViewTracks.js +1 -1
- package/esm/LinearGenomeView/svgcomponents/useViewSvgFigure.js +50 -68
- package/esm/LinearGenomeView/svgcomponents/util.d.ts +9 -5
- package/esm/LinearGenomeView/svgcomponents/util.js +26 -21
- package/esm/LinearGenomeView/types.d.ts +8 -11
- package/esm/LinearGenomeView/util.d.ts +60 -45
- package/esm/LinearGenomeView/util.js +131 -160
- package/esm/MultiLevelRubberband/MultiLevelRubberband.js +7 -4
- package/esm/MultiLevelRubberband/index.d.ts +2 -0
- package/esm/MultiLevelRubberband/index.js +2 -0
- package/esm/MultiLevelRubberband/menuItems.d.ts +34 -0
- package/esm/MultiLevelRubberband/menuItems.js +52 -0
- package/esm/MultiLevelRubberband/rowLabel.d.ts +22 -0
- package/esm/MultiLevelRubberband/rowLabel.js +27 -0
- package/esm/MultiLevelRubberband/types.d.ts +1 -0
- package/esm/MultiLevelRubberband/useRangeSelect.d.ts +2 -1
- package/esm/MultiLevelRubberband/useRangeSelect.js +31 -47
- package/esm/displayKitTests/perRegionTestEnv.d.ts +214 -86
- package/esm/displayKitTests/perRegionTestEnv.js +30 -29
- package/esm/index.d.ts +15 -85
- package/esm/index.js +13 -36
- package/esm/searchUtils.d.ts +18 -9
- package/esm/searchUtils.js +85 -69
- package/esm/shared/RubberbandSpan.js +2 -1
- package/esm/shared/coordLabels.d.ts +7 -0
- package/esm/shared/coordLabels.js +15 -1
- package/esm/shared/useWindowDrag.d.ts +18 -0
- package/esm/shared/useWindowDrag.js +45 -0
- package/package.json +10 -10
- package/esm/LinearGenomeView/FloatingSvgOverlay.d.ts +0 -5
- package/esm/LinearGenomeView/FloatingSvgOverlay.js +0 -25
- package/esm/LinearGenomeView/initKeys.d.ts +0 -13
- package/esm/LinearGenomeView/initKeys.js +0 -104
- package/esm/MultiLevelRubberband/VerticalGuide.d.ts +0 -6
- package/esm/MultiLevelRubberband/VerticalGuide.js +0 -21
- package/esm/lazyPluginExports.d.ts +0 -23
- package/esm/lazyPluginExports.js +0 -36
|
@@ -3,7 +3,7 @@ import type { IAnyStateTreeNode } from '@jbrowse/mobx-state-tree';
|
|
|
3
3
|
* A container's zoom-out ceiling for the rows it stacks. Unanswered is its own
|
|
4
4
|
* state rather than a zero: a row that has not been laid out yet cannot supply
|
|
5
5
|
* a fit, and reading that as "no shared ceiling" is a different claim. See
|
|
6
|
-
* `
|
|
6
|
+
* `LinearSyntenyView/sharedFit.ts`.
|
|
7
7
|
*/
|
|
8
8
|
export type SharedFit = {
|
|
9
9
|
answered: false;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { getNotificationSink } from '@jbrowse/core/util';
|
|
2
|
+
import { transaction } from 'mobx';
|
|
2
3
|
/**
|
|
3
4
|
* Replace a view's displayed regions with a set someone asked for, frame them,
|
|
4
5
|
* and offer an Undo that puts the view back exactly as it was.
|
|
@@ -32,7 +33,12 @@ export function showRegionsWithUndo({ view, regions, message, alsoUndo, }) {
|
|
|
32
33
|
windowWidthBp: view.windowWidthBp,
|
|
33
34
|
windowStartBp: view.windowStartBp,
|
|
34
35
|
};
|
|
35
|
-
|
|
36
|
+
// two root actions in one transaction: `fitAllRegions` reads as the reader's
|
|
37
|
+
// own gesture to a synteny follow, which `showRegions` does not
|
|
38
|
+
transaction(() => {
|
|
39
|
+
view.setDisplayedRegions(regions);
|
|
40
|
+
view.fitAllRegions();
|
|
41
|
+
});
|
|
36
42
|
getNotificationSink(view).notify(message, 'info', {
|
|
37
43
|
name: 'Undo',
|
|
38
44
|
onClick: () => {
|
|
@@ -1,14 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Where the sticky boxes below the view's chrome pin, as CSS rather than as a
|
|
3
|
-
* pixel sum.
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* minimum-height, since pinning them to their constants clipped their content
|
|
9
|
-
* at a larger root font size, and one of them is briefly absent during a slow
|
|
10
|
-
* load. Anything positioning against them in the DOM reads what they publish
|
|
11
|
-
* and keeps the constant only as the fallback for the first frame.
|
|
3
|
+
* pixel sum. Both boxes above are minimum-height, since pinning them to their
|
|
4
|
+
* constants clipped their content at a larger root font size, and one of them
|
|
5
|
+
* is briefly absent during a slow load, so a sum of their constants is only
|
|
6
|
+
* nominal. Anything positioning against them reads what they publish and keeps
|
|
7
|
+
* the constant only as the fallback for the first frame.
|
|
12
8
|
*/
|
|
13
9
|
export declare function stickyChromeTops({ stickyViewHeaders, headerHeight, }: {
|
|
14
10
|
stickyViewHeaders: boolean;
|
|
@@ -3,15 +3,11 @@ import { LGV_HEADER_HEIGHT_VAR, VIEW_HEADER_HEIGHT_VAR, } from '@jbrowse/core/ut
|
|
|
3
3
|
import { SCALE_BAR_HEIGHT } from './consts.js';
|
|
4
4
|
/**
|
|
5
5
|
* Where the sticky boxes below the view's chrome pin, as CSS rather than as a
|
|
6
|
-
* pixel sum.
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* minimum-height, since pinning them to their constants clipped their content
|
|
12
|
-
* at a larger root font size, and one of them is briefly absent during a slow
|
|
13
|
-
* load. Anything positioning against them in the DOM reads what they publish
|
|
14
|
-
* and keeps the constant only as the fallback for the first frame.
|
|
6
|
+
* pixel sum. Both boxes above are minimum-height, since pinning them to their
|
|
7
|
+
* constants clipped their content at a larger root font size, and one of them
|
|
8
|
+
* is briefly absent during a slow load, so a sum of their constants is only
|
|
9
|
+
* nominal. Anything positioning against them reads what they publish and keeps
|
|
10
|
+
* the constant only as the fallback for the first frame.
|
|
15
11
|
*/
|
|
16
12
|
export function stickyChromeTops({ stickyViewHeaders, headerHeight, }) {
|
|
17
13
|
// `scalebar` is undefined rather than 0 when the chrome does not pin: its
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { SvgClipRect } from '@jbrowse/core/svg/SvgExport';
|
|
3
3
|
import { svgNodeId } from '@jbrowse/core/svg/svgId';
|
|
4
|
+
import { usePalette } from '@jbrowse/core/ui/PaletteContext';
|
|
4
5
|
import { getStrokeProps } from '@jbrowse/core/util';
|
|
5
|
-
import { useTheme } from '@mui/material';
|
|
6
6
|
import { gridlineTickXs, vlinePath } from './util.js';
|
|
7
7
|
export default function SVGGridlines({ model, height, }) {
|
|
8
8
|
const { width } = model;
|
|
9
|
-
const
|
|
9
|
+
const palette = usePalette();
|
|
10
10
|
// share the on-screen gridline colors; getStrokeProps splits the rgba alpha
|
|
11
11
|
// into strokeOpacity so the exported SVG stays renderer-safe
|
|
12
|
-
const minorProps = getStrokeProps(
|
|
13
|
-
const majorProps = getStrokeProps(
|
|
12
|
+
const minorProps = getStrokeProps(palette.gridlineMinor);
|
|
13
|
+
const majorProps = getStrokeProps(palette.gridlineMajor);
|
|
14
14
|
const { major, minor } = gridlineTickXs(model);
|
|
15
15
|
// gridlineTicks spans the whole staticBlocks frame, which overhangs the
|
|
16
16
|
// viewport on both sides, so clip to the view width rather than letting ticks
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import type { LinearGenomeViewModel } from '../index.ts';
|
|
2
|
-
|
|
2
|
+
import type { HeaderLayout } from './util.ts';
|
|
3
|
+
export default function SVGHeader({ model, fontSize, rulerHeight, layout, showCytobands, }: {
|
|
3
4
|
model: LinearGenomeViewModel;
|
|
4
5
|
rulerHeight: number;
|
|
5
6
|
fontSize: number;
|
|
7
|
+
layout: HeaderLayout;
|
|
8
|
+
showCytobands: boolean;
|
|
6
9
|
}): import("react").JSX.Element | null;
|
|
@@ -1,18 +1,17 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { usePalette } from '@jbrowse/core/ui/PaletteContext';
|
|
2
3
|
import { getSession, stripAlpha } from '@jbrowse/core/util';
|
|
3
4
|
import { createOverviewLayout, getOverviewRegionPxSpan, } from '@jbrowse/core/util/Base1DUtils';
|
|
4
5
|
import calculateDynamicBlocks from '@jbrowse/core/util/calculateDynamicBlocks';
|
|
5
|
-
import { useTheme } from '@mui/material';
|
|
6
6
|
import Cytobands from '../components/Cytobands.js';
|
|
7
7
|
import OverviewScalebarPolygon from '../components/OverviewScalebarPolygon.js';
|
|
8
8
|
import { getCytobands } from '../components/util.js';
|
|
9
9
|
import { HEADER_OVERVIEW_HEIGHT } from '../consts.js';
|
|
10
10
|
import SVGRuler from './SVGRuler.js';
|
|
11
11
|
import SVGScalebar from './SVGScalebar.js';
|
|
12
|
-
import { getHeaderLayout } from './util.js';
|
|
13
12
|
// "you are here" cytoband overview rendered above the ruler. Self-contained so
|
|
14
13
|
// the overview-layout work only runs when cytobands are actually shown.
|
|
15
|
-
function CytobandOverview({ model,
|
|
14
|
+
function CytobandOverview({ model, y, }) {
|
|
16
15
|
const { width, displayedRegions, minimumBlockWidth, bpPerPx } = model;
|
|
17
16
|
const overview = createOverviewLayout({
|
|
18
17
|
displayedRegions,
|
|
@@ -27,27 +26,15 @@ function CytobandOverview({ model, assembly, y, }) {
|
|
|
27
26
|
bpPerPx,
|
|
28
27
|
blocks: model.dynamicBlocks.blocks,
|
|
29
28
|
});
|
|
30
|
-
return block && span ? (_jsxs("g", { transform: `translate(0 ${y})`, children: [_jsx(Cytobands, { overview: overview, cytobands: getCytobands(
|
|
29
|
+
return block && span ? (_jsxs("g", { transform: `translate(0 ${y})`, children: [_jsx(Cytobands, { overview: overview, cytobands: getCytobands(getSession(model).assemblyManager.get(block.assemblyName), block.refName), block: block }), _jsx("rect", { stroke: "red", fill: "rgb(255,0,0)", fillOpacity: 0.1, width: Math.max(span.rightPx - span.leftPx, 0.5), height: HEADER_OVERVIEW_HEIGHT - 1, x: span.leftPx, y: 0.5 }), _jsx("g", { transform: `translate(0,${HEADER_OVERVIEW_HEIGHT})`, children: _jsx(OverviewScalebarPolygon, { overview: overview, model: model, exportSvg: true }) })] })) : null;
|
|
31
30
|
}
|
|
32
|
-
export default function SVGHeader({ model, fontSize, rulerHeight, }) {
|
|
33
|
-
const
|
|
34
|
-
const
|
|
35
|
-
// cytobands need a single unambiguous assembly, but the header label names
|
|
36
|
-
// every assembly in the view
|
|
37
|
-
const cytobandAssemblyName = assemblyNames.length === 1 ? assemblyNames[0] : undefined;
|
|
38
|
-
const assembly = cytobandAssemblyName
|
|
39
|
-
? assemblyManager.get(cytobandAssemblyName)
|
|
40
|
-
: undefined;
|
|
41
|
-
const theme = useTheme();
|
|
42
|
-
const fill = stripAlpha(theme.palette.text.primary);
|
|
31
|
+
export default function SVGHeader({ model, fontSize, rulerHeight, layout, showCytobands, }) {
|
|
32
|
+
const palette = usePalette();
|
|
33
|
+
const fill = stripAlpha(palette.text.primary);
|
|
43
34
|
// nothing on screen to label: no assembly name, scalebar or ruler
|
|
44
35
|
if (!model.hasVisibleContent) {
|
|
45
36
|
return null;
|
|
46
37
|
}
|
|
47
|
-
const { assemblyLabelBaselineY, cytobandTop, scalebarLineY, rulerTop } =
|
|
48
|
-
|
|
49
|
-
showCytobands: effectiveShowCytobands,
|
|
50
|
-
rulerHeight,
|
|
51
|
-
});
|
|
52
|
-
return (_jsxs("g", { id: "header", children: [_jsx("text", { x: 0, y: assemblyLabelBaselineY, fontSize: fontSize, fill: fill, children: assemblyNames.join(', ') }), effectiveShowCytobands ? (_jsx(CytobandOverview, { model: model, assembly: assembly, y: cytobandTop })) : null, _jsx("g", { transform: `translate(0 ${scalebarLineY})`, children: _jsx(SVGScalebar, { model: model, fontSize: fontSize }) }), _jsx("g", { transform: `translate(0 ${rulerTop})`, children: _jsx(SVGRuler, { model: model, rulerHeight: rulerHeight }) })] }));
|
|
38
|
+
const { assemblyLabelBaselineY, cytobandTop, scalebarLineY, rulerTop } = layout;
|
|
39
|
+
return (_jsxs("g", { id: "header", children: [_jsx("text", { x: 0, y: assemblyLabelBaselineY, fontSize: fontSize, fill: fill, children: model.assemblyNames.join(', ') }), showCytobands ? (_jsx(CytobandOverview, { model: model, y: cytobandTop })) : null, _jsx("g", { transform: `translate(0 ${scalebarLineY})`, children: _jsx(SVGScalebar, { model: model, fontSize: fontSize }) }), _jsx("g", { transform: `translate(0 ${rulerTop})`, children: _jsx(SVGRuler, { model: model, rulerHeight: rulerHeight }) })] }));
|
|
53
40
|
}
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useStyleTheme } from '@jbrowse/core/ui/PaletteContext';
|
|
2
3
|
import { getSession } from '@jbrowse/core/util';
|
|
3
|
-
import { useTheme } from '@mui/material';
|
|
4
4
|
import SVGHighlightBand from '../components/SVGHighlightBand.js';
|
|
5
5
|
import { getHighlightColor, highlightKey } from '../components/util.js';
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
// separately via the LinearGenomeView-HighlightSVGComponent extension point.
|
|
6
|
+
// The view's highlights drawn as full-height bands over the tracks area, with
|
|
7
|
+
// an optional label at the top.
|
|
9
8
|
//
|
|
10
9
|
// Deliberately NOT an observer, and that is the whole of what keeps a live
|
|
11
10
|
// figure coherent. `useViewSvgFigure` freezes a figure by memoizing it against
|
|
@@ -16,11 +15,12 @@ import { getHighlightColor, highlightKey } from '../components/util.js';
|
|
|
16
15
|
// The file export renders the whole document in one synchronous pass, so it
|
|
17
16
|
// never needed the subscription either.
|
|
18
17
|
export default function SVGHighlights({ model, height, }) {
|
|
19
|
-
const theme =
|
|
20
|
-
|
|
21
|
-
|
|
18
|
+
const theme = useStyleTheme();
|
|
19
|
+
const session = getSession(model);
|
|
20
|
+
return session.highlightsVisible
|
|
21
|
+
? model.highlights.map((h, idx) => {
|
|
22
22
|
const coords = model.getHighlightCoords(h);
|
|
23
|
-
return coords ? (_jsx(SVGHighlightBand, { coords: coords, height: height, color: getHighlightColor(h, theme).toRgbString(), label:
|
|
23
|
+
return coords ? (_jsx(SVGHighlightBand, { coords: coords, height: height, color: getHighlightColor(h, theme).toRgbString(), label: session.highlightLabelsVisible ? h.label : undefined, labelColor: theme.palette.text.primary }, highlightKey(h, idx))) : null;
|
|
24
24
|
})
|
|
25
25
|
: null;
|
|
26
26
|
}
|
|
@@ -4,9 +4,9 @@ import { svgNodeId } from '@jbrowse/core/svg/svgId';
|
|
|
4
4
|
import { PluggableElements } from '@jbrowse/core/ui';
|
|
5
5
|
import { getEnv } from '@jbrowse/core/util';
|
|
6
6
|
import SVGHighlights from './SVGHighlights.js';
|
|
7
|
-
// The highlight layer over one view's tracks area:
|
|
8
|
-
// whatever plugins contribute
|
|
9
|
-
//
|
|
7
|
+
// The highlight layer over one view's tracks area: the view's highlights plus
|
|
8
|
+
// whatever plugins contribute, clipped to the view width. Callers translate
|
|
9
|
+
// this to the top-left of the tracks area.
|
|
10
10
|
export default function SVGHighlightsOverlay({ model, tracksHeight, }) {
|
|
11
11
|
const { pluginManager } = getEnv(model);
|
|
12
12
|
return (_jsxs(SvgClipRect, { id: `highlight-clip-${svgNodeId(model)}`, width: model.width, height: tracksHeight, children: [_jsx(SVGHighlights, { model: model, height: tracksHeight }), _jsx(PluggableElements, { pluginManager: pluginManager, name: "LinearGenomeView-HighlightSVGComponent", props: { model, height: tracksHeight } })] }));
|
|
@@ -24,7 +24,7 @@ export async function renderToSvg(model, opts) {
|
|
|
24
24
|
const { fontSize = 13,
|
|
25
25
|
// the label band scales with the font it holds; destructured after
|
|
26
26
|
// fontSize so the default can read it
|
|
27
|
-
textHeight = defaultTextHeight(fontSize), rulerHeight = 34, trackLabels = 'offset', themeName
|
|
27
|
+
textHeight = defaultTextHeight(fontSize), rulerHeight = 34, trackLabels = 'offset', themeName, fontFamily, showGridlines = false, Wrapper, } = opts;
|
|
28
28
|
const session = getSession(model);
|
|
29
29
|
const theme = session.getActiveThemeOptions?.(themeName);
|
|
30
30
|
// owns the two orderings this used to spell out by hand: legendWidth before
|
|
@@ -43,19 +43,15 @@ export async function renderToSvg(model, opts) {
|
|
|
43
43
|
reserveLegendWidth: true,
|
|
44
44
|
});
|
|
45
45
|
notifySkippedSvgTracks(session, skippedTracks);
|
|
46
|
-
//
|
|
47
|
-
//
|
|
48
|
-
// wrapSvgExport) and lays itself out with the same getHeaderLayout call, so a
|
|
49
|
-
// value that moved during the awaits would leave the reserved `tracksTop` and
|
|
50
|
-
// the drawn header describing different rows. Same rule the dotplot, circular
|
|
51
|
-
// and synteny exports each shipped a violation of.
|
|
46
|
+
// read after the displays' waits, and handed to SVGHeader rather than read
|
|
47
|
+
// again, so the reserved `tracksTop` and the drawn header are one layout
|
|
52
48
|
const { width, effectiveShowCytobands } = model;
|
|
53
|
-
const
|
|
49
|
+
const headerLayout = getHeaderLayout({
|
|
54
50
|
fontSize,
|
|
55
51
|
showCytobands: effectiveShowCytobands,
|
|
56
52
|
rulerHeight,
|
|
57
53
|
});
|
|
58
|
-
const
|
|
54
|
+
const { tracksTop } = headerLayout;
|
|
59
55
|
const trackLabelOffset = trackLabelLeftOffset({
|
|
60
56
|
tracks,
|
|
61
57
|
trackLabels,
|
|
@@ -68,7 +64,7 @@ export async function renderToSvg(model, opts) {
|
|
|
68
64
|
return wrapSvgExport({
|
|
69
65
|
theme,
|
|
70
66
|
width: w,
|
|
71
|
-
height,
|
|
67
|
+
height: tracksTop + tracksHeight + exportMargin,
|
|
72
68
|
fontFamily,
|
|
73
69
|
Wrapper,
|
|
74
70
|
children: (_jsx("g", { transform: `translate(${exportMargin} 0)`, children: _jsx(SVGView, { view: model, displayResults: displayResults,
|
|
@@ -76,6 +72,6 @@ export async function renderToSvg(model, opts) {
|
|
|
76
72
|
// header is the full one: cytoband overview and total-bp scalebar as
|
|
77
73
|
// well as the assembly name and ruler. `tracksTop` is the height
|
|
78
74
|
// getHeaderLayout gave it.
|
|
79
|
-
header: _jsx(SVGHeader, { model: model, fontSize: fontSize, rulerHeight: rulerHeight }), fontSize: fontSize, textHeight: textHeight, trackLabels: trackLabels, trackLabelOffset: trackLabelOffset, contentTop: tracksTop, tracksHeight: tracksHeight, showGridlines: showGridlines, leftBuffer: exportMargin, legendWidth: legendWidth }) })),
|
|
75
|
+
header: _jsx(SVGHeader, { model: model, fontSize: fontSize, rulerHeight: rulerHeight, layout: headerLayout, showCytobands: effectiveShowCytobands }), fontSize: fontSize, textHeight: textHeight, trackLabels: trackLabels, trackLabelOffset: trackLabelOffset, contentTop: tracksTop, tracksHeight: tracksHeight, showGridlines: showGridlines, leftBuffer: exportMargin, legendWidth: legendWidth }) })),
|
|
80
76
|
});
|
|
81
77
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import {
|
|
2
|
+
import { usePalette } from '@jbrowse/core/ui/PaletteContext';
|
|
3
3
|
// SVG counterpart of the on-screen PaddingBlocks region separators: an opaque
|
|
4
4
|
// 3px bar at the right edge of every displayed region, in the same per-mode grey
|
|
5
5
|
// (a translucent fill would tint the track data it masks rather than hide it).
|
|
@@ -15,10 +15,7 @@ import { useTheme } from '@mui/material';
|
|
|
15
15
|
// close the "divergence".
|
|
16
16
|
export default function SVGRegionSeparators({ model, height, }) {
|
|
17
17
|
const { dynamicBlocks, offsetPx } = model;
|
|
18
|
-
const
|
|
19
|
-
const fill = theme.palette.mode === 'dark'
|
|
20
|
-
? theme.palette.grey[500]
|
|
21
|
-
: theme.palette.grey[600];
|
|
18
|
+
const fill = usePalette().regionSeparator;
|
|
22
19
|
return (_jsx(_Fragment, { children: dynamicBlocks.contentBlocks
|
|
23
20
|
.filter(block => block.isRightEndOfDisplayedRegion)
|
|
24
21
|
.map(block => (_jsx("rect", { x: block.offsetPx + block.widthPx - offsetPx - 1, width: 3, y: 0, height: height, fill: fill }, block.key))) }));
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { usePalette } from '@jbrowse/core/ui/PaletteContext';
|
|
2
3
|
import { stripAlpha } from '@jbrowse/core/util';
|
|
3
|
-
import { useTheme } from '@mui/material';
|
|
4
4
|
import SVGRuler from './SVGRuler.js';
|
|
5
5
|
import SVGScalebar from './SVGScalebar.js';
|
|
6
6
|
import { getRowHeaderLayout } from './util.js';
|
|
@@ -10,9 +10,8 @@ import { getRowHeaderLayout } from './util.js';
|
|
|
10
10
|
// draws the cytoband overview — there is room for that above a single view, and
|
|
11
11
|
// none between two rows of a synteny stack.
|
|
12
12
|
export default function SVGRowHeader({ view, fontSize,
|
|
13
|
-
//
|
|
14
|
-
//
|
|
15
|
-
// tracks, and the tracks start at `contentTop`.
|
|
13
|
+
// SVGStackedRow passes the same value as SVGView's `contentTop`, so the
|
|
14
|
+
// ticks hanging off this budget meet the tracks
|
|
16
15
|
rulerHeight,
|
|
17
16
|
// Whether this row draws its assembly name. Default true, which is what a
|
|
18
17
|
// stack of DIFFERENT assemblies wants — a synteny export names each row
|
|
@@ -28,10 +27,10 @@ showAssemblyName = true,
|
|
|
28
27
|
// band and every caller has to reserve that much before asking for it. See
|
|
29
28
|
// getRowHeaderLayout for what it is for.
|
|
30
29
|
showScalebar = false, }) {
|
|
31
|
-
const
|
|
30
|
+
const palette = usePalette();
|
|
32
31
|
const { assemblyLabelBaselineY, scalebarLineY } = getRowHeaderLayout({
|
|
33
32
|
fontSize,
|
|
34
33
|
showScalebar,
|
|
35
34
|
});
|
|
36
|
-
return (_jsxs(_Fragment, { children: [showAssemblyName ? (_jsx("text", { x: 0, y: assemblyLabelBaselineY, fontSize: fontSize, fill: stripAlpha(
|
|
35
|
+
return (_jsxs(_Fragment, { children: [showAssemblyName ? (_jsx("text", { x: 0, y: assemblyLabelBaselineY, fontSize: fontSize, fill: stripAlpha(palette.text.primary), children: view.assemblyNames.join(', ') })) : null, scalebarLineY === undefined ? null : (_jsx("g", { transform: `translate(0 ${scalebarLineY})`, children: _jsx(SVGScalebar, { model: view, fontSize: fontSize }) })), _jsx(SVGRuler, { model: view, rulerHeight: rulerHeight })] }));
|
|
37
36
|
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { SvgClipRect } from '@jbrowse/core/svg/SvgExport';
|
|
3
3
|
import { svgNodeId } from '@jbrowse/core/svg/svgId';
|
|
4
|
+
import { usePalette } from '@jbrowse/core/ui/PaletteContext';
|
|
4
5
|
import { stripAlpha } from '@jbrowse/core/util';
|
|
5
|
-
import {
|
|
6
|
-
import { REF_NAME_LABEL_FONT_SIZE, getScalebarRefNameLabels, labelFitsInBlock, refNameLabelFitsInView, tickLabelWidth, } from '../util.js';
|
|
6
|
+
import { REF_NAME_LABEL_FONT_SIZE, TICK_LABEL_FONT_SIZE, getScalebarRefNameLabels, labelFitsInBlock, refNameLabelFitsInView, tickLabelWidth, } from '../util.js';
|
|
7
7
|
import SVGRegionSeparators from './SVGRegionSeparators.js';
|
|
8
|
-
import { RULER_MAJOR_TICK, RULER_MINOR_TICK,
|
|
8
|
+
import { RULER_MAJOR_TICK, RULER_MINOR_TICK, getRulerLayout, gridlineTickXs, refNameLabelBaselineY, refNameLabelBoxHeight, vlinePath, } from './util.js';
|
|
9
9
|
// Tick marks and their coordinate labels, read from the same model getters the
|
|
10
10
|
// on-screen Gridlines/ScalebarCoordinateLabels use (gridlineTicks /
|
|
11
11
|
// scalebarLabels), so tick pitch, label text and label placement can't drift
|
|
@@ -13,8 +13,8 @@ import { RULER_MAJOR_TICK, RULER_MINOR_TICK, RULER_TICK_FONT_SIZE, getRulerLayou
|
|
|
13
13
|
// the viewport, so shift by the view's `staticBlocksTranslateX` and let the
|
|
14
14
|
// caller's clip trim the overhang.
|
|
15
15
|
function Ruler({ model, tickTopY, numbersBaselineY, }) {
|
|
16
|
-
const
|
|
17
|
-
const color = stripAlpha(
|
|
16
|
+
const palette = usePalette();
|
|
17
|
+
const color = stripAlpha(palette.text.secondary);
|
|
18
18
|
const { scalebarLabels, width } = model;
|
|
19
19
|
const { dx, major, minor } = gridlineTickXs(model);
|
|
20
20
|
// major and minor marks share a stroke and differ only in length, so both
|
|
@@ -26,15 +26,14 @@ function Ruler({ model, tickTopY, numbersBaselineY, }) {
|
|
|
26
26
|
const w = tickLabelWidth(label);
|
|
27
27
|
return labelFitsInBlock(dx + x - w / 2, w, width);
|
|
28
28
|
})
|
|
29
|
-
.map(({ x, label, key }) => (_jsx("text", { x: dx + x, y: numbersBaselineY, textAnchor: "middle", fontSize:
|
|
29
|
+
.map(({ x, label, key }) => (_jsx("text", { x: dx + x, y: numbersBaselineY, textAnchor: "middle", fontSize: TICK_LABEL_FONT_SIZE, fill: color, children: label }, key)))] }));
|
|
30
30
|
}
|
|
31
31
|
// Chromosome/refName labels along the ruler. Reuses the on-screen scalebar's
|
|
32
32
|
// sticky-label logic (getScalebarRefNameLabels) so the exported name stays
|
|
33
33
|
// pinned to the viewport's left edge when a region has scrolled off — otherwise
|
|
34
34
|
// the name renders off-canvas whenever you export a view zoomed into a
|
|
35
35
|
// chromosome interior. No assembly-name prefix here (unlike the on-screen
|
|
36
|
-
// scalebar): the SVG export already draws
|
|
37
|
-
// above the ruler, so folding it into this one too is redundant.
|
|
36
|
+
// scalebar): the SVG export already draws the assembly name above the ruler.
|
|
38
37
|
//
|
|
39
38
|
// `orientation` is not redundant, and this is the surface that needed it most.
|
|
40
39
|
// A stacked export gives each row SVGRowHeader — assembly name, ruler, refName
|
|
@@ -42,8 +41,8 @@ function Ruler({ model, tickTopY, numbersBaselineY, }) {
|
|
|
42
41
|
// counting down as the only evidence it was flipped. On screen the search box
|
|
43
42
|
// says so; a figure has no search box.
|
|
44
43
|
function SVGRefNameLabels({ model }) {
|
|
45
|
-
const
|
|
46
|
-
const fill = stripAlpha(
|
|
44
|
+
const palette = usePalette();
|
|
45
|
+
const fill = stripAlpha(palette.text.primary);
|
|
47
46
|
const { labels, caption } = getScalebarRefNameLabels({
|
|
48
47
|
blocks: model.staticBlocks.blocks,
|
|
49
48
|
offsetPx: model.offsetPx,
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { usePalette } from '@jbrowse/core/ui/PaletteContext';
|
|
2
3
|
import { getBpDisplayStr, stripAlpha } from '@jbrowse/core/util';
|
|
3
|
-
import { useTheme } from '@mui/material';
|
|
4
4
|
import { SVG_SCALEBAR_CAP } from '../consts.js';
|
|
5
5
|
import { labelBaselineFromTop } from './util.js';
|
|
6
6
|
export default function SVGScalebar({ model, fontSize, }) {
|
|
7
7
|
const { offsetPx, dynamicBlocks: { totalWidthPxWithoutBorders: totalWidthPx, totalBp }, } = model;
|
|
8
|
-
const
|
|
8
|
+
const palette = usePalette();
|
|
9
9
|
const displayBp = getBpDisplayStr(totalBp);
|
|
10
10
|
const x0 = Math.max(-offsetPx, 0);
|
|
11
11
|
const x1 = x0 + totalWidthPx;
|
|
12
|
-
const color = stripAlpha(
|
|
12
|
+
const color = stripAlpha(palette.text.secondary);
|
|
13
13
|
// the bar is drawn on this component's own origin (y=0), with capped ends and
|
|
14
14
|
// the bp label hanging just below; callers translate it to the desired line y
|
|
15
15
|
return (_jsxs(_Fragment, { children: [_jsx("line", { x1: x0, x2: x1, y1: 0, y2: 0, stroke: color }), _jsx("line", { x1: x0, x2: x0, y1: -SVG_SCALEBAR_CAP, y2: SVG_SCALEBAR_CAP, stroke: color }), _jsx("line", { x1: x1, x2: x1, y1: -SVG_SCALEBAR_CAP, y2: SVG_SCALEBAR_CAP, stroke: color }), _jsx("text", { x: (x0 + x1) / 2, y: labelBaselineFromTop(SVG_SCALEBAR_CAP, fontSize), textAnchor: "middle", fontSize: fontSize, fill: color, children: displayBp })] }));
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { LinearGenomeViewModel } from '../index.ts';
|
|
2
|
+
import type { SvgDisplayResult } from './util.ts';
|
|
3
|
+
import type { TrackLabelMode } from '@jbrowse/display-kit/types';
|
|
4
|
+
/**
|
|
5
|
+
* One view of a stacked export: its ruler, with the assembly name and scalebar
|
|
6
|
+
* above it, over its track bodies. `top` is where the ruler starts; the caller
|
|
7
|
+
* reserves `getRowHeaderLayout().bandHeight` above it for the header to draw
|
|
8
|
+
* into, and the row is `rulerHeight + rendered.tracksHeight` tall below it.
|
|
9
|
+
*/
|
|
10
|
+
export default function SVGStackedRow({ view, rendered, top, margin, fontSize, textHeight, rulerHeight, trackLabels, trackLabelOffset, showGridlines, legendWidth, showAssemblyName, showScalebar, }: {
|
|
11
|
+
view: LinearGenomeViewModel;
|
|
12
|
+
rendered: {
|
|
13
|
+
displayResults: SvgDisplayResult[];
|
|
14
|
+
tracksHeight: number;
|
|
15
|
+
};
|
|
16
|
+
top: number;
|
|
17
|
+
margin: number;
|
|
18
|
+
fontSize: number;
|
|
19
|
+
textHeight: number;
|
|
20
|
+
rulerHeight: number;
|
|
21
|
+
trackLabels: TrackLabelMode;
|
|
22
|
+
trackLabelOffset: number;
|
|
23
|
+
showGridlines: boolean;
|
|
24
|
+
legendWidth?: number;
|
|
25
|
+
showAssemblyName?: boolean;
|
|
26
|
+
showScalebar?: boolean;
|
|
27
|
+
}): import("react").JSX.Element;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import SVGRowHeader from './SVGRowHeader.js';
|
|
3
|
+
import SVGView from './SVGView.js';
|
|
4
|
+
/**
|
|
5
|
+
* One view of a stacked export: its ruler, with the assembly name and scalebar
|
|
6
|
+
* above it, over its track bodies. `top` is where the ruler starts; the caller
|
|
7
|
+
* reserves `getRowHeaderLayout().bandHeight` above it for the header to draw
|
|
8
|
+
* into, and the row is `rulerHeight + rendered.tracksHeight` tall below it.
|
|
9
|
+
*/
|
|
10
|
+
export default function SVGStackedRow({ view, rendered, top, margin, fontSize, textHeight, rulerHeight, trackLabels, trackLabelOffset, showGridlines, legendWidth, showAssemblyName, showScalebar, }) {
|
|
11
|
+
return (_jsx("g", { transform: `translate(${margin} ${top})`, children: _jsx(SVGView, { view: view, displayResults: rendered.displayResults, header: _jsx(SVGRowHeader, { view: view, fontSize: fontSize, rulerHeight: rulerHeight, showAssemblyName: showAssemblyName, showScalebar: showScalebar }), fontSize: fontSize, textHeight: textHeight, trackLabels: trackLabels, trackLabelOffset: trackLabelOffset, contentTop: rulerHeight, tracksHeight: rendered.tracksHeight, showGridlines: showGridlines, leftBuffer: margin, legendWidth: legendWidth }) }));
|
|
12
|
+
}
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { usePalette } from '@jbrowse/core/ui/PaletteContext';
|
|
2
3
|
import { stripAlpha } from '@jbrowse/core/util';
|
|
3
|
-
import { useTheme } from '@mui/material';
|
|
4
4
|
import { TRACK_LABEL_GAP, insetLabelBaselineY, offsetLabelBaselineY, } from './util.js';
|
|
5
5
|
// Where the label sits, per mode. 'left' right-aligns in the gutter
|
|
6
6
|
// trackLabelLeftOffset reserved (hence the shared TRACK_LABEL_GAP); the other
|
|
7
7
|
// modes hang off the leftmost visible content at `x`, either just above the
|
|
8
|
-
// track body ('offset') or inset over it ('
|
|
8
|
+
// track body ('offset') or inset over it ('overlapping').
|
|
9
9
|
//
|
|
10
10
|
// Every mode is placed on its alphabetic baseline (the SVG default, so no
|
|
11
11
|
// dominantBaseline is emitted at all): 'offset' so its descenders land a known
|
|
12
12
|
// distance above the track body — the band it sits in is `textHeight` tall, and
|
|
13
|
-
// offsetLabelBaselineY owns that arithmetic — and 'left'/'
|
|
13
|
+
// offsetLabelBaselineY owns that arithmetic — and 'left'/'overlapping' so their
|
|
14
14
|
// ascenders stay inside the track box they align with. 'left' used to be pinned
|
|
15
15
|
// at a hardcoded y=20 on a hanging baseline, which neither scaled with fontSize
|
|
16
16
|
// nor stayed inside a track shorter than ~35px.
|
|
@@ -32,8 +32,8 @@ function labelPosition({ trackLabels, trackLabelOffset, textHeight, fontSize, x,
|
|
|
32
32
|
return { x: x + 5, y: insetLabelBaselineY(fontSize) };
|
|
33
33
|
}
|
|
34
34
|
export default function SVGTrackLabel({ trackLabels, trackName, fontSize, textHeight, trackLabelOffset, x, }) {
|
|
35
|
-
const
|
|
36
|
-
if (trackLabels === '
|
|
35
|
+
const palette = usePalette();
|
|
36
|
+
if (trackLabels === 'hidden') {
|
|
37
37
|
return null;
|
|
38
38
|
}
|
|
39
39
|
const pos = labelPosition({
|
|
@@ -43,5 +43,14 @@ export default function SVGTrackLabel({ trackLabels, trackName, fontSize, textHe
|
|
|
43
43
|
fontSize,
|
|
44
44
|
x,
|
|
45
45
|
});
|
|
46
|
-
|
|
46
|
+
// 'overlapping' prints over the track's own data, which the on-screen label
|
|
47
|
+
// clears with a paper box, so it carries the halo text over a plot needs
|
|
48
|
+
const halo = trackLabels === 'overlapping'
|
|
49
|
+
? {
|
|
50
|
+
stroke: stripAlpha(palette.background.paper),
|
|
51
|
+
strokeWidth: 3,
|
|
52
|
+
paintOrder: 'stroke',
|
|
53
|
+
}
|
|
54
|
+
: {};
|
|
55
|
+
return (_jsx("text", { x: pos.x, y: pos.y, textAnchor: pos.textAnchor, fontSize: fontSize, fill: stripAlpha(palette.text.primary), ...halo, children: trackName }));
|
|
47
56
|
}
|
|
@@ -7,9 +7,9 @@ export default function SVGTracks({ displayResults, model, textHeight, fontSize,
|
|
|
7
7
|
model: LGV;
|
|
8
8
|
textHeight: number;
|
|
9
9
|
fontSize: number;
|
|
10
|
-
trackLabels
|
|
11
|
-
trackLabelOffset
|
|
12
|
-
leftBuffer
|
|
13
|
-
legendWidth
|
|
10
|
+
trackLabels: TrackLabelMode;
|
|
11
|
+
trackLabelOffset: number;
|
|
12
|
+
leftBuffer: number;
|
|
13
|
+
legendWidth: number;
|
|
14
14
|
}): import("react").JSX.Element;
|
|
15
15
|
export {};
|
|
@@ -3,19 +3,20 @@ import { SvgClipRect } from '@jbrowse/core/svg/SvgExport';
|
|
|
3
3
|
import { svgNodeId } from '@jbrowse/core/svg/svgId';
|
|
4
4
|
import { svgTrackName } from '@jbrowse/core/svg/trackNames';
|
|
5
5
|
import { getSession } from '@jbrowse/core/util';
|
|
6
|
+
import { exportsTooLargeNote } from '@jbrowse/display-kit/renderDisplaySvg';
|
|
6
7
|
import SVGRegionSeparators from './SVGRegionSeparators.js';
|
|
7
8
|
import SVGTrackLabel from './SVGTrackLabel.js';
|
|
8
|
-
import { labelOffset, trackBoxOffsets } from './util.js';
|
|
9
|
-
export default function SVGTracks({ displayResults, model, textHeight, fontSize, trackLabels
|
|
9
|
+
import { labelOffset, trackBoxOffsets, trackLabelMode } from './util.js';
|
|
10
|
+
export default function SVGTracks({ displayResults, model, textHeight, fontSize, trackLabels, trackLabelOffset, leftBuffer, legendWidth, }) {
|
|
10
11
|
const session = getSession(model);
|
|
11
|
-
const textOffset = labelOffset(trackLabels, textHeight);
|
|
12
12
|
const x = Math.max(-model.offsetPx, 0);
|
|
13
|
-
const offsets = trackBoxOffsets(displayResults.map(r => r.track),
|
|
13
|
+
const offsets = trackBoxOffsets(displayResults.map(r => r.track), trackLabels, textHeight);
|
|
14
14
|
return (_jsx(_Fragment, { children: displayResults.map(({ track, result }, i) => {
|
|
15
15
|
const conf = track.configuration;
|
|
16
16
|
const trackName = svgTrackName(track, session);
|
|
17
17
|
const display = track.displays[0];
|
|
18
18
|
const currentOffset = offsets[i];
|
|
19
|
-
|
|
19
|
+
const textOffset = labelOffset(track, trackLabels, textHeight);
|
|
20
|
+
return (_jsxs("g", { transform: `translate(0 ${currentOffset})`, children: [_jsx(SvgClipRect, { id: `track-clip-${svgNodeId(model)}-${conf.trackId}`, x: -leftBuffer, y: textOffset, width: model.width + trackLabelOffset + leftBuffer + legendWidth, height: display.height, children: _jsxs("g", { transform: `translate(${trackLabelOffset} ${textOffset})`, children: [result, exportsTooLargeNote(display) ? null : (_jsx(SVGRegionSeparators, { model: model, height: display.height }))] }) }), _jsx(SVGTrackLabel, { trackName: trackName, fontSize: fontSize, textHeight: textHeight, trackLabels: trackLabelMode(track, trackLabels), trackLabelOffset: trackLabelOffset, x: x })] }, conf.trackId));
|
|
20
21
|
}) }));
|
|
21
22
|
}
|
|
@@ -13,6 +13,6 @@ export default function SVGView({ view, displayResults, header, fontSize, textHe
|
|
|
13
13
|
contentTop: number;
|
|
14
14
|
tracksHeight: number;
|
|
15
15
|
showGridlines: boolean;
|
|
16
|
-
leftBuffer
|
|
16
|
+
leftBuffer: number;
|
|
17
17
|
legendWidth?: number;
|
|
18
18
|
}): import("react").JSX.Element;
|
|
@@ -8,6 +8,6 @@ import SVGTracks from './SVGTracks.js';
|
|
|
8
8
|
// linear-synteny and breakpoint-split rows — so their per-view layout can't
|
|
9
9
|
// drift. It did drift while the standalone export kept its own copy of these
|
|
10
10
|
// four groups: only that copy widened the per-track clip by `legendWidth`.
|
|
11
|
-
export default function SVGView({ view, displayResults, header, fontSize, textHeight, trackLabels, trackLabelOffset, contentTop, tracksHeight, showGridlines, leftBuffer
|
|
11
|
+
export default function SVGView({ view, displayResults, header, fontSize, textHeight, trackLabels, trackLabelOffset, contentTop, tracksHeight, showGridlines, leftBuffer, legendWidth = 0, }) {
|
|
12
12
|
return (_jsxs(_Fragment, { children: [_jsx("g", { transform: `translate(${trackLabelOffset})`, children: header }), showGridlines ? (_jsx("g", { transform: `translate(${trackLabelOffset} ${contentTop})`, children: _jsx(SVGGridlines, { model: view, height: tracksHeight }) })) : null, _jsx("g", { transform: `translate(0 ${contentTop})`, children: _jsx(SVGTracks, { textHeight: textHeight, trackLabels: trackLabels, fontSize: fontSize, model: view, displayResults: displayResults, trackLabelOffset: trackLabelOffset, leftBuffer: leftBuffer, legendWidth: legendWidth }) }), _jsx("g", { transform: `translate(${trackLabelOffset} ${contentTop})`, children: _jsx(SVGHighlightsOverlay, { model: view, tracksHeight: tracksHeight }) })] }));
|
|
13
13
|
}
|
|
@@ -1,25 +1,25 @@
|
|
|
1
1
|
import { useEffect, useRef } from 'react';
|
|
2
2
|
import { svgNodeId } from '@jbrowse/core/svg/svgId';
|
|
3
|
+
import { contractReportsOn, reportContractViolation, } from '@jbrowse/render-core/contractReports';
|
|
3
4
|
/**
|
|
4
|
-
*
|
|
5
|
-
* No-ops in production.
|
|
5
|
+
* Checks that a live SVG figure is being used the way it can work.
|
|
6
6
|
*
|
|
7
7
|
* `useViewSvgFigure` publishes a figure whose two halves are drawn at different
|
|
8
8
|
* moments and only agree by construction, and both ways of breaking that are
|
|
9
9
|
* invisible in the output: the drawing is still a plausible picture of
|
|
10
10
|
* somewhere, just not of one moment or not with its own clips. So the run
|
|
11
11
|
* reports instead, through the `[jbrowse <family> contract]` channel every
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
12
|
+
* check in the tree uses — `config/jest/contractGate.js` fails the test that
|
|
13
|
+
* collects one, and ARCHITECTURAL_LIMITS.md §"Ordering is the contract" is the
|
|
14
|
+
* register. `figure` rather than `svg-figure` because the gate's family pattern
|
|
15
|
+
* is `\w+`, which a hyphen is not.
|
|
16
16
|
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
17
|
+
* Reported and never thrown, as the rest of that family: a figure is chrome a
|
|
18
|
+
* host renders beside its own, and taking the host's tree down over a drawing
|
|
19
|
+
* is worse than the drawing being wrong.
|
|
20
20
|
*/
|
|
21
21
|
function report(message) {
|
|
22
|
-
|
|
22
|
+
reportContractViolation('figure', message);
|
|
23
23
|
}
|
|
24
24
|
// Live figures per view, keyed the way their SVG ids are. A count rather than a
|
|
25
25
|
// flag: the report belongs on the second mount, and the first has to survive the
|
|
@@ -43,9 +43,6 @@ const figuresPerView = new Map();
|
|
|
43
43
|
*/
|
|
44
44
|
export function useOneFigurePerView(view) {
|
|
45
45
|
useEffect(() => {
|
|
46
|
-
if (process.env.NODE_ENV === 'production') {
|
|
47
|
-
return undefined;
|
|
48
|
-
}
|
|
49
46
|
// captured, not re-derived on cleanup: by then the view may have left the
|
|
50
47
|
// tree, and `svgNodeId` walks it
|
|
51
48
|
const key = svgNodeId(view);
|
|
@@ -99,7 +96,10 @@ export function useFrozenFigureContract(ref, snapshot) {
|
|
|
99
96
|
rendered.current = snapshot;
|
|
100
97
|
useEffect(() => {
|
|
101
98
|
const el = ref.current;
|
|
102
|
-
|
|
99
|
+
// the one check here with a cost of its own — a `MutationObserver` over the
|
|
100
|
+
// whole figure subtree — so unlike its neighbour it asks whether the
|
|
101
|
+
// channel is armed rather than running and reporting into it
|
|
102
|
+
if (!contractReportsOn() || !el) {
|
|
103
103
|
return undefined;
|
|
104
104
|
}
|
|
105
105
|
const observer = new MutationObserver(() => {
|
|
@@ -59,7 +59,7 @@ export async function renderViewTracks({ view, opts, theme, textHeight, trackLab
|
|
|
59
59
|
return {
|
|
60
60
|
tracks,
|
|
61
61
|
displayResults,
|
|
62
|
-
tracksHeight: totalHeight(tracks,
|
|
62
|
+
tracksHeight: totalHeight(tracks, trackLabels, textHeight),
|
|
63
63
|
legendWidth,
|
|
64
64
|
skippedTracks,
|
|
65
65
|
};
|