@jbrowse/plugin-breakpoint-split-view 2.6.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/LICENSE +201 -0
- package/dist/BreakpointAlignmentsFeatureDetail/BreakpointAlignmentsFeatureDetail.d.ts +5 -0
- package/dist/BreakpointAlignmentsFeatureDetail/BreakpointAlignmentsFeatureDetail.js +21 -0
- package/dist/BreakpointAlignmentsFeatureDetail/BreakpointAlignmentsFeatureDetail.js.map +1 -0
- package/dist/BreakpointAlignmentsFeatureDetail/index.d.ts +3 -0
- package/dist/BreakpointAlignmentsFeatureDetail/index.js +57 -0
- package/dist/BreakpointAlignmentsFeatureDetail/index.js.map +1 -0
- package/dist/BreakpointSplitView/BreakpointSplitView.d.ts +23 -0
- package/dist/BreakpointSplitView/BreakpointSplitView.js +92 -0
- package/dist/BreakpointSplitView/BreakpointSplitView.js.map +1 -0
- package/dist/BreakpointSplitView/components/AlignmentConnections.d.ts +9 -0
- package/dist/BreakpointSplitView/components/AlignmentConnections.js +133 -0
- package/dist/BreakpointSplitView/components/AlignmentConnections.js.map +1 -0
- package/dist/BreakpointSplitView/components/Breakends.d.ts +9 -0
- package/dist/BreakpointSplitView/components/Breakends.js +117 -0
- package/dist/BreakpointSplitView/components/Breakends.js.map +1 -0
- package/dist/BreakpointSplitView/components/BreakpointSplitView.d.ts +6 -0
- package/dist/BreakpointSplitView/components/BreakpointSplitView.js +89 -0
- package/dist/BreakpointSplitView/components/BreakpointSplitView.js.map +1 -0
- package/dist/BreakpointSplitView/components/ExportSvgDialog.d.ts +8 -0
- package/dist/BreakpointSplitView/components/ExportSvgDialog.js +84 -0
- package/dist/BreakpointSplitView/components/ExportSvgDialog.js.map +1 -0
- package/dist/BreakpointSplitView/components/Overlay.d.ts +9 -0
- package/dist/BreakpointSplitView/components/Overlay.js +23 -0
- package/dist/BreakpointSplitView/components/Overlay.js.map +1 -0
- package/dist/BreakpointSplitView/components/Translocations.d.ts +9 -0
- package/dist/BreakpointSplitView/components/Translocations.js +113 -0
- package/dist/BreakpointSplitView/components/Translocations.js.map +1 -0
- package/dist/BreakpointSplitView/components/util.d.ts +7 -0
- package/dist/BreakpointSplitView/components/util.js +116 -0
- package/dist/BreakpointSplitView/components/util.js.map +1 -0
- package/dist/BreakpointSplitView/index.d.ts +3 -0
- package/dist/BreakpointSplitView/index.js +43 -0
- package/dist/BreakpointSplitView/index.js.map +1 -0
- package/dist/BreakpointSplitView/model.d.ts +299 -0
- package/dist/BreakpointSplitView/model.js +245 -0
- package/dist/BreakpointSplitView/model.js.map +1 -0
- package/dist/BreakpointSplitView/svgcomponents/SVGBackground.d.ts +6 -0
- package/dist/BreakpointSplitView/svgcomponents/SVGBackground.js +13 -0
- package/dist/BreakpointSplitView/svgcomponents/SVGBackground.js.map +1 -0
- package/dist/BreakpointSplitView/svgcomponents/SVGBreakpointSplitView.d.ts +4 -0
- package/dist/BreakpointSplitView/svgcomponents/SVGBreakpointSplitView.js +83 -0
- package/dist/BreakpointSplitView/svgcomponents/SVGBreakpointSplitView.js.map +1 -0
- package/dist/BreakpointSplitView/util.d.ts +18 -0
- package/dist/BreakpointSplitView/util.js +56 -0
- package/dist/BreakpointSplitView/util.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +21 -0
- package/dist/index.js.map +1 -0
- package/esm/BreakpointAlignmentsFeatureDetail/BreakpointAlignmentsFeatureDetail.d.ts +5 -0
- package/esm/BreakpointAlignmentsFeatureDetail/BreakpointAlignmentsFeatureDetail.js +16 -0
- package/esm/BreakpointAlignmentsFeatureDetail/BreakpointAlignmentsFeatureDetail.js.map +1 -0
- package/esm/BreakpointAlignmentsFeatureDetail/index.d.ts +3 -0
- package/esm/BreakpointAlignmentsFeatureDetail/index.js +32 -0
- package/esm/BreakpointAlignmentsFeatureDetail/index.js.map +1 -0
- package/esm/BreakpointSplitView/BreakpointSplitView.d.ts +23 -0
- package/esm/BreakpointSplitView/BreakpointSplitView.js +86 -0
- package/esm/BreakpointSplitView/BreakpointSplitView.js.map +1 -0
- package/esm/BreakpointSplitView/components/AlignmentConnections.d.ts +9 -0
- package/esm/BreakpointSplitView/components/AlignmentConnections.js +108 -0
- package/esm/BreakpointSplitView/components/AlignmentConnections.js.map +1 -0
- package/esm/BreakpointSplitView/components/Breakends.d.ts +9 -0
- package/esm/BreakpointSplitView/components/Breakends.js +92 -0
- package/esm/BreakpointSplitView/components/Breakends.js.map +1 -0
- package/esm/BreakpointSplitView/components/BreakpointSplitView.d.ts +6 -0
- package/esm/BreakpointSplitView/components/BreakpointSplitView.js +61 -0
- package/esm/BreakpointSplitView/components/BreakpointSplitView.js.map +1 -0
- package/esm/BreakpointSplitView/components/ExportSvgDialog.d.ts +8 -0
- package/esm/BreakpointSplitView/components/ExportSvgDialog.js +58 -0
- package/esm/BreakpointSplitView/components/ExportSvgDialog.js.map +1 -0
- package/esm/BreakpointSplitView/components/Overlay.d.ts +9 -0
- package/esm/BreakpointSplitView/components/Overlay.js +18 -0
- package/esm/BreakpointSplitView/components/Overlay.js.map +1 -0
- package/esm/BreakpointSplitView/components/Translocations.d.ts +9 -0
- package/esm/BreakpointSplitView/components/Translocations.js +88 -0
- package/esm/BreakpointSplitView/components/Translocations.js.map +1 -0
- package/esm/BreakpointSplitView/components/util.d.ts +7 -0
- package/esm/BreakpointSplitView/components/util.js +107 -0
- package/esm/BreakpointSplitView/components/util.js.map +1 -0
- package/esm/BreakpointSplitView/index.d.ts +3 -0
- package/esm/BreakpointSplitView/index.js +15 -0
- package/esm/BreakpointSplitView/index.js.map +1 -0
- package/esm/BreakpointSplitView/model.d.ts +299 -0
- package/esm/BreakpointSplitView/model.js +216 -0
- package/esm/BreakpointSplitView/model.js.map +1 -0
- package/esm/BreakpointSplitView/svgcomponents/SVGBackground.d.ts +6 -0
- package/esm/BreakpointSplitView/svgcomponents/SVGBackground.js +7 -0
- package/esm/BreakpointSplitView/svgcomponents/SVGBackground.js.map +1 -0
- package/esm/BreakpointSplitView/svgcomponents/SVGBreakpointSplitView.d.ts +4 -0
- package/esm/BreakpointSplitView/svgcomponents/SVGBreakpointSplitView.js +76 -0
- package/esm/BreakpointSplitView/svgcomponents/SVGBreakpointSplitView.js.map +1 -0
- package/esm/BreakpointSplitView/util.d.ts +18 -0
- package/esm/BreakpointSplitView/util.js +49 -0
- package/esm/BreakpointSplitView/util.js.map +1 -0
- package/esm/index.d.ts +7 -0
- package/esm/index.js +15 -0
- package/esm/index.js.map +1 -0
- package/package.json +63 -0
- package/src/BreakpointAlignmentsFeatureDetail/BreakpointAlignmentsFeatureDetail.tsx +24 -0
- package/src/BreakpointAlignmentsFeatureDetail/index.ts +35 -0
- package/src/BreakpointSplitView/BreakpointSplitView.ts +100 -0
- package/src/BreakpointSplitView/components/AlignmentConnections.tsx +166 -0
- package/src/BreakpointSplitView/components/Breakends.tsx +141 -0
- package/src/BreakpointSplitView/components/BreakpointSplitView.tsx +95 -0
- package/src/BreakpointSplitView/components/ExportSvgDialog.tsx +149 -0
- package/src/BreakpointSplitView/components/Overlay.tsx +29 -0
- package/src/BreakpointSplitView/components/Translocations.tsx +147 -0
- package/src/BreakpointSplitView/components/util.ts +127 -0
- package/src/BreakpointSplitView/index.ts +17 -0
- package/src/BreakpointSplitView/model.ts +333 -0
- package/src/BreakpointSplitView/svgcomponents/SVGBackground.tsx +21 -0
- package/src/BreakpointSplitView/svgcomponents/SVGBreakpointSplitView.tsx +179 -0
- package/src/BreakpointSplitView/util.ts +89 -0
- package/src/index.test.ts +3 -0
- package/src/index.ts +15 -0
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { LinearGenomeViewModel } from '@jbrowse/plugin-linear-genome-view';
|
|
2
|
+
import { LayoutRecord } from './model';
|
|
3
|
+
type LGV = LinearGenomeViewModel;
|
|
4
|
+
interface Display {
|
|
5
|
+
height: number;
|
|
6
|
+
scrollTop: number;
|
|
7
|
+
SNPCoverageDisplay?: {
|
|
8
|
+
height: number;
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
interface Track {
|
|
12
|
+
displays: Display[];
|
|
13
|
+
}
|
|
14
|
+
export declare function getPxFromCoordinate(view: LGV, refName: string, coord: number): number;
|
|
15
|
+
export declare function yPos(trackId: string, level: number, views: LGV[], tracks: Track[], c: LayoutRecord, getYPosOverride?: (trackId: string, level: number) => number): number;
|
|
16
|
+
export declare const useNextFrame: (variable: unknown) => void;
|
|
17
|
+
export declare function intersect<T>(cb: (l: T) => string, a1?: T[], a2?: T[], ...rest: T[][]): T[];
|
|
18
|
+
export {};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { useState, useEffect } from 'react';
|
|
2
|
+
import { clamp } from '@jbrowse/core/util';
|
|
3
|
+
const [, TOP, , BOTTOM] = [0, 1, 2, 3];
|
|
4
|
+
function cheight(chunk) {
|
|
5
|
+
return chunk[BOTTOM] - chunk[TOP];
|
|
6
|
+
}
|
|
7
|
+
function heightFromSpecificLevel(views, trackId, level, getYPosOverride) {
|
|
8
|
+
var _a;
|
|
9
|
+
return getYPosOverride
|
|
10
|
+
? getYPosOverride(trackId, level)
|
|
11
|
+
: ((_a = views[level].trackRefs[trackId]) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect().top) || 0;
|
|
12
|
+
}
|
|
13
|
+
export function getPxFromCoordinate(view, refName, coord) {
|
|
14
|
+
return ((view.bpToPx({ refName, coord }) || {}).offsetPx || 0) - view.offsetPx;
|
|
15
|
+
}
|
|
16
|
+
// get's the yposition of a layout record in a track
|
|
17
|
+
export function yPos(trackId, level, views, tracks, c, getYPosOverride) {
|
|
18
|
+
const display = tracks[level].displays[0];
|
|
19
|
+
const min = 0;
|
|
20
|
+
const max = display.height;
|
|
21
|
+
let offset = 0;
|
|
22
|
+
const { SNPCoverageDisplay } = display;
|
|
23
|
+
if (SNPCoverageDisplay) {
|
|
24
|
+
offset = SNPCoverageDisplay.height;
|
|
25
|
+
}
|
|
26
|
+
const yPos = getYPosOverride ? 0 : display.scrollTop;
|
|
27
|
+
return (clamp(c[TOP] - yPos + cheight(c) / 2 + offset, min, max) +
|
|
28
|
+
heightFromSpecificLevel(views, trackId, level, getYPosOverride) +
|
|
29
|
+
display.scrollTop);
|
|
30
|
+
}
|
|
31
|
+
// we combo a useEffect and useState combo to force rerender on snap changing.
|
|
32
|
+
// the setup of this being a useEffect+useState makes it re-render once the
|
|
33
|
+
// useEffect is called, which is generally the "next frame". If we removed the
|
|
34
|
+
// below use
|
|
35
|
+
export const useNextFrame = (variable) => {
|
|
36
|
+
const [, setNextFrameState] = useState();
|
|
37
|
+
useEffect(() => {
|
|
38
|
+
setNextFrameState(variable);
|
|
39
|
+
}, [variable]);
|
|
40
|
+
};
|
|
41
|
+
// https://stackoverflow.com/a/49186706/2129219 the array-intersection package
|
|
42
|
+
// on npm has a large kb size, and we are just intersecting open track ids so
|
|
43
|
+
// simple is better
|
|
44
|
+
export function intersect(cb, a1 = [], a2 = [], ...rest) {
|
|
45
|
+
const ids = new Set(a2.map(elt => cb(elt)));
|
|
46
|
+
const a12 = a1.filter(value => ids.has(cb(value)));
|
|
47
|
+
return rest.length === 0 ? a12 : intersect(cb, a12, ...rest);
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=util.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"util.js","sourceRoot":"","sources":["../../src/BreakpointSplitView/util.ts"],"names":[],"mappings":"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"}
|
package/esm/index.d.ts
ADDED
package/esm/index.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import Plugin from '@jbrowse/core/Plugin';
|
|
2
|
+
import BreakpointAlignmentsWidgetF from './BreakpointAlignmentsFeatureDetail';
|
|
3
|
+
import BreakpointSplitViewF from './BreakpointSplitView';
|
|
4
|
+
export default class BreakpointSplitViewPlugin extends Plugin {
|
|
5
|
+
constructor() {
|
|
6
|
+
super(...arguments);
|
|
7
|
+
this.name = 'BreakpointSplitViewPlugin';
|
|
8
|
+
}
|
|
9
|
+
install(pluginManager) {
|
|
10
|
+
BreakpointSplitViewF(pluginManager);
|
|
11
|
+
BreakpointAlignmentsWidgetF(pluginManager);
|
|
12
|
+
}
|
|
13
|
+
configure() { }
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=index.js.map
|
package/esm/index.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,MAAM,MAAM,sBAAsB,CAAA;AACzC,OAAO,2BAA2B,MAAM,qCAAqC,CAAA;AAC7E,OAAO,oBAAoB,MAAM,uBAAuB,CAAA;AAExD,MAAM,CAAC,OAAO,OAAO,yBAA0B,SAAQ,MAAM;IAA7D;;QACE,SAAI,GAAG,2BAA2B,CAAA;IAQpC,CAAC;IANC,OAAO,CAAC,aAA4B;QAClC,oBAAoB,CAAC,aAAa,CAAC,CAAA;QACnC,2BAA2B,CAAC,aAAa,CAAC,CAAA;IAC5C,CAAC;IAED,SAAS,KAAI,CAAC;CACf"}
|
package/package.json
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@jbrowse/plugin-breakpoint-split-view",
|
|
3
|
+
"version": "2.6.1",
|
|
4
|
+
"description": "JBrowse 2 breakpoint detail split view",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"jbrowse",
|
|
7
|
+
"jbrowse2"
|
|
8
|
+
],
|
|
9
|
+
"license": "Apache-2.0",
|
|
10
|
+
"homepage": "https://jbrowse.org",
|
|
11
|
+
"bugs": "https://github.com/GMOD/jbrowse-components/issues",
|
|
12
|
+
"repository": {
|
|
13
|
+
"type": "git",
|
|
14
|
+
"url": "https://github.com/GMOD/jbrowse-components.git",
|
|
15
|
+
"directory": "plugins/breakpoint-split-view"
|
|
16
|
+
},
|
|
17
|
+
"author": "JBrowse Team",
|
|
18
|
+
"distMain": "dist/index.js",
|
|
19
|
+
"srcMain": "src/index.ts",
|
|
20
|
+
"main": "dist/index.js",
|
|
21
|
+
"files": [
|
|
22
|
+
"dist",
|
|
23
|
+
"src",
|
|
24
|
+
"esm"
|
|
25
|
+
],
|
|
26
|
+
"scripts": {
|
|
27
|
+
"build": "npm-run-all build:*",
|
|
28
|
+
"test": "cd ../..; jest plugins/breakpoint-split-view",
|
|
29
|
+
"prepublishOnly": "yarn test",
|
|
30
|
+
"prepack": "yarn build && yarn useDist",
|
|
31
|
+
"postpack": "yarn useSrc",
|
|
32
|
+
"useDist": "node ../../scripts/useDist.js",
|
|
33
|
+
"useSrc": "node ../../scripts/useSrc.js",
|
|
34
|
+
"prebuild": "npm run clean",
|
|
35
|
+
"build:esm": "tsc --build tsconfig.build.esm.json",
|
|
36
|
+
"build:es5": "tsc --build tsconfig.build.es5.json",
|
|
37
|
+
"clean": "rimraf dist esm *.tsbuildinfo"
|
|
38
|
+
},
|
|
39
|
+
"dependencies": {
|
|
40
|
+
"@gmod/vcf": "^5.0.9",
|
|
41
|
+
"@mui/icons-material": "^5.0.1",
|
|
42
|
+
"@types/file-saver": "^2.0.1",
|
|
43
|
+
"file-saver": "^2.0.0"
|
|
44
|
+
},
|
|
45
|
+
"peerDependencies": {
|
|
46
|
+
"@jbrowse/core": "^2.0.0",
|
|
47
|
+
"@jbrowse/plugin-linear-genome-view": "^2.0.0",
|
|
48
|
+
"@mui/material": "^5.0.0",
|
|
49
|
+
"mobx": "^6.0.0",
|
|
50
|
+
"mobx-react": "^7.0.0",
|
|
51
|
+
"mobx-state-tree": "^5.0.0",
|
|
52
|
+
"react": ">=16.8.4",
|
|
53
|
+
"react-dom": ">=16.8.4",
|
|
54
|
+
"tss-react": "^4.0.0"
|
|
55
|
+
},
|
|
56
|
+
"distModule": "esm/index.js",
|
|
57
|
+
"srcModule": "src/index.ts",
|
|
58
|
+
"module": "esm/index.js",
|
|
59
|
+
"publishConfig": {
|
|
60
|
+
"access": "public"
|
|
61
|
+
},
|
|
62
|
+
"gitHead": "1cbe7ba097fb2d2763c776e5e429e4670cdd583c"
|
|
63
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import Paper from '@mui/material/Paper'
|
|
2
|
+
import { observer } from 'mobx-react'
|
|
3
|
+
import React from 'react'
|
|
4
|
+
import {
|
|
5
|
+
BaseCoreDetails,
|
|
6
|
+
BaseAttributes,
|
|
7
|
+
} from '@jbrowse/core/BaseFeatureWidget/BaseFeatureDetail'
|
|
8
|
+
|
|
9
|
+
const BreakpointAlignmentsFeatureDetail = observer(
|
|
10
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
11
|
+
({ model }: { model: any }) => {
|
|
12
|
+
const { feature1, feature2 } = JSON.parse(JSON.stringify(model.featureData))
|
|
13
|
+
return (
|
|
14
|
+
<Paper data-testid="alignment-side-drawer">
|
|
15
|
+
<BaseCoreDetails title="Feature 1" feature={feature1} />
|
|
16
|
+
<BaseCoreDetails title="Feature 2" feature={feature2} />
|
|
17
|
+
<BaseAttributes title="Feature 1 attributes" feature={feature1} />
|
|
18
|
+
<BaseAttributes title="Feature 2 attributes" feature={feature2} />
|
|
19
|
+
</Paper>
|
|
20
|
+
)
|
|
21
|
+
},
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
export default BreakpointAlignmentsFeatureDetail
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { lazy } from 'react'
|
|
2
|
+
import { ConfigurationSchema } from '@jbrowse/core/configuration'
|
|
3
|
+
import PluginManager from '@jbrowse/core/PluginManager'
|
|
4
|
+
import { ElementId } from '@jbrowse/core/util/types/mst'
|
|
5
|
+
import { types } from 'mobx-state-tree'
|
|
6
|
+
import { WidgetType } from '@jbrowse/core/pluggableElementTypes'
|
|
7
|
+
|
|
8
|
+
const configSchema = ConfigurationSchema('BreakpointAlignmentsWidget', {})
|
|
9
|
+
|
|
10
|
+
const stateModel = types
|
|
11
|
+
.model('BreakpointAlignmentsWidget', {
|
|
12
|
+
id: ElementId,
|
|
13
|
+
type: types.literal('BreakpointAlignmentsWidget'),
|
|
14
|
+
featureData: types.frozen(),
|
|
15
|
+
})
|
|
16
|
+
.actions(self => ({
|
|
17
|
+
setFeatureData(data: unknown) {
|
|
18
|
+
self.featureData = data
|
|
19
|
+
},
|
|
20
|
+
clearFeatureData() {
|
|
21
|
+
self.featureData = undefined
|
|
22
|
+
},
|
|
23
|
+
}))
|
|
24
|
+
|
|
25
|
+
export default (pluginManager: PluginManager) => {
|
|
26
|
+
pluginManager.addWidgetType(() => {
|
|
27
|
+
return new WidgetType({
|
|
28
|
+
name: 'BreakpointAlignmentsWidget',
|
|
29
|
+
heading: 'Breakpoint feature details',
|
|
30
|
+
configSchema,
|
|
31
|
+
stateModel,
|
|
32
|
+
ReactComponent: lazy(() => import('./BreakpointAlignmentsFeatureDetail')),
|
|
33
|
+
})
|
|
34
|
+
})
|
|
35
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { getSession, Feature } from '@jbrowse/core/util'
|
|
2
|
+
import ViewType from '@jbrowse/core/pluggableElementTypes/ViewType'
|
|
3
|
+
import { parseBreakend } from '@gmod/vcf'
|
|
4
|
+
import { LinearGenomeViewModel } from '@jbrowse/plugin-linear-genome-view'
|
|
5
|
+
|
|
6
|
+
type LGV = LinearGenomeViewModel
|
|
7
|
+
|
|
8
|
+
export default class BreakpointSplitViewType extends ViewType {
|
|
9
|
+
snapshotFromBreakendFeature(feature: Feature, view: LGV) {
|
|
10
|
+
const alt = feature.get('ALT')?.[0]
|
|
11
|
+
const bnd = alt ? parseBreakend(alt) : undefined
|
|
12
|
+
const startPos = feature.get('start')
|
|
13
|
+
let endPos
|
|
14
|
+
const bpPerPx = 10
|
|
15
|
+
|
|
16
|
+
// TODO: Figure this out for multiple assembly names
|
|
17
|
+
const { assemblyName } = view.displayedRegions[0]
|
|
18
|
+
const { assemblyManager } = getSession(view)
|
|
19
|
+
const assembly = assemblyManager.get(assemblyName)
|
|
20
|
+
|
|
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 { getCanonicalRefName } = assembly
|
|
28
|
+
const featureRefName = getCanonicalRefName(feature.get('refName'))
|
|
29
|
+
const topRegion = assembly.regions.find(f => f.refName === featureRefName)
|
|
30
|
+
|
|
31
|
+
let mateRefName: string | undefined
|
|
32
|
+
let startMod = 0
|
|
33
|
+
let endMod = 0
|
|
34
|
+
|
|
35
|
+
// a VCF breakend feature
|
|
36
|
+
if (alt === '<TRA>') {
|
|
37
|
+
const INFO = feature.get('INFO')
|
|
38
|
+
endPos = INFO.END[0] - 1
|
|
39
|
+
mateRefName = getCanonicalRefName(INFO.CHR2[0])
|
|
40
|
+
} else if (bnd?.MatePosition) {
|
|
41
|
+
const matePosition = bnd.MatePosition.split(':')
|
|
42
|
+
endPos = +matePosition[1] - 1
|
|
43
|
+
mateRefName = getCanonicalRefName(matePosition[0])
|
|
44
|
+
if (bnd.Join === 'left') {
|
|
45
|
+
startMod = -1
|
|
46
|
+
}
|
|
47
|
+
if (bnd.MateDirection === 'left') {
|
|
48
|
+
endMod = -1
|
|
49
|
+
}
|
|
50
|
+
} else if (feature.get('mate')) {
|
|
51
|
+
// a generic 'mate' feature
|
|
52
|
+
const mate = feature.get('mate')
|
|
53
|
+
mateRefName = getCanonicalRefName(mate.refName)
|
|
54
|
+
endPos = mate.start
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (!mateRefName) {
|
|
58
|
+
throw new Error(
|
|
59
|
+
`unable to resolve mate refName ${mateRefName} in reference genome`,
|
|
60
|
+
)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const bottomRegion = assembly.regions.find(f => f.refName === mateRefName)
|
|
64
|
+
|
|
65
|
+
if (!topRegion || !bottomRegion) {
|
|
66
|
+
throw new Error(
|
|
67
|
+
`unable to find the refName for the top or bottom of the breakpoint view`,
|
|
68
|
+
)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const topMarkedRegion = [{ ...topRegion }, { ...topRegion }]
|
|
72
|
+
const bottomMarkedRegion = [{ ...bottomRegion }, { ...bottomRegion }]
|
|
73
|
+
topMarkedRegion[0].end = startPos + startMod
|
|
74
|
+
topMarkedRegion[1].start = startPos + startMod
|
|
75
|
+
bottomMarkedRegion[0].end = endPos + endMod
|
|
76
|
+
bottomMarkedRegion[1].start = endPos + endMod
|
|
77
|
+
return {
|
|
78
|
+
type: 'BreakpointSplitView',
|
|
79
|
+
views: [
|
|
80
|
+
{
|
|
81
|
+
type: 'LinearGenomeView',
|
|
82
|
+
displayedRegions: topMarkedRegion,
|
|
83
|
+
hideHeader: true,
|
|
84
|
+
bpPerPx,
|
|
85
|
+
offsetPx: (topRegion.start + feature.get('start')) / bpPerPx,
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
type: 'LinearGenomeView',
|
|
89
|
+
displayedRegions: bottomMarkedRegion,
|
|
90
|
+
hideHeader: true,
|
|
91
|
+
bpPerPx,
|
|
92
|
+
offsetPx: (bottomRegion.start + endPos) / bpPerPx,
|
|
93
|
+
},
|
|
94
|
+
],
|
|
95
|
+
displayName: `${
|
|
96
|
+
feature.get('name') || feature.get('id') || 'breakend'
|
|
97
|
+
} split detail`,
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import React, { useMemo, useState } from 'react'
|
|
2
|
+
import { observer } from 'mobx-react'
|
|
3
|
+
import { getSnapshot } from 'mobx-state-tree'
|
|
4
|
+
import { useTheme } from '@mui/material'
|
|
5
|
+
import { getSession } from '@jbrowse/core/util'
|
|
6
|
+
|
|
7
|
+
// locals
|
|
8
|
+
import {
|
|
9
|
+
getBadlyPairedAlignments,
|
|
10
|
+
getMatchedAlignmentFeatures,
|
|
11
|
+
hasPairedReads,
|
|
12
|
+
} from './util'
|
|
13
|
+
import { yPos, useNextFrame, getPxFromCoordinate } from '../util'
|
|
14
|
+
import { BreakpointViewModel } from '../model'
|
|
15
|
+
|
|
16
|
+
const [LEFT, , RIGHT] = [0, 1, 2, 3]
|
|
17
|
+
|
|
18
|
+
const AlignmentConnections = observer(function ({
|
|
19
|
+
model,
|
|
20
|
+
trackId,
|
|
21
|
+
parentRef,
|
|
22
|
+
getTrackYPosOverride,
|
|
23
|
+
}: {
|
|
24
|
+
model: BreakpointViewModel
|
|
25
|
+
trackId: string
|
|
26
|
+
parentRef: React.RefObject<SVGSVGElement>
|
|
27
|
+
getTrackYPosOverride?: (trackId: string, level: number) => number
|
|
28
|
+
}) {
|
|
29
|
+
const { views, showIntraviewLinks } = model
|
|
30
|
+
const theme = useTheme()
|
|
31
|
+
const session = getSession(model)
|
|
32
|
+
const snap = getSnapshot(model)
|
|
33
|
+
const { assemblyManager } = session
|
|
34
|
+
const assembly = assemblyManager.get(views[0].assemblyNames[0])
|
|
35
|
+
useNextFrame(snap)
|
|
36
|
+
const allFeatures = model.getTrackFeatures(trackId)
|
|
37
|
+
const hasPaired = useMemo(() => hasPairedReads(allFeatures), [allFeatures])
|
|
38
|
+
|
|
39
|
+
const layoutMatches = useMemo(() => {
|
|
40
|
+
const layoutMatches = model.getMatchedFeaturesInLayout(
|
|
41
|
+
trackId,
|
|
42
|
+
hasPaired
|
|
43
|
+
? getBadlyPairedAlignments(allFeatures)
|
|
44
|
+
: getMatchedAlignmentFeatures(allFeatures),
|
|
45
|
+
)
|
|
46
|
+
if (!hasPaired) {
|
|
47
|
+
layoutMatches.forEach(m => {
|
|
48
|
+
m.sort((a, b) => a.feature.get('clipPos') - b.feature.get('clipPos'))
|
|
49
|
+
})
|
|
50
|
+
}
|
|
51
|
+
return layoutMatches
|
|
52
|
+
}, [allFeatures, trackId, hasPaired, model])
|
|
53
|
+
|
|
54
|
+
const [mouseoverElt, setMouseoverElt] = useState<string>()
|
|
55
|
+
|
|
56
|
+
let yOffset = 0
|
|
57
|
+
if (parentRef.current) {
|
|
58
|
+
const rect = parentRef.current.getBoundingClientRect()
|
|
59
|
+
yOffset = rect.top
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (!assembly) {
|
|
63
|
+
return null
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return (
|
|
67
|
+
<g
|
|
68
|
+
stroke={theme.palette.text.disabled}
|
|
69
|
+
fill="none"
|
|
70
|
+
data-testid={layoutMatches.length ? `${trackId}-loaded` : trackId}
|
|
71
|
+
>
|
|
72
|
+
{layoutMatches.map(chunk => {
|
|
73
|
+
const ret = []
|
|
74
|
+
// we follow a path in the list of chunks, not from top to bottom, just in series
|
|
75
|
+
// following x1,y1 -> x2,y2
|
|
76
|
+
for (let i = 0; i < chunk.length - 1; i++) {
|
|
77
|
+
const { layout: c1, feature: f1, level: level1 } = chunk[i]
|
|
78
|
+
const { layout: c2, feature: f2, level: level2 } = chunk[i + 1]
|
|
79
|
+
|
|
80
|
+
if (!c1 || !c2) {
|
|
81
|
+
console.warn('received null layout for a overlay feature')
|
|
82
|
+
return null
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// disable rendering connections in a single level
|
|
86
|
+
if (!showIntraviewLinks && level1 === level2) {
|
|
87
|
+
return null
|
|
88
|
+
}
|
|
89
|
+
const f1ref = assembly.getCanonicalRefName(f1.get('refName'))
|
|
90
|
+
const f2ref = assembly.getCanonicalRefName(f2.get('refName'))
|
|
91
|
+
|
|
92
|
+
if (!f1ref || !f2ref) {
|
|
93
|
+
throw new Error(`unable to find ref for ${f1ref || f2ref}`)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const s1 = f1.get('strand')
|
|
97
|
+
const s2 = f2.get('strand')
|
|
98
|
+
const p1 = c1[s1 === -1 ? LEFT : RIGHT]
|
|
99
|
+
const sn1 = s2 === -1
|
|
100
|
+
const p2 = hasPaired ? c2[sn1 ? LEFT : RIGHT] : c2[sn1 ? RIGHT : LEFT]
|
|
101
|
+
const x1 = getPxFromCoordinate(views[level1], f1ref, p1)
|
|
102
|
+
const x2 = getPxFromCoordinate(views[level2], f2ref, p2)
|
|
103
|
+
const reversed1 = views[level1].pxToBp(x1).reversed
|
|
104
|
+
const reversed2 = views[level2].pxToBp(x2).reversed
|
|
105
|
+
const tracks = views.map(v => v.getTrack(trackId))
|
|
106
|
+
const y1 =
|
|
107
|
+
yPos(trackId, level1, views, tracks, c1, getTrackYPosOverride) -
|
|
108
|
+
yOffset
|
|
109
|
+
const y2 =
|
|
110
|
+
yPos(trackId, level2, views, tracks, c2, getTrackYPosOverride) -
|
|
111
|
+
yOffset
|
|
112
|
+
|
|
113
|
+
// possible todo: use totalCurveHeight to possibly make alternative
|
|
114
|
+
// squiggle if the S is too small
|
|
115
|
+
const path = [
|
|
116
|
+
'M',
|
|
117
|
+
x1,
|
|
118
|
+
y1,
|
|
119
|
+
'C',
|
|
120
|
+
x1 + 200 * f1.get('strand') * (reversed1 ? -1 : 1),
|
|
121
|
+
y1,
|
|
122
|
+
x2 -
|
|
123
|
+
200 *
|
|
124
|
+
f2.get('strand') *
|
|
125
|
+
(reversed2 ? -1 : 1) *
|
|
126
|
+
(hasPaired ? -1 : 1),
|
|
127
|
+
y2,
|
|
128
|
+
x2,
|
|
129
|
+
y2,
|
|
130
|
+
].join(' ')
|
|
131
|
+
const id = `${f1.id()}-${f2.id()}`
|
|
132
|
+
ret.push(
|
|
133
|
+
<path
|
|
134
|
+
d={path}
|
|
135
|
+
key={id}
|
|
136
|
+
data-testid="r1"
|
|
137
|
+
strokeWidth={mouseoverElt === id ? 5 : 1}
|
|
138
|
+
onClick={() => {
|
|
139
|
+
const featureWidget = session.addWidget?.(
|
|
140
|
+
'BreakpointAlignmentsWidget',
|
|
141
|
+
'breakpointAlignments',
|
|
142
|
+
{
|
|
143
|
+
featureData: {
|
|
144
|
+
feature1: (
|
|
145
|
+
allFeatures.get(f1.id()) || { toJSON: () => {} }
|
|
146
|
+
).toJSON(),
|
|
147
|
+
feature2: (
|
|
148
|
+
allFeatures.get(f2.id()) || { toJSON: () => {} }
|
|
149
|
+
).toJSON(),
|
|
150
|
+
},
|
|
151
|
+
},
|
|
152
|
+
)
|
|
153
|
+
session.showWidget?.(featureWidget)
|
|
154
|
+
}}
|
|
155
|
+
onMouseOver={() => setMouseoverElt(id)}
|
|
156
|
+
onMouseOut={() => setMouseoverElt(undefined)}
|
|
157
|
+
/>,
|
|
158
|
+
)
|
|
159
|
+
}
|
|
160
|
+
return ret
|
|
161
|
+
})}
|
|
162
|
+
</g>
|
|
163
|
+
)
|
|
164
|
+
})
|
|
165
|
+
|
|
166
|
+
export default AlignmentConnections
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import React, { useState, useMemo } from 'react'
|
|
2
|
+
import { getSession } from '@jbrowse/core/util'
|
|
3
|
+
import { observer } from 'mobx-react'
|
|
4
|
+
import { getSnapshot } from 'mobx-state-tree'
|
|
5
|
+
|
|
6
|
+
// locals
|
|
7
|
+
import { findMatchingAlt, getMatchedBreakendFeatures } from './util'
|
|
8
|
+
import { yPos, getPxFromCoordinate, useNextFrame } from '../util'
|
|
9
|
+
import { BreakpointViewModel } from '../model'
|
|
10
|
+
|
|
11
|
+
const [LEFT] = [0, 1, 2, 3]
|
|
12
|
+
|
|
13
|
+
const Breakends = observer(function ({
|
|
14
|
+
model,
|
|
15
|
+
trackId,
|
|
16
|
+
parentRef: ref,
|
|
17
|
+
getTrackYPosOverride,
|
|
18
|
+
}: {
|
|
19
|
+
model: BreakpointViewModel
|
|
20
|
+
trackId: string
|
|
21
|
+
parentRef: React.RefObject<SVGSVGElement>
|
|
22
|
+
getTrackYPosOverride?: (trackId: string, level: number) => number
|
|
23
|
+
}) {
|
|
24
|
+
const { views } = model
|
|
25
|
+
const session = getSession(model)
|
|
26
|
+
const { assemblyManager } = session
|
|
27
|
+
const totalFeatures = model.getTrackFeatures(trackId)
|
|
28
|
+
const layoutMatches = useMemo(
|
|
29
|
+
() =>
|
|
30
|
+
model.getMatchedFeaturesInLayout(
|
|
31
|
+
trackId,
|
|
32
|
+
getMatchedBreakendFeatures(totalFeatures),
|
|
33
|
+
),
|
|
34
|
+
[totalFeatures, trackId, model],
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
const [mouseoverElt, setMouseoverElt] = useState<string>()
|
|
38
|
+
const snap = getSnapshot(model)
|
|
39
|
+
useNextFrame(snap)
|
|
40
|
+
const assembly = assemblyManager.get(views[0].assemblyNames[0])
|
|
41
|
+
|
|
42
|
+
if (!assembly) {
|
|
43
|
+
return null
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
let yoff = 0
|
|
47
|
+
if (ref.current) {
|
|
48
|
+
const rect = ref.current.getBoundingClientRect()
|
|
49
|
+
yoff = rect.top
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return (
|
|
53
|
+
<g
|
|
54
|
+
stroke="green"
|
|
55
|
+
strokeWidth={5}
|
|
56
|
+
fill="none"
|
|
57
|
+
data-testid={layoutMatches.length ? `${trackId}-loaded` : trackId}
|
|
58
|
+
>
|
|
59
|
+
{layoutMatches.map(chunk => {
|
|
60
|
+
const ret = []
|
|
61
|
+
// we follow a path in the list of chunks, not from top to bottom, just
|
|
62
|
+
// in series following x1,y1 -> x2,y2
|
|
63
|
+
for (let i = 0; i < chunk.length - 1; i += 1) {
|
|
64
|
+
const { layout: c1, feature: f1, level: level1 } = chunk[i]
|
|
65
|
+
const { layout: c2, feature: f2, level: level2 } = chunk[i + 1]
|
|
66
|
+
const id = f1.id()
|
|
67
|
+
|
|
68
|
+
const relevantAlt = findMatchingAlt(f1, f2)
|
|
69
|
+
if (!c1 || !c2) {
|
|
70
|
+
return null
|
|
71
|
+
}
|
|
72
|
+
const f1origref = f1.get('refName')
|
|
73
|
+
const f2origref = f2.get('refName')
|
|
74
|
+
const f1ref = assembly.getCanonicalRefName(f1origref)
|
|
75
|
+
const f2ref = assembly.getCanonicalRefName(f2origref)
|
|
76
|
+
if (!f1ref || !f2ref) {
|
|
77
|
+
throw new Error(`unable to find ref for ${f1ref || f2ref}`)
|
|
78
|
+
}
|
|
79
|
+
const x1 = getPxFromCoordinate(views[level1], f1ref, c1[LEFT])
|
|
80
|
+
const x2 = getPxFromCoordinate(views[level2], f2ref, c2[LEFT])
|
|
81
|
+
const reversed1 = views[level1].pxToBp(x1).reversed
|
|
82
|
+
const reversed2 = views[level2].pxToBp(x2).reversed
|
|
83
|
+
|
|
84
|
+
const tracks = views.map(v => v.getTrack(trackId))
|
|
85
|
+
const y1 =
|
|
86
|
+
yPos(trackId, level1, views, tracks, c1, getTrackYPosOverride) -
|
|
87
|
+
yoff
|
|
88
|
+
const y2 =
|
|
89
|
+
yPos(trackId, level2, views, tracks, c2, getTrackYPosOverride) -
|
|
90
|
+
yoff
|
|
91
|
+
if (!relevantAlt) {
|
|
92
|
+
console.warn('the relevant ALT allele was not found, cannot render')
|
|
93
|
+
} else {
|
|
94
|
+
const path = [
|
|
95
|
+
'M', // move to
|
|
96
|
+
x1 -
|
|
97
|
+
20 *
|
|
98
|
+
(relevantAlt.Join === 'left' ? -1 : 1) *
|
|
99
|
+
(reversed1 ? -1 : 1),
|
|
100
|
+
y1,
|
|
101
|
+
'L', // line to
|
|
102
|
+
x1,
|
|
103
|
+
y1,
|
|
104
|
+
'L', // line to
|
|
105
|
+
x2,
|
|
106
|
+
y2,
|
|
107
|
+
'L', // line to
|
|
108
|
+
x2 -
|
|
109
|
+
20 *
|
|
110
|
+
(relevantAlt.MateDirection === 'left' ? 1 : -1) *
|
|
111
|
+
(reversed2 ? -1 : 1),
|
|
112
|
+
y2,
|
|
113
|
+
].join(' ')
|
|
114
|
+
ret.push(
|
|
115
|
+
<path
|
|
116
|
+
d={path}
|
|
117
|
+
key={JSON.stringify(path)}
|
|
118
|
+
strokeWidth={id === mouseoverElt ? 10 : 5}
|
|
119
|
+
onClick={() => {
|
|
120
|
+
const featureWidget = session.addWidget?.(
|
|
121
|
+
'VariantFeatureWidget',
|
|
122
|
+
'variantFeature',
|
|
123
|
+
{
|
|
124
|
+
featureData: totalFeatures.get(id)?.toJSON(),
|
|
125
|
+
},
|
|
126
|
+
)
|
|
127
|
+
session.showWidget?.(featureWidget)
|
|
128
|
+
}}
|
|
129
|
+
onMouseOver={() => setMouseoverElt(id)}
|
|
130
|
+
onMouseOut={() => setMouseoverElt(undefined)}
|
|
131
|
+
/>,
|
|
132
|
+
)
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return ret
|
|
136
|
+
})}
|
|
137
|
+
</g>
|
|
138
|
+
)
|
|
139
|
+
})
|
|
140
|
+
|
|
141
|
+
export default Breakends
|