@jbrowse/plugin-breakpoint-split-view 3.7.0 → 4.0.1
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/BreakpointAlignmentsFeatureDetail/BreakpointAlignmentsFeatureDetail.js +1 -1
- package/esm/BreakpointAlignmentsFeatureDetail/index.js +2 -2
- package/esm/BreakpointGetFeatures/BreakpointGetFeatures.d.ts +51 -0
- package/esm/BreakpointGetFeatures/BreakpointGetFeatures.js +57 -0
- package/esm/BreakpointGetFeatures/index.d.ts +2 -0
- package/esm/BreakpointGetFeatures/index.js +4 -0
- package/esm/BreakpointSplitView/BreakpointSplitView.js +33 -47
- package/esm/BreakpointSplitView/components/AlignmentConnections.d.ts +2 -7
- package/esm/BreakpointSplitView/components/AlignmentConnections.js +20 -25
- package/esm/BreakpointSplitView/components/Breakends.d.ts +2 -7
- package/esm/BreakpointSplitView/components/Breakends.js +28 -59
- package/esm/BreakpointSplitView/components/BreakpointSplitView.d.ts +1 -1
- package/esm/BreakpointSplitView/components/BreakpointSplitView.js +19 -6
- package/esm/BreakpointSplitView/components/BreakpointSplitViewOverlay.d.ts +1 -1
- package/esm/BreakpointSplitView/components/BreakpointSplitViewOverlay.js +3 -3
- package/esm/BreakpointSplitView/components/ExportSvgDialog.d.ts +1 -1
- package/esm/BreakpointSplitView/components/ExportSvgDialog.js +5 -1
- package/esm/BreakpointSplitView/components/Header.d.ts +2 -2
- package/esm/BreakpointSplitView/components/Header.js +4 -4
- package/esm/BreakpointSplitView/components/HeaderSearchBoxes.js +2 -2
- package/esm/BreakpointSplitView/components/Overlay.d.ts +1 -1
- package/esm/BreakpointSplitView/components/Overlay.js +8 -11
- package/esm/BreakpointSplitView/components/PairedFeatures.d.ts +2 -7
- package/esm/BreakpointSplitView/components/PairedFeatures.js +22 -47
- package/esm/BreakpointSplitView/components/Rubberband.d.ts +6 -0
- package/esm/BreakpointSplitView/components/Rubberband.js +27 -0
- package/esm/BreakpointSplitView/components/RubberbandSpan.d.ts +15 -0
- package/esm/BreakpointSplitView/components/RubberbandSpan.js +30 -0
- package/esm/BreakpointSplitView/components/RubberbandTooltip.d.ts +5 -0
- package/esm/BreakpointSplitView/components/RubberbandTooltip.js +17 -0
- package/esm/BreakpointSplitView/components/Translocations.d.ts +2 -7
- package/esm/BreakpointSplitView/components/Translocations.js +23 -58
- package/esm/BreakpointSplitView/components/VerticalGuide.d.ts +6 -0
- package/esm/BreakpointSplitView/components/VerticalGuide.js +24 -0
- package/esm/BreakpointSplitView/components/overlayUtils.d.ts +24 -0
- package/esm/BreakpointSplitView/components/overlayUtils.js +47 -0
- package/esm/BreakpointSplitView/components/rubberbandUtil.d.ts +4 -0
- package/esm/BreakpointSplitView/components/rubberbandUtil.js +3 -0
- package/esm/BreakpointSplitView/components/useRangeSelect.d.ts +59 -0
- package/esm/BreakpointSplitView/components/useRangeSelect.js +121 -0
- package/esm/BreakpointSplitView/components/util.js +1 -2
- package/esm/BreakpointSplitView/getClip.js +4 -4
- package/esm/BreakpointSplitView/index.js +2 -2
- package/esm/BreakpointSplitView/model.d.ts +402 -117
- package/esm/BreakpointSplitView/model.js +105 -41
- package/esm/BreakpointSplitView/svgcomponents/SVGBreakpointSplitView.d.ts +2 -2
- package/esm/BreakpointSplitView/svgcomponents/SVGBreakpointSplitView.js +8 -8
- package/esm/BreakpointSplitView/types.d.ts +8 -0
- package/esm/BreakpointSplitView/util.d.ts +1 -1
- package/esm/BreakpointSplitView/util.js +8 -17
- package/esm/LaunchBreakpointSplitView/index.d.ts +2 -0
- package/esm/LaunchBreakpointSplitView/index.js +12 -0
- package/esm/index.js +7 -6
- package/package.json +28 -34
- package/dist/BreakpointAlignmentsFeatureDetail/BreakpointAlignmentsFeatureDetail.d.ts +0 -10
- package/dist/BreakpointAlignmentsFeatureDetail/BreakpointAlignmentsFeatureDetail.js +0 -12
- package/dist/BreakpointAlignmentsFeatureDetail/index.d.ts +0 -2
- package/dist/BreakpointAlignmentsFeatureDetail/index.js +0 -67
- package/dist/BreakpointSplitView/BreakpointSplitView.d.ts +0 -34
- package/dist/BreakpointSplitView/BreakpointSplitView.js +0 -84
- package/dist/BreakpointSplitView/components/AlignmentConnections.d.ts +0 -8
- package/dist/BreakpointSplitView/components/AlignmentConnections.js +0 -133
- package/dist/BreakpointSplitView/components/Breakends.d.ts +0 -8
- package/dist/BreakpointSplitView/components/Breakends.js +0 -95
- package/dist/BreakpointSplitView/components/BreakpointSplitView.d.ts +0 -5
- package/dist/BreakpointSplitView/components/BreakpointSplitView.js +0 -46
- package/dist/BreakpointSplitView/components/BreakpointSplitViewOverlay.d.ts +0 -5
- package/dist/BreakpointSplitView/components/BreakpointSplitViewOverlay.js +0 -33
- package/dist/BreakpointSplitView/components/ExportSvgDialog.d.ts +0 -7
- package/dist/BreakpointSplitView/components/ExportSvgDialog.js +0 -57
- package/dist/BreakpointSplitView/components/Header.d.ts +0 -5
- package/dist/BreakpointSplitView/components/Header.js +0 -52
- package/dist/BreakpointSplitView/components/HeaderSearchBoxes.d.ts +0 -5
- package/dist/BreakpointSplitView/components/HeaderSearchBoxes.js +0 -24
- package/dist/BreakpointSplitView/components/Overlay.d.ts +0 -8
- package/dist/BreakpointSplitView/components/Overlay.js +0 -27
- package/dist/BreakpointSplitView/components/PairedFeatures.d.ts +0 -8
- package/dist/BreakpointSplitView/components/PairedFeatures.js +0 -75
- package/dist/BreakpointSplitView/components/Translocations.d.ts +0 -8
- package/dist/BreakpointSplitView/components/Translocations.js +0 -99
- package/dist/BreakpointSplitView/components/getOrientationColor.d.ts +0 -41
- package/dist/BreakpointSplitView/components/getOrientationColor.js +0 -103
- package/dist/BreakpointSplitView/components/util.d.ts +0 -8
- package/dist/BreakpointSplitView/components/util.js +0 -142
- package/dist/BreakpointSplitView/getClip.d.ts +0 -1
- package/dist/BreakpointSplitView/getClip.js +0 -10
- package/dist/BreakpointSplitView/index.d.ts +0 -2
- package/dist/BreakpointSplitView/index.js +0 -52
- package/dist/BreakpointSplitView/model.d.ts +0 -344
- package/dist/BreakpointSplitView/model.js +0 -256
- package/dist/BreakpointSplitView/svgcomponents/SVGBackground.d.ts +0 -5
- package/dist/BreakpointSplitView/svgcomponents/SVGBackground.js +0 -10
- package/dist/BreakpointSplitView/svgcomponents/SVGBreakpointSplitView.d.ts +0 -5
- package/dist/BreakpointSplitView/svgcomponents/SVGBreakpointSplitView.js +0 -41
- package/dist/BreakpointSplitView/svgcomponents/util.d.ts +0 -4
- package/dist/BreakpointSplitView/svgcomponents/util.js +0 -18
- package/dist/BreakpointSplitView/types.d.ts +0 -22
- package/dist/BreakpointSplitView/types.js +0 -2
- package/dist/BreakpointSplitView/util.d.ts +0 -28
- package/dist/BreakpointSplitView/util.js +0 -67
- package/dist/index.d.ts +0 -7
- package/dist/index.js +0 -20
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useState } from 'react';
|
|
3
3
|
import CascadingMenuButton from '@jbrowse/core/ui/CascadingMenuButton';
|
|
4
|
+
import { makeStyles } from '@jbrowse/core/util/tss-react';
|
|
4
5
|
import MoreVertIcon from '@mui/icons-material/MoreVert';
|
|
5
6
|
import SearchIcon from '@mui/icons-material/Search';
|
|
6
7
|
import { FormGroup } from '@mui/material';
|
|
7
8
|
import { observer } from 'mobx-react';
|
|
8
|
-
import
|
|
9
|
-
import HeaderSearchBoxes from './HeaderSearchBoxes';
|
|
9
|
+
import HeaderSearchBoxes from "./HeaderSearchBoxes.js";
|
|
10
10
|
const useStyles = makeStyles()({
|
|
11
11
|
inline: {
|
|
12
12
|
display: 'inline-flex',
|
|
13
13
|
},
|
|
14
14
|
});
|
|
15
|
-
const Header = observer(function ({ model }) {
|
|
15
|
+
const Header = observer(function Header({ model, }) {
|
|
16
16
|
const { classes } = useStyles();
|
|
17
17
|
const { views } = model;
|
|
18
18
|
const [showSearchBoxes, setShowSearchBoxes] = useState(views.length <= 3);
|
|
19
19
|
const [sideBySide, setSideBySide] = useState(views.length <= 3);
|
|
20
|
-
return (_jsxs(FormGroup, { row: true, children: [_jsx(CascadingMenuButton, { menuItems: model.menuItems(), children: _jsx(MoreVertIcon, {}) }), _jsx(CascadingMenuButton, { menuItems: [
|
|
20
|
+
return (_jsxs(FormGroup, { row: true, children: [_jsx(CascadingMenuButton, { menuItems: () => model.menuItems(), children: _jsx(MoreVertIcon, {}) }), _jsx(CascadingMenuButton, { menuItems: [
|
|
21
21
|
{
|
|
22
22
|
label: 'Show search boxes',
|
|
23
23
|
type: 'checkbox',
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { getBpDisplayStr } from '@jbrowse/core/util';
|
|
3
|
+
import { makeStyles } from '@jbrowse/core/util/tss-react';
|
|
3
4
|
import { SearchBox } from '@jbrowse/plugin-linear-genome-view';
|
|
4
5
|
import { Typography } from '@mui/material';
|
|
5
6
|
import { observer } from 'mobx-react';
|
|
6
|
-
import { makeStyles } from 'tss-react/mui';
|
|
7
7
|
const useStyles = makeStyles()(() => ({
|
|
8
8
|
bp: {
|
|
9
9
|
display: 'flex',
|
|
@@ -14,7 +14,7 @@ const useStyles = makeStyles()(() => ({
|
|
|
14
14
|
display: 'flex',
|
|
15
15
|
},
|
|
16
16
|
}));
|
|
17
|
-
const HeaderSearchBoxes = observer(function ({ view, }) {
|
|
17
|
+
const HeaderSearchBoxes = observer(function HeaderSearchBoxes({ view, }) {
|
|
18
18
|
const { classes } = useStyles();
|
|
19
19
|
const { assemblyDisplayNames, coarseTotalBp } = view;
|
|
20
20
|
return (_jsxs("span", { className: classes.searchBox, children: [_jsx(SearchBox, { model: view, showHelp: false }), _jsxs(Typography, { variant: "body2", color: "textSecondary", className: classes.bp, children: [assemblyDisplayNames.join(','), " ", getBpDisplayStr(coarseTotalBp)] })] }));
|
|
@@ -1,22 +1,19 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { observer } from 'mobx-react';
|
|
3
|
-
import AlignmentConnections from
|
|
4
|
-
import Breakends from
|
|
5
|
-
import PairedFeatures from
|
|
6
|
-
import Translocations from
|
|
7
|
-
const Overlay = observer(function (props) {
|
|
8
|
-
var _a;
|
|
3
|
+
import AlignmentConnections from "./AlignmentConnections.js";
|
|
4
|
+
import Breakends from "./Breakends.js";
|
|
5
|
+
import PairedFeatures from "./PairedFeatures.js";
|
|
6
|
+
import Translocations from "./Translocations.js";
|
|
7
|
+
const Overlay = observer(function Overlay(props) {
|
|
9
8
|
const { model, trackId } = props;
|
|
10
9
|
const tracks = model.getMatchedTracks(trackId);
|
|
11
|
-
const type =
|
|
10
|
+
const type = tracks[0]?.type;
|
|
12
11
|
if (type === 'AlignmentsTrack') {
|
|
13
12
|
return _jsx(AlignmentConnections, { ...props });
|
|
14
13
|
}
|
|
15
|
-
|
|
14
|
+
if (type === 'VariantTrack') {
|
|
16
15
|
return model.hasTranslocations(trackId) ? (_jsx(Translocations, { ...props })) : model.hasPairedFeatures(trackId) ? (_jsx(PairedFeatures, { ...props })) : (_jsx(Breakends, { ...props }));
|
|
17
16
|
}
|
|
18
|
-
|
|
19
|
-
return null;
|
|
20
|
-
}
|
|
17
|
+
return null;
|
|
21
18
|
});
|
|
22
19
|
export default Overlay;
|
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
declare const PairedFeatures: ({ model, trackId, parentRef
|
|
3
|
-
model: BreakpointViewModel;
|
|
4
|
-
trackId: string;
|
|
5
|
-
parentRef: React.RefObject<SVGSVGElement | null>;
|
|
6
|
-
getTrackYPosOverride?: (trackId: string, level: number) => number;
|
|
7
|
-
}) => import("react/jsx-runtime").JSX.Element | null;
|
|
1
|
+
import type { OverlayProps } from './overlayUtils.tsx';
|
|
2
|
+
declare const PairedFeatures: ({ model, trackId, parentRef, getTrackYPosOverride, }: OverlayProps) => import("react/jsx-runtime").JSX.Element | null;
|
|
8
3
|
export default PairedFeatures;
|
|
@@ -1,71 +1,46 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { useMemo, useState } from 'react';
|
|
3
3
|
import { getSession } from '@jbrowse/core/util';
|
|
4
|
+
import { getSnapshot } from '@jbrowse/mobx-state-tree';
|
|
4
5
|
import { observer } from 'mobx-react';
|
|
5
|
-
import {
|
|
6
|
-
import { getMatchedPairedFeatures } from
|
|
7
|
-
import { getPxFromCoordinate, useNextFrame, yPos } from
|
|
8
|
-
const
|
|
9
|
-
const PairedFeatures = observer(function ({ model, trackId, parentRef: ref, getTrackYPosOverride, }) {
|
|
6
|
+
import { LEFT, buildSimplePath, createMouseHandlers, getCanonicalRefs, getTestId, getYOffset, } from "./overlayUtils.js";
|
|
7
|
+
import { getMatchedPairedFeatures } from "./util.js";
|
|
8
|
+
import { getPxFromCoordinate, useNextFrame, yPos } from "../util.js";
|
|
9
|
+
const PairedFeatures = observer(function PairedFeatures({ model, trackId, parentRef, getTrackYPosOverride, }) {
|
|
10
10
|
const { interactiveOverlay, views } = model;
|
|
11
11
|
const session = getSession(model);
|
|
12
12
|
const { assemblyManager } = session;
|
|
13
|
-
const totalFeatures = model.getTrackFeatures(trackId);
|
|
14
|
-
const layoutMatches = useMemo(() => model.getMatchedFeaturesInLayout(trackId, getMatchedPairedFeatures(totalFeatures)), [totalFeatures, trackId, model]);
|
|
15
|
-
const [mouseoverElt, setMouseoverElt] = useState();
|
|
16
13
|
const snap = getSnapshot(model);
|
|
14
|
+
const v0 = views[0];
|
|
15
|
+
const assembly = v0 ? assemblyManager.get(v0.assemblyNames[0]) : undefined;
|
|
17
16
|
useNextFrame(snap);
|
|
18
|
-
const
|
|
17
|
+
const totalFeatures = model.getTrackFeatures(trackId);
|
|
18
|
+
const layoutMatches = useMemo(() => {
|
|
19
|
+
const matchedFeatures = getMatchedPairedFeatures(totalFeatures);
|
|
20
|
+
return model.getMatchedFeaturesInLayout(trackId, matchedFeatures);
|
|
21
|
+
}, [totalFeatures, trackId, model]);
|
|
22
|
+
const [mouseoverElt, setMouseoverElt] = useState();
|
|
23
|
+
const yOffset = getYOffset(parentRef);
|
|
24
|
+
const tracks = views.map(v => v.getTrack(trackId));
|
|
19
25
|
if (!assembly) {
|
|
20
26
|
return null;
|
|
21
27
|
}
|
|
22
|
-
|
|
23
|
-
if (ref.current) {
|
|
24
|
-
const rect = ref.current.getBoundingClientRect();
|
|
25
|
-
yoff = rect.top;
|
|
26
|
-
}
|
|
27
|
-
return (_jsx("g", { stroke: "green", strokeWidth: 5, fill: "none", "data-testid": layoutMatches.length ? `${trackId}-loaded` : trackId, children: layoutMatches.map(chunk => {
|
|
28
|
+
return (_jsx("g", { stroke: "green", strokeWidth: 5, fill: "none", "data-testid": getTestId(trackId, layoutMatches.length > 0), children: layoutMatches.map(chunk => {
|
|
28
29
|
const ret = [];
|
|
29
30
|
for (let i = 0; i < chunk.length - 1; i += 1) {
|
|
30
31
|
const { layout: c1, feature: f1, level: level1 } = chunk[i];
|
|
31
32
|
const { layout: c2, feature: f2, level: level2 } = chunk[i + 1];
|
|
32
33
|
const id = f1.id();
|
|
33
|
-
|
|
34
|
-
return null;
|
|
35
|
-
}
|
|
36
|
-
const f1origref = f1.get('refName');
|
|
37
|
-
const f2origref = f2.get('refName');
|
|
38
|
-
const f1ref = assembly.getCanonicalRefName(f1origref);
|
|
39
|
-
const f2ref = assembly.getCanonicalRefName(f2origref);
|
|
40
|
-
if (!f1ref || !f2ref) {
|
|
41
|
-
throw new Error(`unable to find ref for ${f1ref || f2ref}`);
|
|
42
|
-
}
|
|
34
|
+
const { f1ref, f2ref } = getCanonicalRefs(assembly, f1.get('refName'), f2.get('refName'));
|
|
43
35
|
const x1 = getPxFromCoordinate(views[level1], f1ref, c1[LEFT]);
|
|
44
36
|
const x2 = getPxFromCoordinate(views[level2], f2ref, c2[LEFT]);
|
|
45
|
-
const tracks = views.map(v => v.getTrack(trackId));
|
|
46
37
|
const y1 = yPos(trackId, level1, views, tracks, c1, getTrackYPosOverride) -
|
|
47
|
-
|
|
38
|
+
yOffset;
|
|
48
39
|
const y2 = yPos(trackId, level2, views, tracks, c2, getTrackYPosOverride) -
|
|
49
|
-
|
|
50
|
-
const path =
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
y1,
|
|
54
|
-
'L',
|
|
55
|
-
x2,
|
|
56
|
-
y2,
|
|
57
|
-
].join(' ');
|
|
58
|
-
ret.push(_jsx("path", { d: path, "data-testid": "r2", pointerEvents: interactiveOverlay ? 'auto' : undefined, strokeWidth: id === mouseoverElt ? 10 : 5, onClick: () => {
|
|
59
|
-
var _a, _b, _c;
|
|
60
|
-
const featureWidget = (_a = session.addWidget) === null || _a === void 0 ? void 0 : _a.call(session, 'VariantFeatureWidget', 'variantFeature', {
|
|
61
|
-
featureData: (_b = totalFeatures.get(id)) === null || _b === void 0 ? void 0 : _b.toJSON(),
|
|
62
|
-
});
|
|
63
|
-
(_c = session.showWidget) === null || _c === void 0 ? void 0 : _c.call(session, featureWidget);
|
|
64
|
-
}, onMouseOver: () => {
|
|
65
|
-
setMouseoverElt(id);
|
|
66
|
-
}, onMouseOut: () => {
|
|
67
|
-
setMouseoverElt(undefined);
|
|
68
|
-
} }, JSON.stringify(path)));
|
|
40
|
+
yOffset;
|
|
41
|
+
const path = buildSimplePath(x1, y1, x2, y2);
|
|
42
|
+
const mouseHandlers = createMouseHandlers(id, setMouseoverElt, session, 'VariantFeatureWidget', 'variantFeature', totalFeatures.get(id)?.toJSON());
|
|
43
|
+
ret.push(_jsx("path", { d: path, "data-testid": "r2", pointerEvents: interactiveOverlay ? 'auto' : undefined, strokeWidth: id === mouseoverElt ? 10 : 5, ...mouseHandlers }, JSON.stringify(path)));
|
|
69
44
|
}
|
|
70
45
|
return ret;
|
|
71
46
|
}) }));
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useRef } from 'react';
|
|
3
|
+
import { Menu } from '@jbrowse/core/ui';
|
|
4
|
+
import { makeStyles } from '@jbrowse/core/util/tss-react';
|
|
5
|
+
import { observer } from 'mobx-react';
|
|
6
|
+
import RubberbandSpan from "./RubberbandSpan.js";
|
|
7
|
+
import VerticalGuide from "./VerticalGuide.js";
|
|
8
|
+
import { useRangeSelect } from "./useRangeSelect.js";
|
|
9
|
+
const useStyles = makeStyles()({
|
|
10
|
+
rubberbandControl: {
|
|
11
|
+
cursor: 'crosshair',
|
|
12
|
+
width: '100%',
|
|
13
|
+
minHeight: 8,
|
|
14
|
+
position: 'relative',
|
|
15
|
+
zIndex: 900,
|
|
16
|
+
},
|
|
17
|
+
});
|
|
18
|
+
const Rubberband = observer(function Rubberband({ model, ControlComponent = _jsx("div", {}), }) {
|
|
19
|
+
const ref = useRef(null);
|
|
20
|
+
const { classes } = useStyles();
|
|
21
|
+
const { guideX, rubberbandOn, leftBpOffset, rightBpOffset, numOfBpSelected, width, left, anchorPosition, open, handleMenuItemClick, handleClose, mouseMove, mouseDown, mouseOut, } = useRangeSelect(ref, model);
|
|
22
|
+
return (_jsxs(_Fragment, { children: [guideX !== undefined ? (_jsx(VerticalGuide, { model: model, coordX: guideX })) : rubberbandOn ? (_jsx(RubberbandSpan, { leftBpOffset: leftBpOffset, rightBpOffset: rightBpOffset, numOfBpSelected: numOfBpSelected, width: width, left: left })) : null, anchorPosition ? (_jsx(Menu, { anchorReference: "anchorPosition", anchorPosition: {
|
|
23
|
+
left: anchorPosition.clientX,
|
|
24
|
+
top: anchorPosition.clientY,
|
|
25
|
+
}, onMenuItemClick: handleMenuItemClick, open: open, onClose: handleClose, menuItems: model.rubberBandMenuItems() })) : null, _jsx("div", { "data-testid": "rubberband_controls", className: classes.rubberbandControl, ref: ref, onMouseDown: mouseDown, onMouseMove: mouseMove, onMouseOut: mouseOut, children: ControlComponent })] }));
|
|
26
|
+
});
|
|
27
|
+
export default Rubberband;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
interface Offset {
|
|
2
|
+
coord: number;
|
|
3
|
+
refName?: string;
|
|
4
|
+
oob?: boolean;
|
|
5
|
+
}
|
|
6
|
+
export default function RubberbandSpan({ leftBpOffset, rightBpOffset, numOfBpSelected, left, width, top, sticky, }: {
|
|
7
|
+
leftBpOffset: Offset[];
|
|
8
|
+
rightBpOffset: Offset[];
|
|
9
|
+
numOfBpSelected?: number[];
|
|
10
|
+
left: number;
|
|
11
|
+
width: number;
|
|
12
|
+
top?: number;
|
|
13
|
+
sticky?: boolean;
|
|
14
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
15
|
+
export {};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState } from 'react';
|
|
3
|
+
import { getBpDisplayStr, stringify } from '@jbrowse/core/util';
|
|
4
|
+
import { makeStyles } from '@jbrowse/core/util/tss-react';
|
|
5
|
+
import { Typography, alpha } from '@mui/material';
|
|
6
|
+
import RubberbandTooltip from "./RubberbandTooltip.js";
|
|
7
|
+
const useStyles = makeStyles()(theme => {
|
|
8
|
+
const { tertiary } = theme.palette;
|
|
9
|
+
const background = alpha(tertiary.light, 0.7);
|
|
10
|
+
return {
|
|
11
|
+
rubberband: {
|
|
12
|
+
height: '100%',
|
|
13
|
+
background,
|
|
14
|
+
position: 'absolute',
|
|
15
|
+
zIndex: 830,
|
|
16
|
+
textAlign: 'center',
|
|
17
|
+
},
|
|
18
|
+
rubberbandText: {
|
|
19
|
+
color: theme.palette.tertiary.contrastText,
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
});
|
|
23
|
+
export default function RubberbandSpan({ leftBpOffset, rightBpOffset, numOfBpSelected, left, width, top = 0, sticky = false, }) {
|
|
24
|
+
const { classes } = useStyles();
|
|
25
|
+
const [anchorEl, setAnchorEl] = useState(null);
|
|
26
|
+
return (_jsxs(_Fragment, { children: [anchorEl ? (_jsxs(_Fragment, { children: [_jsx(RubberbandTooltip, { side: "left", anchorEl: anchorEl, text: leftBpOffset.map((l, idx) => (_jsx("div", { children: stringify(l, true) }, `${JSON.stringify(l)}-left-${idx}`))) }), _jsx(RubberbandTooltip, { side: "right", anchorEl: anchorEl, text: rightBpOffset.map((l, idx) => (_jsx("div", { children: stringify(l, true) }, `${JSON.stringify(l)}-right-${idx}`))) })] })) : null, _jsx("div", { className: classes.rubberband, style: { left, width }, children: numOfBpSelected ? (_jsx(Typography, { ref: setAnchorEl, variant: "h6", className: classes.rubberbandText, style: {
|
|
27
|
+
top,
|
|
28
|
+
position: sticky ? 'sticky' : undefined,
|
|
29
|
+
}, children: numOfBpSelected.map((n, i) => (_jsx("div", { children: getBpDisplayStr(n) }, `bpSelectedRow-${i}`))) })) : null })] }));
|
|
30
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Tooltip } from '@mui/material';
|
|
3
|
+
export default function RubberbandTooltip({ anchorEl, side, text, }) {
|
|
4
|
+
return (_jsx(Tooltip, { title: text, open: true, placement: side === 'left' ? 'left-start' : 'right-start', slotProps: {
|
|
5
|
+
popper: {
|
|
6
|
+
anchorEl,
|
|
7
|
+
modifiers: [
|
|
8
|
+
{
|
|
9
|
+
name: 'offset',
|
|
10
|
+
options: {
|
|
11
|
+
offset: [-30, -10],
|
|
12
|
+
},
|
|
13
|
+
},
|
|
14
|
+
],
|
|
15
|
+
},
|
|
16
|
+
}, children: _jsx("span", {}) }));
|
|
17
|
+
}
|
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
declare const Translocations: ({ model, trackId, parentRef
|
|
3
|
-
model: BreakpointViewModel;
|
|
4
|
-
trackId: string;
|
|
5
|
-
parentRef: React.RefObject<SVGSVGElement | null>;
|
|
6
|
-
getTrackYPosOverride?: (trackId: string, level: number) => number;
|
|
7
|
-
}) => import("react/jsx-runtime").JSX.Element | null;
|
|
1
|
+
import type { OverlayProps } from './overlayUtils.tsx';
|
|
2
|
+
declare const Translocations: ({ model, trackId, parentRef, getTrackYPosOverride, }: OverlayProps) => import("react/jsx-runtime").JSX.Element | null;
|
|
8
3
|
export default Translocations;
|
|
@@ -1,57 +1,43 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { useMemo, useState } from 'react';
|
|
3
3
|
import { getSession } from '@jbrowse/core/util';
|
|
4
|
+
import { getSnapshot } from '@jbrowse/mobx-state-tree';
|
|
4
5
|
import { observer } from 'mobx-react';
|
|
5
|
-
import {
|
|
6
|
-
import { getMatchedTranslocationFeatures } from
|
|
7
|
-
import { getPxFromCoordinate, useNextFrame, yPos } from
|
|
8
|
-
const
|
|
9
|
-
function str(s) {
|
|
10
|
-
if (s === '+') {
|
|
11
|
-
return 1;
|
|
12
|
-
}
|
|
13
|
-
else if (s === '-') {
|
|
14
|
-
return -1;
|
|
15
|
-
}
|
|
16
|
-
else {
|
|
17
|
-
return 0;
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
const Translocations = observer(function ({ model, trackId, parentRef: ref, getTrackYPosOverride, }) {
|
|
6
|
+
import { LEFT, buildBreakpointPath, createMouseHandlers, getTestId, getYOffset, strandToSign, } from "./overlayUtils.js";
|
|
7
|
+
import { getMatchedTranslocationFeatures } from "./util.js";
|
|
8
|
+
import { getPxFromCoordinate, useNextFrame, yPos } from "../util.js";
|
|
9
|
+
const Translocations = observer(function Translocations({ model, trackId, parentRef, getTrackYPosOverride, }) {
|
|
21
10
|
const { interactiveOverlay, views } = model;
|
|
22
11
|
const session = getSession(model);
|
|
23
12
|
const { assemblyManager } = session;
|
|
24
|
-
const totalFeatures = model.getTrackFeatures(trackId);
|
|
25
|
-
const layoutMatches = useMemo(() => model.getMatchedFeaturesInLayout(trackId, getMatchedTranslocationFeatures(totalFeatures)), [totalFeatures, trackId, model]);
|
|
26
|
-
const [mouseoverElt, setMouseoverElt] = useState();
|
|
27
13
|
const snap = getSnapshot(model);
|
|
14
|
+
const v0 = views[0];
|
|
15
|
+
const assembly = v0 ? assemblyManager.get(v0.assemblyNames[0]) : undefined;
|
|
28
16
|
useNextFrame(snap);
|
|
29
|
-
const
|
|
17
|
+
const totalFeatures = model.getTrackFeatures(trackId);
|
|
18
|
+
const layoutMatches = useMemo(() => {
|
|
19
|
+
const matchedFeatures = getMatchedTranslocationFeatures(totalFeatures);
|
|
20
|
+
return model.getMatchedFeaturesInLayout(trackId, matchedFeatures);
|
|
21
|
+
}, [totalFeatures, trackId, model]);
|
|
22
|
+
const [mouseoverElt, setMouseoverElt] = useState();
|
|
23
|
+
const yOffset = getYOffset(parentRef);
|
|
24
|
+
const tracks = views.map(v => v.getTrack(trackId));
|
|
30
25
|
if (!assembly) {
|
|
31
26
|
return null;
|
|
32
27
|
}
|
|
33
|
-
let yOffset = 0;
|
|
34
|
-
if (ref.current) {
|
|
35
|
-
const rect = ref.current.getBoundingClientRect();
|
|
36
|
-
yOffset = rect.top;
|
|
37
|
-
}
|
|
38
28
|
if (views.length < 2) {
|
|
39
29
|
return null;
|
|
40
30
|
}
|
|
41
|
-
return (_jsx("g", { fill: "none", stroke: "green", strokeWidth: 5, "data-testid": layoutMatches.length
|
|
42
|
-
var _a, _b;
|
|
31
|
+
return (_jsx("g", { fill: "none", stroke: "green", strokeWidth: 5, "data-testid": getTestId(trackId, layoutMatches.length > 0), children: layoutMatches.map(chunk => {
|
|
43
32
|
const ret = [];
|
|
44
33
|
for (const { layout: c1, feature: f1, level: level1 } of chunk) {
|
|
45
34
|
const level2 = level1 === 0 ? 1 : 0;
|
|
46
35
|
const id = f1.id();
|
|
47
|
-
if (!c1) {
|
|
48
|
-
return null;
|
|
49
|
-
}
|
|
50
36
|
const info = f1.get('INFO');
|
|
51
37
|
const chr2 = info.CHR2[0];
|
|
52
38
|
const end2 = info.END[0];
|
|
53
|
-
const res =
|
|
54
|
-
const [myDirection, mateDirection] = res
|
|
39
|
+
const res = info.STRANDS?.[0]?.split('');
|
|
40
|
+
const [myDirection, mateDirection] = res ?? ['.', '.'];
|
|
55
41
|
const r = getPxFromCoordinate(views[level2], chr2, end2);
|
|
56
42
|
if (r) {
|
|
57
43
|
const c2 = [r, 0, r + 1, 0];
|
|
@@ -59,36 +45,15 @@ const Translocations = observer(function ({ model, trackId, parentRef: ref, getT
|
|
|
59
45
|
const x2 = r;
|
|
60
46
|
const reversed1 = views[level1].pxToBp(x1).reversed;
|
|
61
47
|
const reversed2 = views[level2].pxToBp(x2).reversed;
|
|
62
|
-
const tracks = views.map(v => v.getTrack(trackId));
|
|
63
48
|
const y1 = yPos(trackId, level1, views, tracks, c1, getTrackYPosOverride) -
|
|
64
49
|
yOffset;
|
|
65
50
|
const y2 = yPos(trackId, level2, views, tracks, c2, getTrackYPosOverride) -
|
|
66
51
|
yOffset;
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
x1,
|
|
73
|
-
y1,
|
|
74
|
-
'L',
|
|
75
|
-
x2,
|
|
76
|
-
y2,
|
|
77
|
-
'L',
|
|
78
|
-
x2 - 20 * str(mateDirection) * (reversed2 ? -1 : 1),
|
|
79
|
-
y2,
|
|
80
|
-
].join(' ');
|
|
81
|
-
ret.push(_jsx("path", { d: path, pointerEvents: interactiveOverlay ? 'auto' : undefined, strokeWidth: id === mouseoverElt ? 10 : 5, onClick: () => {
|
|
82
|
-
var _a, _b;
|
|
83
|
-
const featureWidget = (_a = session.addWidget) === null || _a === void 0 ? void 0 : _a.call(session, 'VariantFeatureWidget', 'variantFeature', {
|
|
84
|
-
featureData: (totalFeatures.get(id) || { toJSON: () => { } }).toJSON(),
|
|
85
|
-
});
|
|
86
|
-
(_b = session.showWidget) === null || _b === void 0 ? void 0 : _b.call(session, featureWidget);
|
|
87
|
-
}, onMouseOver: () => {
|
|
88
|
-
setMouseoverElt(id);
|
|
89
|
-
}, onMouseOut: () => {
|
|
90
|
-
setMouseoverElt(undefined);
|
|
91
|
-
} }, JSON.stringify(path)));
|
|
52
|
+
const x1Tick = x1 - 20 * strandToSign(myDirection) * (reversed1 ? -1 : 1);
|
|
53
|
+
const x2Tick = x2 - 20 * strandToSign(mateDirection) * (reversed2 ? -1 : 1);
|
|
54
|
+
const path = buildBreakpointPath(x1, y1, x2, y2, x1Tick, x2Tick);
|
|
55
|
+
const mouseHandlers = createMouseHandlers(id, setMouseoverElt, session, 'VariantFeatureWidget', 'variantFeature', totalFeatures.get(id)?.toJSON());
|
|
56
|
+
ret.push(_jsx("path", { d: path, pointerEvents: interactiveOverlay ? 'auto' : undefined, strokeWidth: id === mouseoverElt ? 10 : 5, ...mouseHandlers }, JSON.stringify(path)));
|
|
92
57
|
}
|
|
93
58
|
}
|
|
94
59
|
return ret;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { stringify } from '@jbrowse/core/util';
|
|
3
|
+
import { makeStyles } from '@jbrowse/core/util/tss-react';
|
|
4
|
+
import { Tooltip } from '@mui/material';
|
|
5
|
+
import { observer } from 'mobx-react';
|
|
6
|
+
const useStyles = makeStyles()({
|
|
7
|
+
guide: {
|
|
8
|
+
pointerEvents: 'none',
|
|
9
|
+
height: '100%',
|
|
10
|
+
width: 1,
|
|
11
|
+
position: 'absolute',
|
|
12
|
+
background: 'red',
|
|
13
|
+
zIndex: 1001,
|
|
14
|
+
},
|
|
15
|
+
});
|
|
16
|
+
const VerticalGuide = observer(function VerticalGuide({ model, coordX, }) {
|
|
17
|
+
const { classes } = useStyles();
|
|
18
|
+
return (_jsx(Tooltip, { open: true, placement: "top", title: model.views
|
|
19
|
+
.map(view => view.pxToBp(coordX))
|
|
20
|
+
.map((elt, idx) => (_jsx("div", { children: stringify(elt, true) }, [JSON.stringify(elt), idx].join('-')))), arrow: true, children: _jsx("div", { className: classes.guide, style: {
|
|
21
|
+
left: coordX,
|
|
22
|
+
} }) }));
|
|
23
|
+
});
|
|
24
|
+
export default VerticalGuide;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { BreakpointViewModel } from '../model.ts';
|
|
2
|
+
import type { Assembly } from '@jbrowse/core/assemblyManager/assembly';
|
|
3
|
+
import type { getSession } from '@jbrowse/core/util';
|
|
4
|
+
export declare const LEFT: 0, RIGHT: 2;
|
|
5
|
+
export interface OverlayProps {
|
|
6
|
+
model: BreakpointViewModel;
|
|
7
|
+
trackId: string;
|
|
8
|
+
parentRef: React.RefObject<SVGSVGElement | null>;
|
|
9
|
+
getTrackYPosOverride?: (trackId: string, level: number) => number;
|
|
10
|
+
}
|
|
11
|
+
export declare function getYOffset(parentRef: React.RefObject<SVGSVGElement | null>): number;
|
|
12
|
+
export declare function createMouseHandlers(id: string, setMouseoverElt: (id: string | undefined) => void, session: ReturnType<typeof getSession>, widgetType: string, widgetId: string, featureData: unknown): {
|
|
13
|
+
onClick: () => void;
|
|
14
|
+
onMouseOver: () => void;
|
|
15
|
+
onMouseOut: () => void;
|
|
16
|
+
};
|
|
17
|
+
export declare function getTestId(trackId: string, hasMatches: boolean): string;
|
|
18
|
+
export declare function getCanonicalRefs(assembly: Assembly, f1RefName: string, f2RefName: string): {
|
|
19
|
+
f1ref: string;
|
|
20
|
+
f2ref: string;
|
|
21
|
+
};
|
|
22
|
+
export declare function strandToSign(s: string): 0 | 1 | -1;
|
|
23
|
+
export declare function buildSimplePath(x1: number, y1: number, x2: number, y2: number): string;
|
|
24
|
+
export declare function buildBreakpointPath(x1: number, y1: number, x2: number, y2: number, x1Tick: number, x2Tick: number): string;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
export const [LEFT, , RIGHT] = [0, 1, 2, 3];
|
|
2
|
+
export function getYOffset(parentRef) {
|
|
3
|
+
return parentRef.current?.getBoundingClientRect().top ?? 0;
|
|
4
|
+
}
|
|
5
|
+
export function createMouseHandlers(id, setMouseoverElt, session, widgetType, widgetId, featureData) {
|
|
6
|
+
return {
|
|
7
|
+
onClick: () => {
|
|
8
|
+
const featureWidget = session.addWidget?.(widgetType, widgetId, {
|
|
9
|
+
featureData,
|
|
10
|
+
});
|
|
11
|
+
session.showWidget?.(featureWidget);
|
|
12
|
+
},
|
|
13
|
+
onMouseOver: () => {
|
|
14
|
+
setMouseoverElt(id);
|
|
15
|
+
},
|
|
16
|
+
onMouseOut: () => {
|
|
17
|
+
setMouseoverElt(undefined);
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
export function getTestId(trackId, hasMatches) {
|
|
22
|
+
return hasMatches ? `${trackId}-loaded` : trackId;
|
|
23
|
+
}
|
|
24
|
+
export function getCanonicalRefs(assembly, f1RefName, f2RefName) {
|
|
25
|
+
const f1ref = assembly.getCanonicalRefName(f1RefName);
|
|
26
|
+
const f2ref = assembly.getCanonicalRefName(f2RefName);
|
|
27
|
+
if (!f1ref || !f2ref) {
|
|
28
|
+
throw new Error(`unable to find ref for ${f1ref || f2ref}`);
|
|
29
|
+
}
|
|
30
|
+
return { f1ref, f2ref };
|
|
31
|
+
}
|
|
32
|
+
export function strandToSign(s) {
|
|
33
|
+
return s === '+' ? 1 : s === '-' ? -1 : 0;
|
|
34
|
+
}
|
|
35
|
+
const FLAT_ARC_HEIGHT = 30;
|
|
36
|
+
function arcOrLine(x1, y1, x2, y2) {
|
|
37
|
+
const midX = (x1 + x2) / 2;
|
|
38
|
+
return y1 === y2
|
|
39
|
+
? `Q ${midX} ${y1 - FLAT_ARC_HEIGHT} ${x2} ${y2}`
|
|
40
|
+
: `L ${x2} ${y2}`;
|
|
41
|
+
}
|
|
42
|
+
export function buildSimplePath(x1, y1, x2, y2) {
|
|
43
|
+
return `M ${x1} ${y1} ${arcOrLine(x1, y1, x2, y2)}`;
|
|
44
|
+
}
|
|
45
|
+
export function buildBreakpointPath(x1, y1, x2, y2, x1Tick, x2Tick) {
|
|
46
|
+
return `M ${x1Tick} ${y1} L ${x1} ${y1} ${arcOrLine(x1, y1, x2, y2)} L ${x2Tick} ${y2}`;
|
|
47
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type React from 'react';
|
|
2
|
+
import type { BreakpointViewModel } from '../model.ts';
|
|
3
|
+
interface AnchorPosition {
|
|
4
|
+
offsetX: number;
|
|
5
|
+
clientX: number;
|
|
6
|
+
clientY: number;
|
|
7
|
+
}
|
|
8
|
+
export declare function useRangeSelect(ref: React.RefObject<HTMLDivElement | null>, model: BreakpointViewModel): {
|
|
9
|
+
open: boolean;
|
|
10
|
+
guideX: number | undefined;
|
|
11
|
+
mouseDown: (event: React.MouseEvent<HTMLDivElement>) => void;
|
|
12
|
+
mouseMove: (event: React.MouseEvent<HTMLDivElement>) => void;
|
|
13
|
+
mouseOut: () => void;
|
|
14
|
+
handleMenuItemClick: (_: unknown, callback: () => void) => void;
|
|
15
|
+
rubberbandOn?: undefined;
|
|
16
|
+
handleClose?: undefined;
|
|
17
|
+
leftBpOffset?: undefined;
|
|
18
|
+
rightBpOffset?: undefined;
|
|
19
|
+
anchorPosition?: undefined;
|
|
20
|
+
numOfBpSelected?: undefined;
|
|
21
|
+
width?: undefined;
|
|
22
|
+
left?: undefined;
|
|
23
|
+
} | {
|
|
24
|
+
open: boolean;
|
|
25
|
+
rubberbandOn: boolean;
|
|
26
|
+
mouseDown: (event: React.MouseEvent<HTMLDivElement>) => void;
|
|
27
|
+
mouseMove: (event: React.MouseEvent<HTMLDivElement>) => void;
|
|
28
|
+
mouseOut: () => void;
|
|
29
|
+
handleClose: () => void;
|
|
30
|
+
handleMenuItemClick: (_: unknown, callback: () => void) => void;
|
|
31
|
+
leftBpOffset: {
|
|
32
|
+
coord: number;
|
|
33
|
+
index: number;
|
|
34
|
+
refName: string;
|
|
35
|
+
oob: boolean;
|
|
36
|
+
assemblyName: string;
|
|
37
|
+
offset: number;
|
|
38
|
+
start: number;
|
|
39
|
+
end: number;
|
|
40
|
+
reversed?: boolean;
|
|
41
|
+
}[];
|
|
42
|
+
rightBpOffset: {
|
|
43
|
+
coord: number;
|
|
44
|
+
index: number;
|
|
45
|
+
refName: string;
|
|
46
|
+
oob: boolean;
|
|
47
|
+
assemblyName: string;
|
|
48
|
+
offset: number;
|
|
49
|
+
start: number;
|
|
50
|
+
end: number;
|
|
51
|
+
reversed?: boolean;
|
|
52
|
+
}[];
|
|
53
|
+
anchorPosition: AnchorPosition | undefined;
|
|
54
|
+
numOfBpSelected: number[];
|
|
55
|
+
width: number;
|
|
56
|
+
left: number;
|
|
57
|
+
guideX?: undefined;
|
|
58
|
+
};
|
|
59
|
+
export {};
|