@jbrowse/plugin-breakpoint-split-view 2.15.0 → 2.15.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/dist/BreakpointSplitView/BreakpointSplitView.d.ts +29 -1
- package/dist/BreakpointSplitView/BreakpointSplitView.js +71 -32
- package/dist/BreakpointSplitView/components/AlignmentConnections.js +43 -12
- package/dist/BreakpointSplitView/components/BreakpointSplitView.js +12 -10
- package/dist/BreakpointSplitView/components/BreakpointSplitViewOverlay.js +6 -5
- package/dist/BreakpointSplitView/components/Overlay.js +8 -3
- package/dist/BreakpointSplitView/components/PairedFeatures.d.ts +9 -0
- package/dist/BreakpointSplitView/components/PairedFeatures.js +100 -0
- package/dist/BreakpointSplitView/components/getOrientationColor.d.ts +41 -0
- package/dist/BreakpointSplitView/components/getOrientationColor.js +111 -0
- package/dist/BreakpointSplitView/components/util.d.ts +1 -0
- package/dist/BreakpointSplitView/components/util.js +26 -0
- package/dist/BreakpointSplitView/model.d.ts +24 -5
- package/dist/BreakpointSplitView/model.js +35 -7
- package/dist/BreakpointSplitView/svgcomponents/SVGBreakpointSplitView.js +5 -8
- package/dist/BreakpointSplitView/util.d.ts +1 -0
- package/dist/BreakpointSplitView/util.js +1 -0
- package/esm/BreakpointSplitView/BreakpointSplitView.d.ts +29 -1
- package/esm/BreakpointSplitView/BreakpointSplitView.js +71 -32
- package/esm/BreakpointSplitView/components/AlignmentConnections.js +44 -13
- package/esm/BreakpointSplitView/components/BreakpointSplitView.js +11 -9
- package/esm/BreakpointSplitView/components/BreakpointSplitViewOverlay.js +6 -5
- package/esm/BreakpointSplitView/components/Overlay.js +8 -3
- package/esm/BreakpointSplitView/components/PairedFeatures.d.ts +9 -0
- package/esm/BreakpointSplitView/components/PairedFeatures.js +75 -0
- package/esm/BreakpointSplitView/components/getOrientationColor.d.ts +41 -0
- package/esm/BreakpointSplitView/components/getOrientationColor.js +103 -0
- package/esm/BreakpointSplitView/components/util.d.ts +1 -0
- package/esm/BreakpointSplitView/components/util.js +25 -0
- package/esm/BreakpointSplitView/model.d.ts +24 -5
- package/esm/BreakpointSplitView/model.js +35 -7
- package/esm/BreakpointSplitView/svgcomponents/SVGBreakpointSplitView.js +5 -8
- package/esm/BreakpointSplitView/util.d.ts +1 -0
- package/esm/BreakpointSplitView/util.js +1 -1
- package/package.json +2 -2
|
@@ -1,8 +1,18 @@
|
|
|
1
1
|
import { Feature, Region } from '@jbrowse/core/util';
|
|
2
2
|
import ViewType from '@jbrowse/core/pluggableElementTypes/ViewType';
|
|
3
3
|
import { IStateTreeNode } from 'mobx-state-tree';
|
|
4
|
+
import { Assembly } from '@jbrowse/core/assemblyManager/assembly';
|
|
4
5
|
export default class BreakpointSplitViewType extends ViewType {
|
|
5
|
-
|
|
6
|
+
getBreakendCoveringRegions({ feature, assembly, }: {
|
|
7
|
+
feature: Feature;
|
|
8
|
+
assembly: Assembly;
|
|
9
|
+
}): {
|
|
10
|
+
pos: number;
|
|
11
|
+
refName: string | undefined;
|
|
12
|
+
mateRefName: string | undefined;
|
|
13
|
+
matePos: number;
|
|
14
|
+
};
|
|
15
|
+
singleLevelSnapshotFromBreakendFeature(feature: Feature, view: {
|
|
6
16
|
displayedRegions: Region[];
|
|
7
17
|
} & IStateTreeNode): {
|
|
8
18
|
type: string;
|
|
@@ -21,4 +31,22 @@ export default class BreakpointSplitViewType extends ViewType {
|
|
|
21
31
|
displayName: string;
|
|
22
32
|
featureData: unknown;
|
|
23
33
|
};
|
|
34
|
+
snapshotFromBreakendFeature(feature: Feature, view: {
|
|
35
|
+
displayedRegions: Region[];
|
|
36
|
+
} & IStateTreeNode): {
|
|
37
|
+
type: string;
|
|
38
|
+
views: {
|
|
39
|
+
type: string;
|
|
40
|
+
displayedRegions: {
|
|
41
|
+
start: number;
|
|
42
|
+
end: number;
|
|
43
|
+
refName: string;
|
|
44
|
+
assemblyName: string;
|
|
45
|
+
}[];
|
|
46
|
+
hideHeader: boolean;
|
|
47
|
+
bpPerPx: number;
|
|
48
|
+
offsetPx: number;
|
|
49
|
+
}[];
|
|
50
|
+
displayName: string;
|
|
51
|
+
};
|
|
24
52
|
}
|
|
@@ -7,49 +7,29 @@ const util_1 = require("@jbrowse/core/util");
|
|
|
7
7
|
const ViewType_1 = __importDefault(require("@jbrowse/core/pluggableElementTypes/ViewType"));
|
|
8
8
|
const vcf_1 = require("@gmod/vcf");
|
|
9
9
|
class BreakpointSplitViewType extends ViewType_1.default {
|
|
10
|
-
|
|
10
|
+
getBreakendCoveringRegions({ feature, assembly, }) {
|
|
11
11
|
var _a;
|
|
12
12
|
const alt = (_a = feature.get('ALT')) === null || _a === void 0 ? void 0 : _a[0];
|
|
13
13
|
const bnd = alt ? (0, vcf_1.parseBreakend)(alt) : undefined;
|
|
14
14
|
const startPos = feature.get('start');
|
|
15
|
+
const refName = feature.get('refName');
|
|
15
16
|
let endPos;
|
|
16
|
-
const bpPerPx = 10;
|
|
17
|
-
// TODO: Figure this out for multiple assembly names
|
|
18
|
-
const { assemblyName } = view.displayedRegions[0];
|
|
19
|
-
const { assemblyManager } = (0, util_1.getSession)(view);
|
|
20
|
-
const assembly = assemblyManager.get(assemblyName);
|
|
21
|
-
if (!assembly) {
|
|
22
|
-
throw new Error(`assembly ${assemblyName} not found`);
|
|
23
|
-
}
|
|
24
|
-
if (!assembly.regions) {
|
|
25
|
-
throw new Error(`assembly ${assemblyName} regions not loaded`);
|
|
26
|
-
}
|
|
27
|
-
const featureRefName = assembly.getCanonicalRefName(feature.get('refName'));
|
|
28
|
-
const topRegion = assembly.regions.find(f => f.refName === featureRefName);
|
|
29
17
|
let mateRefName;
|
|
30
|
-
let startMod = 0;
|
|
31
|
-
let endMod = 0;
|
|
32
18
|
// a VCF breakend feature
|
|
33
19
|
if (alt === '<TRA>') {
|
|
34
20
|
const INFO = feature.get('INFO');
|
|
35
21
|
endPos = INFO.END[0] - 1;
|
|
36
|
-
mateRefName =
|
|
22
|
+
mateRefName = INFO.CHR2[0];
|
|
37
23
|
}
|
|
38
24
|
else if (bnd === null || bnd === void 0 ? void 0 : bnd.MatePosition) {
|
|
39
25
|
const matePosition = bnd.MatePosition.split(':');
|
|
40
26
|
endPos = +matePosition[1] - 1;
|
|
41
|
-
mateRefName =
|
|
42
|
-
if (bnd.Join === 'left') {
|
|
43
|
-
startMod = -1;
|
|
44
|
-
}
|
|
45
|
-
if (bnd.MateDirection === 'left') {
|
|
46
|
-
endMod = -1;
|
|
47
|
-
}
|
|
27
|
+
mateRefName = matePosition[0];
|
|
48
28
|
}
|
|
49
29
|
else if (feature.get('mate')) {
|
|
50
30
|
// a generic 'mate' feature
|
|
51
31
|
const mate = feature.get('mate');
|
|
52
|
-
mateRefName =
|
|
32
|
+
mateRefName = mate.refName;
|
|
53
33
|
endPos = mate.start;
|
|
54
34
|
}
|
|
55
35
|
else {
|
|
@@ -58,16 +38,76 @@ class BreakpointSplitViewType extends ViewType_1.default {
|
|
|
58
38
|
if (!mateRefName) {
|
|
59
39
|
throw new Error(`unable to resolve mate refName ${mateRefName} in reference genome`);
|
|
60
40
|
}
|
|
41
|
+
return {
|
|
42
|
+
pos: startPos,
|
|
43
|
+
refName: assembly.getCanonicalRefName(refName),
|
|
44
|
+
mateRefName: assembly.getCanonicalRefName(mateRefName),
|
|
45
|
+
matePos: endPos,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
singleLevelSnapshotFromBreakendFeature(feature, view) {
|
|
49
|
+
const session = (0, util_1.getSession)(view);
|
|
50
|
+
const bpPerPx = 10;
|
|
51
|
+
const { assemblyName } = view.displayedRegions[0];
|
|
52
|
+
const { assemblyManager } = session;
|
|
53
|
+
const assembly = assemblyManager.get(assemblyName);
|
|
54
|
+
if (!assembly) {
|
|
55
|
+
throw new Error(`assembly ${assemblyName} not found`);
|
|
56
|
+
}
|
|
57
|
+
if (!assembly.regions) {
|
|
58
|
+
throw new Error(`assembly ${assemblyName} regions not loaded`);
|
|
59
|
+
}
|
|
60
|
+
const { refName, pos: startPos, mateRefName, matePos: endPos, } = this.getBreakendCoveringRegions({
|
|
61
|
+
feature,
|
|
62
|
+
assembly,
|
|
63
|
+
});
|
|
64
|
+
const topRegion = assembly.regions.find(f => f.refName === refName);
|
|
61
65
|
const bottomRegion = assembly.regions.find(f => f.refName === mateRefName);
|
|
62
|
-
|
|
63
|
-
|
|
66
|
+
const topMarkedRegion = [{ ...topRegion }, { ...topRegion }];
|
|
67
|
+
const bottomMarkedRegion = [{ ...bottomRegion }, { ...bottomRegion }];
|
|
68
|
+
topMarkedRegion[0].end = startPos;
|
|
69
|
+
topMarkedRegion[1].start = startPos;
|
|
70
|
+
bottomMarkedRegion[0].end = endPos;
|
|
71
|
+
bottomMarkedRegion[1].start = endPos;
|
|
72
|
+
return {
|
|
73
|
+
type: 'BreakpointSplitView',
|
|
74
|
+
views: [
|
|
75
|
+
{
|
|
76
|
+
type: 'LinearGenomeView',
|
|
77
|
+
displayedRegions: topMarkedRegion,
|
|
78
|
+
hideHeader: true,
|
|
79
|
+
bpPerPx,
|
|
80
|
+
offsetPx: (topRegion.start + feature.get('start')) / bpPerPx,
|
|
81
|
+
},
|
|
82
|
+
],
|
|
83
|
+
displayName: `${feature.get('name') || feature.get('id') || 'breakend'} split detail`,
|
|
84
|
+
featureData: undefined,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
snapshotFromBreakendFeature(feature, view) {
|
|
88
|
+
const session = (0, util_1.getSession)(view);
|
|
89
|
+
const bpPerPx = 10;
|
|
90
|
+
const { assemblyName } = view.displayedRegions[0];
|
|
91
|
+
const { assemblyManager } = session;
|
|
92
|
+
const assembly = assemblyManager.get(assemblyName);
|
|
93
|
+
if (!assembly) {
|
|
94
|
+
throw new Error(`assembly ${assemblyName} not found`);
|
|
95
|
+
}
|
|
96
|
+
if (!assembly.regions) {
|
|
97
|
+
throw new Error(`assembly ${assemblyName} regions not loaded`);
|
|
64
98
|
}
|
|
99
|
+
const { refName, pos: startPos, mateRefName, matePos: endPos, } = this.getBreakendCoveringRegions({
|
|
100
|
+
feature,
|
|
101
|
+
assembly,
|
|
102
|
+
});
|
|
103
|
+
const topRegion = assembly.regions.find(f => f.refName === refName);
|
|
104
|
+
const bottomRegion = assembly.regions.find(f => f.refName === mateRefName);
|
|
65
105
|
const topMarkedRegion = [{ ...topRegion }, { ...topRegion }];
|
|
66
106
|
const bottomMarkedRegion = [{ ...bottomRegion }, { ...bottomRegion }];
|
|
67
|
-
topMarkedRegion[0].end = startPos
|
|
68
|
-
topMarkedRegion[1].start = startPos
|
|
69
|
-
bottomMarkedRegion[0].end = endPos
|
|
70
|
-
bottomMarkedRegion[1].start = endPos
|
|
107
|
+
topMarkedRegion[0].end = startPos;
|
|
108
|
+
topMarkedRegion[1].start = startPos;
|
|
109
|
+
bottomMarkedRegion[0].end = endPos;
|
|
110
|
+
bottomMarkedRegion[1].start = endPos;
|
|
71
111
|
return {
|
|
72
112
|
type: 'BreakpointSplitView',
|
|
73
113
|
views: [
|
|
@@ -87,7 +127,6 @@ class BreakpointSplitViewType extends ViewType_1.default {
|
|
|
87
127
|
},
|
|
88
128
|
],
|
|
89
129
|
displayName: `${feature.get('name') || feature.get('id') || 'breakend'} split detail`,
|
|
90
|
-
featureData: undefined,
|
|
91
130
|
};
|
|
92
131
|
}
|
|
93
132
|
}
|
|
@@ -31,6 +31,7 @@ const util_1 = require("@jbrowse/core/util");
|
|
|
31
31
|
// locals
|
|
32
32
|
const util_2 = require("./util");
|
|
33
33
|
const util_3 = require("../util");
|
|
34
|
+
const getOrientationColor_1 = require("./getOrientationColor");
|
|
34
35
|
const [LEFT, , RIGHT] = [0, 1, 2, 3];
|
|
35
36
|
const AlignmentConnections = (0, mobx_react_1.observer)(function ({ model, trackId, parentRef, getTrackYPosOverride, }) {
|
|
36
37
|
const { views, showIntraviewLinks } = model;
|
|
@@ -38,7 +39,8 @@ const AlignmentConnections = (0, mobx_react_1.observer)(function ({ model, track
|
|
|
38
39
|
const session = (0, util_1.getSession)(model);
|
|
39
40
|
const snap = (0, mobx_state_tree_1.getSnapshot)(model);
|
|
40
41
|
const { assemblyManager } = session;
|
|
41
|
-
const
|
|
42
|
+
const v0 = views[0];
|
|
43
|
+
const assembly = v0 ? assemblyManager.get(v0.assemblyNames[0]) : undefined;
|
|
42
44
|
(0, util_3.useNextFrame)(snap);
|
|
43
45
|
const allFeatures = model.getTrackFeatures(trackId);
|
|
44
46
|
const hasPaired = (0, react_1.useMemo)(() => (0, util_2.hasPairedReads)(allFeatures), [allFeatures]);
|
|
@@ -59,7 +61,7 @@ const AlignmentConnections = (0, mobx_react_1.observer)(function ({ model, track
|
|
|
59
61
|
const rect = parentRef.current.getBoundingClientRect();
|
|
60
62
|
yOffset = rect.top;
|
|
61
63
|
}
|
|
62
|
-
return assembly ? (react_1.default.createElement("g", { fill: "none",
|
|
64
|
+
return assembly ? (react_1.default.createElement("g", { fill: "none", "data-testid": layoutMatches.length ? `${trackId}-loaded` : trackId }, layoutMatches.map(chunk => {
|
|
63
65
|
const ret = [];
|
|
64
66
|
// we follow a path in the list of chunks, not from top to bottom, just in series
|
|
65
67
|
// following x1,y1 -> x2,y2
|
|
@@ -70,7 +72,7 @@ const AlignmentConnections = (0, mobx_react_1.observer)(function ({ model, track
|
|
|
70
72
|
console.warn('received null layout for a overlay feature');
|
|
71
73
|
return null;
|
|
72
74
|
}
|
|
73
|
-
// disable rendering connections in a single
|
|
75
|
+
// disable rendering connections in a single row
|
|
74
76
|
if (!showIntraviewLinks && level1 === level2) {
|
|
75
77
|
return null;
|
|
76
78
|
}
|
|
@@ -79,8 +81,25 @@ const AlignmentConnections = (0, mobx_react_1.observer)(function ({ model, track
|
|
|
79
81
|
if (!f1ref || !f2ref) {
|
|
80
82
|
throw new Error(`unable to find ref for ${f1ref || f2ref}`);
|
|
81
83
|
}
|
|
84
|
+
const r = {
|
|
85
|
+
pair_orientation: f1.get('pair_orientation'),
|
|
86
|
+
};
|
|
82
87
|
const s1 = f1.get('strand');
|
|
83
88
|
const s2 = f2.get('strand');
|
|
89
|
+
const sameRef = f1ref === f2ref;
|
|
90
|
+
const checkOrientation = sameRef;
|
|
91
|
+
let orientationColor = '';
|
|
92
|
+
let isAbnormal = false;
|
|
93
|
+
if (checkOrientation) {
|
|
94
|
+
if (hasPaired) {
|
|
95
|
+
orientationColor = (0, getOrientationColor_1.getPairedOrientationColor)(r);
|
|
96
|
+
isAbnormal = (0, getOrientationColor_1.isAbnormalOrientation)(r);
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
orientationColor = (0, getOrientationColor_1.getLongReadOrientationColorOrDefault)(s1, s2);
|
|
100
|
+
isAbnormal = (0, getOrientationColor_1.getLongReadOrientationAbnormal)(s1, s2);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
84
103
|
const p1 = c1[s1 === -1 ? LEFT : RIGHT];
|
|
85
104
|
const sn1 = s2 === -1;
|
|
86
105
|
const p2 = hasPaired ? c2[sn1 ? LEFT : RIGHT] : c2[sn1 ? RIGHT : LEFT];
|
|
@@ -88,11 +107,20 @@ const AlignmentConnections = (0, mobx_react_1.observer)(function ({ model, track
|
|
|
88
107
|
const x2 = (0, util_3.getPxFromCoordinate)(views[level2], f2ref, p2);
|
|
89
108
|
const reversed1 = views[level1].pxToBp(x1).reversed;
|
|
90
109
|
const reversed2 = views[level2].pxToBp(x2).reversed;
|
|
110
|
+
const rf1 = reversed1 ? -1 : 1;
|
|
111
|
+
const rf2 = reversed2 ? -1 : 1;
|
|
91
112
|
const tracks = views.map(v => v.getTrack(trackId));
|
|
92
113
|
const y1 = (0, util_3.yPos)(trackId, level1, views, tracks, c1, getTrackYPosOverride) -
|
|
93
114
|
yOffset;
|
|
94
115
|
const y2 = (0, util_3.yPos)(trackId, level2, views, tracks, c2, getTrackYPosOverride) -
|
|
95
116
|
yOffset;
|
|
117
|
+
const sameLevel = level1 === level2;
|
|
118
|
+
const abnormalSpecialRenderFlag = sameLevel && isAbnormal;
|
|
119
|
+
const trackHeight = abnormalSpecialRenderFlag
|
|
120
|
+
? tracks[level1].displays[0].height
|
|
121
|
+
: 0;
|
|
122
|
+
const pf1 = hasPaired ? -1 : 1;
|
|
123
|
+
const y0 = (0, util_3.heightFromSpecificLevel)(views, trackId, level1, getTrackYPosOverride);
|
|
96
124
|
// possible todo: use totalCurveHeight to possibly make alternative
|
|
97
125
|
// squiggle if the S is too small
|
|
98
126
|
const path = [
|
|
@@ -100,19 +128,22 @@ const AlignmentConnections = (0, mobx_react_1.observer)(function ({ model, track
|
|
|
100
128
|
x1,
|
|
101
129
|
y1,
|
|
102
130
|
'C',
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
131
|
+
// first bezier x,y
|
|
132
|
+
x1 + 200 * f1.get('strand') * rf1,
|
|
133
|
+
abnormalSpecialRenderFlag
|
|
134
|
+
? Math.min(y0 - yOffset + trackHeight, y1 + trackHeight)
|
|
135
|
+
: y1,
|
|
136
|
+
// second bezier x,y
|
|
137
|
+
x2 - 200 * f2.get('strand') * rf2 * pf1,
|
|
138
|
+
abnormalSpecialRenderFlag
|
|
139
|
+
? Math.min(y0 - yOffset + trackHeight, y2 + trackHeight)
|
|
140
|
+
: y2,
|
|
141
|
+
// third bezier x,y
|
|
111
142
|
x2,
|
|
112
143
|
y2,
|
|
113
144
|
].join(' ');
|
|
114
145
|
const id = `${f1.id()}-${f2.id()}`;
|
|
115
|
-
ret.push(react_1.default.createElement("path", { d: path, key: id, "data-testid": "r1", strokeWidth: mouseoverElt === id ? 5 : 1, onClick: () => {
|
|
146
|
+
ret.push(react_1.default.createElement("path", { d: path, key: id, "data-testid": "r1", strokeWidth: mouseoverElt === id ? 5 : 1, ...(0, util_1.getStrokeProps)(orientationColor || theme.palette.text.disabled), onClick: () => {
|
|
116
147
|
var _a, _b;
|
|
117
148
|
const featureWidget = (_a = session.addWidget) === null || _a === void 0 ? void 0 : _a.call(session, 'BreakpointAlignmentsWidget', 'breakpointAlignments', {
|
|
118
149
|
featureData: {
|
|
@@ -5,7 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
const react_1 = __importDefault(require("react"));
|
|
7
7
|
const mobx_react_1 = require("mobx-react");
|
|
8
|
-
const
|
|
8
|
+
const util_1 = require("@jbrowse/core/util");
|
|
9
9
|
const mui_1 = require("tss-react/mui");
|
|
10
10
|
const BreakpointSplitViewOverlay_1 = __importDefault(require("./BreakpointSplitViewOverlay"));
|
|
11
11
|
const useStyles = (0, mui_1.makeStyles)()(theme => ({
|
|
@@ -19,22 +19,24 @@ const useStyles = (0, mui_1.makeStyles)()(theme => ({
|
|
|
19
19
|
content: {
|
|
20
20
|
gridArea: '1/1',
|
|
21
21
|
},
|
|
22
|
+
rel: {
|
|
23
|
+
position: 'relative',
|
|
24
|
+
},
|
|
22
25
|
}));
|
|
23
26
|
const BreakpointSplitViewLevels = (0, mobx_react_1.observer)(function ({ model, }) {
|
|
24
27
|
const { classes } = useStyles();
|
|
25
28
|
const { views } = model;
|
|
26
|
-
const { pluginManager } = (0,
|
|
29
|
+
const { pluginManager } = (0, util_1.getEnv)(model);
|
|
27
30
|
return (react_1.default.createElement("div", { className: classes.content },
|
|
28
|
-
react_1.default.createElement("div", {
|
|
31
|
+
react_1.default.createElement("div", { className: classes.rel }, views.map((view, idx) => {
|
|
29
32
|
const { ReactComponent } = pluginManager.getViewType(view.type);
|
|
30
33
|
const viewComponent = react_1.default.createElement(ReactComponent, { key: view.id, model: view });
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
];
|
|
34
|
+
return idx === views.length - 1
|
|
35
|
+
? viewComponent
|
|
36
|
+
: [
|
|
37
|
+
viewComponent,
|
|
38
|
+
react_1.default.createElement("div", { key: `${view.id}-divider`, className: classes.viewDivider }),
|
|
39
|
+
];
|
|
38
40
|
}))));
|
|
39
41
|
});
|
|
40
42
|
const BreakpointSplitView = (0, mobx_react_1.observer)(function ({ model, }) {
|
|
@@ -51,11 +51,12 @@ const BreakpointSplitViewOverlay = (0, mobx_react_1.observer)(function ({ model,
|
|
|
51
51
|
zIndex: 10,
|
|
52
52
|
pointerEvents: interactToggled ? undefined : 'none',
|
|
53
53
|
} }, matchedTracks.map(track => (
|
|
54
|
-
// note: we must pass ref down, because
|
|
55
|
-
//
|
|
56
|
-
//
|
|
57
|
-
//
|
|
58
|
-
//
|
|
54
|
+
// note: we must pass ref down, because:
|
|
55
|
+
// - the child component needs to getBoundingClientRect on the this
|
|
56
|
+
// components SVG, and...
|
|
57
|
+
// - we cannot rely on using getBoundingClientRect in this component
|
|
58
|
+
// to make sure this works because if it gets shifted around by
|
|
59
|
+
// another element, this will not re-render necessarily
|
|
59
60
|
react_1.default.createElement(Overlay_1.default, { parentRef: ref, key: track.configuration.trackId, model: model, trackId: track.configuration.trackId }))))));
|
|
60
61
|
});
|
|
61
62
|
exports.default = BreakpointSplitViewOverlay;
|
|
@@ -8,16 +8,21 @@ const mobx_react_1 = require("mobx-react");
|
|
|
8
8
|
const AlignmentConnections_1 = __importDefault(require("./AlignmentConnections"));
|
|
9
9
|
const Breakends_1 = __importDefault(require("./Breakends"));
|
|
10
10
|
const Translocations_1 = __importDefault(require("./Translocations"));
|
|
11
|
+
const PairedFeatures_1 = __importDefault(require("./PairedFeatures"));
|
|
11
12
|
const Overlay = (0, mobx_react_1.observer)(function (props) {
|
|
12
13
|
var _a, _b;
|
|
13
14
|
const { model, trackId } = props;
|
|
14
15
|
const tracks = model.getMatchedTracks(trackId);
|
|
16
|
+
// curvy line type arcs
|
|
15
17
|
if (((_a = tracks[0]) === null || _a === void 0 ? void 0 : _a.type) === 'AlignmentsTrack') {
|
|
16
18
|
return react_1.default.createElement(AlignmentConnections_1.default, { ...props });
|
|
17
19
|
}
|
|
18
|
-
|
|
19
|
-
|
|
20
|
+
// translocation type arcs
|
|
21
|
+
else if (((_b = tracks[0]) === null || _b === void 0 ? void 0 : _b.type) === 'VariantTrack') {
|
|
22
|
+
return model.hasTranslocations(trackId) ? (react_1.default.createElement(Translocations_1.default, { ...props })) : model.hasPairedFeatures(trackId) ? (react_1.default.createElement(PairedFeatures_1.default, { ...props })) : (react_1.default.createElement(Breakends_1.default, { ...props }));
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
return null;
|
|
20
26
|
}
|
|
21
|
-
return null;
|
|
22
27
|
});
|
|
23
28
|
exports.default = Overlay;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { BreakpointViewModel } from '../model';
|
|
3
|
+
declare const PairedFeatures: ({ model, trackId, parentRef: ref, getTrackYPosOverride, }: {
|
|
4
|
+
model: BreakpointViewModel;
|
|
5
|
+
trackId: string;
|
|
6
|
+
parentRef: React.RefObject<SVGSVGElement>;
|
|
7
|
+
getTrackYPosOverride?: (trackId: string, level: number) => number;
|
|
8
|
+
}) => React.JSX.Element | null;
|
|
9
|
+
export default PairedFeatures;
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
const react_1 = __importStar(require("react"));
|
|
27
|
+
const util_1 = require("@jbrowse/core/util");
|
|
28
|
+
const mobx_react_1 = require("mobx-react");
|
|
29
|
+
const mobx_state_tree_1 = require("mobx-state-tree");
|
|
30
|
+
// locals
|
|
31
|
+
const util_2 = require("./util");
|
|
32
|
+
const util_3 = require("../util");
|
|
33
|
+
const [LEFT] = [0, 1, 2, 3];
|
|
34
|
+
const PairedFeatures = (0, mobx_react_1.observer)(function ({ model, trackId, parentRef: ref, getTrackYPosOverride, }) {
|
|
35
|
+
const { views } = model;
|
|
36
|
+
const session = (0, util_1.getSession)(model);
|
|
37
|
+
const { assemblyManager } = session;
|
|
38
|
+
const totalFeatures = model.getTrackFeatures(trackId);
|
|
39
|
+
const layoutMatches = (0, react_1.useMemo)(() => model.getMatchedFeaturesInLayout(trackId, (0, util_2.getMatchedPairedFeatures)(totalFeatures)), [totalFeatures, trackId, model]);
|
|
40
|
+
const [mouseoverElt, setMouseoverElt] = (0, react_1.useState)();
|
|
41
|
+
const snap = (0, mobx_state_tree_1.getSnapshot)(model);
|
|
42
|
+
(0, util_3.useNextFrame)(snap);
|
|
43
|
+
const assembly = assemblyManager.get(views[0].assemblyNames[0]);
|
|
44
|
+
if (!assembly) {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
let yoff = 0;
|
|
48
|
+
if (ref.current) {
|
|
49
|
+
const rect = ref.current.getBoundingClientRect();
|
|
50
|
+
yoff = rect.top;
|
|
51
|
+
}
|
|
52
|
+
return (react_1.default.createElement("g", { stroke: "green", strokeWidth: 5, fill: "none", "data-testid": layoutMatches.length ? `${trackId}-loaded` : trackId }, layoutMatches.map(chunk => {
|
|
53
|
+
const ret = [];
|
|
54
|
+
// we follow a path in the list of chunks, not from top to bottom, just
|
|
55
|
+
// in series following x1,y1 -> x2,y2
|
|
56
|
+
for (let i = 0; i < chunk.length - 1; i += 1) {
|
|
57
|
+
const { layout: c1, feature: f1, level: level1 } = chunk[i];
|
|
58
|
+
const { layout: c2, feature: f2, level: level2 } = chunk[i + 1];
|
|
59
|
+
const id = f1.id();
|
|
60
|
+
if (!c1 || !c2) {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
const f1origref = f1.get('refName');
|
|
64
|
+
const f2origref = f2.get('refName');
|
|
65
|
+
const f1ref = assembly.getCanonicalRefName(f1origref);
|
|
66
|
+
const f2ref = assembly.getCanonicalRefName(f2origref);
|
|
67
|
+
if (!f1ref || !f2ref) {
|
|
68
|
+
throw new Error(`unable to find ref for ${f1ref || f2ref}`);
|
|
69
|
+
}
|
|
70
|
+
const x1 = (0, util_3.getPxFromCoordinate)(views[level1], f1ref, c1[LEFT]);
|
|
71
|
+
const x2 = (0, util_3.getPxFromCoordinate)(views[level2], f2ref, c2[LEFT]);
|
|
72
|
+
const tracks = views.map(v => v.getTrack(trackId));
|
|
73
|
+
const y1 = (0, util_3.yPos)(trackId, level1, views, tracks, c1, getTrackYPosOverride) -
|
|
74
|
+
yoff;
|
|
75
|
+
const y2 = (0, util_3.yPos)(trackId, level2, views, tracks, c2, getTrackYPosOverride) -
|
|
76
|
+
yoff;
|
|
77
|
+
const path = [
|
|
78
|
+
'M', // move to
|
|
79
|
+
x1,
|
|
80
|
+
y1,
|
|
81
|
+
'L', // line to
|
|
82
|
+
x2,
|
|
83
|
+
y2,
|
|
84
|
+
].join(' ');
|
|
85
|
+
ret.push(react_1.default.createElement("path", { d: path, "data-testid": "r2", key: JSON.stringify(path), strokeWidth: id === mouseoverElt ? 10 : 5, onClick: () => {
|
|
86
|
+
var _a, _b, _c;
|
|
87
|
+
const featureWidget = (_a = session.addWidget) === null || _a === void 0 ? void 0 : _a.call(session, 'VariantFeatureWidget', 'variantFeature', {
|
|
88
|
+
featureData: (_b = totalFeatures.get(id)) === null || _b === void 0 ? void 0 : _b.toJSON(),
|
|
89
|
+
});
|
|
90
|
+
(_c = session.showWidget) === null || _c === void 0 ? void 0 : _c.call(session, featureWidget);
|
|
91
|
+
}, onMouseOver: () => {
|
|
92
|
+
setMouseoverElt(id);
|
|
93
|
+
}, onMouseOut: () => {
|
|
94
|
+
setMouseoverElt(undefined);
|
|
95
|
+
} }));
|
|
96
|
+
}
|
|
97
|
+
return ret;
|
|
98
|
+
})));
|
|
99
|
+
});
|
|
100
|
+
exports.default = PairedFeatures;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export declare const orientationTypes: {
|
|
2
|
+
fr: Record<string, string>;
|
|
3
|
+
rf: Record<string, string>;
|
|
4
|
+
ff: Record<string, string>;
|
|
5
|
+
};
|
|
6
|
+
export declare const pairMap: {
|
|
7
|
+
readonly LR: "color_pair_lr";
|
|
8
|
+
readonly LL: "color_pair_ll";
|
|
9
|
+
readonly RR: "color_pair_rr";
|
|
10
|
+
readonly RL: "color_pair_rl";
|
|
11
|
+
};
|
|
12
|
+
export declare const strokeColor: {
|
|
13
|
+
color_fwd_strand_not_proper: string;
|
|
14
|
+
color_rev_strand_not_proper: string;
|
|
15
|
+
color_fwd_strand: string;
|
|
16
|
+
color_rev_strand: string;
|
|
17
|
+
color_fwd_missing_mate: string;
|
|
18
|
+
color_rev_missing_mate: string;
|
|
19
|
+
color_fwd_diff_chr: string;
|
|
20
|
+
color_rev_diff_chr: string;
|
|
21
|
+
color_pair_lr: string;
|
|
22
|
+
color_pair_rr: string;
|
|
23
|
+
color_pair_rl: string;
|
|
24
|
+
color_pair_ll: string;
|
|
25
|
+
color_nostrand: string;
|
|
26
|
+
color_interchrom: string;
|
|
27
|
+
color_longinsert: string;
|
|
28
|
+
color_shortinsert: string;
|
|
29
|
+
color_unknown: string;
|
|
30
|
+
};
|
|
31
|
+
export declare function getPairedOrientationColorOrDefault(f: {
|
|
32
|
+
pair_orientation?: string;
|
|
33
|
+
}): string | undefined;
|
|
34
|
+
export declare function getLongReadOrientationColorOrDefault(s1: number, s2: number): string;
|
|
35
|
+
export declare function getLongReadOrientationAbnormal(s1: number, s2: number): boolean;
|
|
36
|
+
export declare function isAbnormalOrientation(f: {
|
|
37
|
+
pair_orientation?: string;
|
|
38
|
+
}): boolean;
|
|
39
|
+
export declare function getPairedOrientationColor(f: {
|
|
40
|
+
pair_orientation?: string;
|
|
41
|
+
}): string;
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.strokeColor = exports.pairMap = exports.orientationTypes = void 0;
|
|
4
|
+
exports.getPairedOrientationColorOrDefault = getPairedOrientationColorOrDefault;
|
|
5
|
+
exports.getLongReadOrientationColorOrDefault = getLongReadOrientationColorOrDefault;
|
|
6
|
+
exports.getLongReadOrientationAbnormal = getLongReadOrientationAbnormal;
|
|
7
|
+
exports.isAbnormalOrientation = isAbnormalOrientation;
|
|
8
|
+
exports.getPairedOrientationColor = getPairedOrientationColor;
|
|
9
|
+
const material_1 = require("@mui/material");
|
|
10
|
+
// orientation definitions from igv.js, see also
|
|
11
|
+
// https://software.broadinstitute.org/software/igv/interpreting_pair_orientations
|
|
12
|
+
exports.orientationTypes = {
|
|
13
|
+
fr: {
|
|
14
|
+
F1R2: 'LR',
|
|
15
|
+
F2R1: 'LR',
|
|
16
|
+
F1F2: 'LL',
|
|
17
|
+
F2F1: 'LL',
|
|
18
|
+
R1R2: 'RR',
|
|
19
|
+
R2R1: 'RR',
|
|
20
|
+
R1F2: 'RL',
|
|
21
|
+
R2F1: 'RL',
|
|
22
|
+
},
|
|
23
|
+
rf: {
|
|
24
|
+
R1F2: 'LR',
|
|
25
|
+
R2F1: 'LR',
|
|
26
|
+
R1R2: 'LL',
|
|
27
|
+
R2R1: 'LL',
|
|
28
|
+
F1F2: 'RR',
|
|
29
|
+
F2F1: 'RR',
|
|
30
|
+
F1R2: 'RL',
|
|
31
|
+
F2R1: 'RL',
|
|
32
|
+
},
|
|
33
|
+
ff: {
|
|
34
|
+
F2F1: 'LR',
|
|
35
|
+
R1R2: 'LR',
|
|
36
|
+
F2R1: 'LL',
|
|
37
|
+
R1F2: 'LL',
|
|
38
|
+
R2F1: 'RR',
|
|
39
|
+
F1R2: 'RR',
|
|
40
|
+
R2R1: 'RL',
|
|
41
|
+
F1F2: 'RL',
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
exports.pairMap = {
|
|
45
|
+
LR: 'color_pair_lr',
|
|
46
|
+
LL: 'color_pair_ll',
|
|
47
|
+
RR: 'color_pair_rr',
|
|
48
|
+
RL: 'color_pair_rl',
|
|
49
|
+
};
|
|
50
|
+
// manually calculated by running
|
|
51
|
+
// const color = require('color')
|
|
52
|
+
// Object.fromEntries(Object.entries(fillColor).map(([key,val])=>{
|
|
53
|
+
// return [key, color(val).darken('0.3').hex()]
|
|
54
|
+
// }))
|
|
55
|
+
// this avoids (expensive) use of Color module at runtime
|
|
56
|
+
exports.strokeColor = {
|
|
57
|
+
color_fwd_strand_not_proper: (0, material_1.alpha)('#CA6767', 0.8),
|
|
58
|
+
color_rev_strand_not_proper: (0, material_1.alpha)('#7272AA', 0.8),
|
|
59
|
+
color_fwd_strand: (0, material_1.alpha)('#DC2A2A', 0.8),
|
|
60
|
+
color_rev_strand: (0, material_1.alpha)('#4141BA', 0.8),
|
|
61
|
+
color_fwd_missing_mate: (0, material_1.alpha)('#921111', 0.8),
|
|
62
|
+
color_rev_missing_mate: (0, material_1.alpha)('#111192', 0.8),
|
|
63
|
+
color_fwd_diff_chr: (0, material_1.alpha)('#000000', 0.8),
|
|
64
|
+
color_rev_diff_chr: (0, material_1.alpha)('#696969', 0.8),
|
|
65
|
+
color_pair_lr: (0, material_1.alpha)('#8C8C8C', 0.8),
|
|
66
|
+
color_pair_rr: (0, material_1.alpha)('#00005A', 0.8),
|
|
67
|
+
color_pair_rl: (0, material_1.alpha)('#005A5A', 0.8),
|
|
68
|
+
color_pair_ll: (0, material_1.alpha)('#005A00', 0.8),
|
|
69
|
+
color_nostrand: (0, material_1.alpha)('#8C8C8C', 0.8),
|
|
70
|
+
color_interchrom: (0, material_1.alpha)('#5A005A', 0.8),
|
|
71
|
+
color_longinsert: (0, material_1.alpha)('#B30000', 0.8),
|
|
72
|
+
color_shortinsert: (0, material_1.alpha)('#FF3A5C', 0.8),
|
|
73
|
+
color_unknown: (0, material_1.alpha)('#555', 0.8),
|
|
74
|
+
};
|
|
75
|
+
const defaultColor = exports.strokeColor.color_unknown;
|
|
76
|
+
function getPairedOrientationColorOrDefault(f) {
|
|
77
|
+
const type = exports.orientationTypes.fr;
|
|
78
|
+
const r = type[f.pair_orientation || ''];
|
|
79
|
+
const type2 = exports.pairMap[r];
|
|
80
|
+
return r === 'LR' ? undefined : exports.strokeColor[type2];
|
|
81
|
+
}
|
|
82
|
+
function getLongReadOrientationColorOrDefault(s1, s2) {
|
|
83
|
+
if (s1 === -1 && s2 === 1) {
|
|
84
|
+
return exports.strokeColor.color_pair_rr;
|
|
85
|
+
}
|
|
86
|
+
else if (s1 === 1 && s2 === -1) {
|
|
87
|
+
return exports.strokeColor.color_pair_ll;
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
return exports.strokeColor.color_unknown;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
function getLongReadOrientationAbnormal(s1, s2) {
|
|
94
|
+
if (s1 === -1 && s2 === 1) {
|
|
95
|
+
return true;
|
|
96
|
+
}
|
|
97
|
+
else if (s1 === 1 && s2 === -1) {
|
|
98
|
+
return true;
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
function isAbnormalOrientation(f) {
|
|
105
|
+
const type = exports.orientationTypes.fr;
|
|
106
|
+
const r = type[f.pair_orientation || ''];
|
|
107
|
+
return r !== 'LR';
|
|
108
|
+
}
|
|
109
|
+
function getPairedOrientationColor(f) {
|
|
110
|
+
return getPairedOrientationColorOrDefault(f) || defaultColor;
|
|
111
|
+
}
|
|
@@ -5,3 +5,4 @@ export declare function hasPairedReads(features: Map<string, Feature>): boolean;
|
|
|
5
5
|
export declare function findMatchingAlt(feat1: Feature, feat2: Feature): import("@gmod/vcf").Breakend | undefined;
|
|
6
6
|
export declare function getMatchedBreakendFeatures(feats: Map<string, Feature>): Feature[][];
|
|
7
7
|
export declare function getMatchedTranslocationFeatures(feats: Map<string, Feature>): Feature[][];
|
|
8
|
+
export declare function getMatchedPairedFeatures(feats: Map<string, Feature>): Feature[][];
|