@jbrowse/plugin-breakpoint-split-view 2.15.0 → 2.15.2

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.
Files changed (35) hide show
  1. package/dist/BreakpointSplitView/BreakpointSplitView.d.ts +29 -1
  2. package/dist/BreakpointSplitView/BreakpointSplitView.js +71 -32
  3. package/dist/BreakpointSplitView/components/AlignmentConnections.js +43 -12
  4. package/dist/BreakpointSplitView/components/BreakpointSplitView.js +12 -10
  5. package/dist/BreakpointSplitView/components/BreakpointSplitViewOverlay.js +6 -5
  6. package/dist/BreakpointSplitView/components/Overlay.js +8 -3
  7. package/dist/BreakpointSplitView/components/PairedFeatures.d.ts +9 -0
  8. package/dist/BreakpointSplitView/components/PairedFeatures.js +100 -0
  9. package/dist/BreakpointSplitView/components/getOrientationColor.d.ts +41 -0
  10. package/dist/BreakpointSplitView/components/getOrientationColor.js +111 -0
  11. package/dist/BreakpointSplitView/components/util.d.ts +1 -0
  12. package/dist/BreakpointSplitView/components/util.js +26 -0
  13. package/dist/BreakpointSplitView/model.d.ts +24 -5
  14. package/dist/BreakpointSplitView/model.js +35 -7
  15. package/dist/BreakpointSplitView/svgcomponents/SVGBreakpointSplitView.js +5 -8
  16. package/dist/BreakpointSplitView/util.d.ts +1 -0
  17. package/dist/BreakpointSplitView/util.js +1 -0
  18. package/esm/BreakpointSplitView/BreakpointSplitView.d.ts +29 -1
  19. package/esm/BreakpointSplitView/BreakpointSplitView.js +71 -32
  20. package/esm/BreakpointSplitView/components/AlignmentConnections.js +44 -13
  21. package/esm/BreakpointSplitView/components/BreakpointSplitView.js +11 -9
  22. package/esm/BreakpointSplitView/components/BreakpointSplitViewOverlay.js +6 -5
  23. package/esm/BreakpointSplitView/components/Overlay.js +8 -3
  24. package/esm/BreakpointSplitView/components/PairedFeatures.d.ts +9 -0
  25. package/esm/BreakpointSplitView/components/PairedFeatures.js +75 -0
  26. package/esm/BreakpointSplitView/components/getOrientationColor.d.ts +41 -0
  27. package/esm/BreakpointSplitView/components/getOrientationColor.js +103 -0
  28. package/esm/BreakpointSplitView/components/util.d.ts +1 -0
  29. package/esm/BreakpointSplitView/components/util.js +25 -0
  30. package/esm/BreakpointSplitView/model.d.ts +24 -5
  31. package/esm/BreakpointSplitView/model.js +35 -7
  32. package/esm/BreakpointSplitView/svgcomponents/SVGBreakpointSplitView.js +5 -8
  33. package/esm/BreakpointSplitView/util.d.ts +1 -0
  34. package/esm/BreakpointSplitView/util.js +1 -1
  35. 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
- snapshotFromBreakendFeature(feature: Feature, view: {
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
- snapshotFromBreakendFeature(feature, view) {
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 = assembly.getCanonicalRefName(INFO.CHR2[0]);
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 = assembly.getCanonicalRefName(matePosition[0]);
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 = assembly.getCanonicalRefName(mate.refName);
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
- if (!topRegion || !bottomRegion) {
63
- throw new Error('unable to find the refName for the top or bottom of the breakpoint view');
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 + startMod;
68
- topMarkedRegion[1].start = startPos + startMod;
69
- bottomMarkedRegion[0].end = endPos + endMod;
70
- bottomMarkedRegion[1].start = endPos + endMod;
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 assembly = assemblyManager.get(views[0].assemblyNames[0]);
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", ...(0, util_1.getStrokeProps)(theme.palette.text.disabled), "data-testid": layoutMatches.length ? `${trackId}-loaded` : trackId }, layoutMatches.map(chunk => {
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 level
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
- x1 + 200 * f1.get('strand') * (reversed1 ? -1 : 1),
104
- y1,
105
- x2 -
106
- 200 *
107
- f2.get('strand') *
108
- (reversed2 ? -1 : 1) *
109
- (hasPaired ? -1 : 1),
110
- y2,
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 mobx_state_tree_1 = require("mobx-state-tree");
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, mobx_state_tree_1.getEnv)(model);
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", { style: { position: 'relative' } }, views.map((view, idx) => {
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
- if (idx === views.length - 1) {
32
- return viewComponent;
33
- }
34
- return [
35
- viewComponent,
36
- react_1.default.createElement("div", { key: `${view.id}-divider`, className: classes.viewDivider }),
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 the child component needs to
55
- // getBoundingClientRect on the this components SVG, and we cannot
56
- // rely on using getBoundingClientRect in this component to make
57
- // sure this works because if it gets shifted around by another
58
- // element, this will not re-render necessarily
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
- if (((_b = tracks[0]) === null || _b === void 0 ? void 0 : _b.type) === 'VariantTrack') {
19
- return model.hasTranslocations(trackId) ? (react_1.default.createElement(Translocations_1.default, { ...props })) : (react_1.default.createElement(Breakends_1.default, { ...props }));
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[][];