@jbrowse/plugin-breakpoint-split-view 2.6.1 → 2.6.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.
- package/dist/BreakpointAlignmentsFeatureDetail/BreakpointAlignmentsFeatureDetail.js +0 -1
- package/dist/BreakpointAlignmentsFeatureDetail/index.js +0 -1
- package/dist/BreakpointSplitView/BreakpointSplitView.js +0 -1
- package/dist/BreakpointSplitView/components/AlignmentConnections.js +0 -1
- package/dist/BreakpointSplitView/components/Breakends.js +0 -1
- package/dist/BreakpointSplitView/components/BreakpointSplitView.js +0 -1
- package/dist/BreakpointSplitView/components/ExportSvgDialog.js +0 -1
- package/dist/BreakpointSplitView/components/Overlay.js +0 -1
- package/dist/BreakpointSplitView/components/Translocations.js +0 -1
- package/dist/BreakpointSplitView/components/util.js +0 -1
- package/dist/BreakpointSplitView/index.js +0 -1
- package/dist/BreakpointSplitView/model.d.ts +1 -1
- package/dist/BreakpointSplitView/model.js +3 -3
- package/dist/BreakpointSplitView/svgcomponents/SVGBackground.js +0 -1
- package/dist/BreakpointSplitView/svgcomponents/SVGBreakpointSplitView.js +0 -1
- package/dist/BreakpointSplitView/util.js +0 -1
- package/dist/index.js +0 -1
- package/esm/BreakpointAlignmentsFeatureDetail/BreakpointAlignmentsFeatureDetail.js +0 -1
- package/esm/BreakpointAlignmentsFeatureDetail/index.js +0 -1
- package/esm/BreakpointSplitView/BreakpointSplitView.js +0 -1
- package/esm/BreakpointSplitView/components/AlignmentConnections.js +0 -1
- package/esm/BreakpointSplitView/components/Breakends.js +0 -1
- package/esm/BreakpointSplitView/components/BreakpointSplitView.js +0 -1
- package/esm/BreakpointSplitView/components/ExportSvgDialog.js +0 -1
- package/esm/BreakpointSplitView/components/Overlay.js +0 -1
- package/esm/BreakpointSplitView/components/Translocations.js +0 -1
- package/esm/BreakpointSplitView/components/util.js +0 -1
- package/esm/BreakpointSplitView/index.js +0 -1
- package/esm/BreakpointSplitView/model.d.ts +1 -1
- package/esm/BreakpointSplitView/model.js +3 -3
- package/esm/BreakpointSplitView/svgcomponents/SVGBackground.js +0 -1
- package/esm/BreakpointSplitView/svgcomponents/SVGBreakpointSplitView.js +0 -1
- package/esm/BreakpointSplitView/util.js +0 -1
- package/esm/index.js +0 -1
- package/package.json +3 -4
- package/dist/BreakpointAlignmentsFeatureDetail/BreakpointAlignmentsFeatureDetail.js.map +0 -1
- package/dist/BreakpointAlignmentsFeatureDetail/index.js.map +0 -1
- package/dist/BreakpointSplitView/BreakpointSplitView.js.map +0 -1
- package/dist/BreakpointSplitView/components/AlignmentConnections.js.map +0 -1
- package/dist/BreakpointSplitView/components/Breakends.js.map +0 -1
- package/dist/BreakpointSplitView/components/BreakpointSplitView.js.map +0 -1
- package/dist/BreakpointSplitView/components/ExportSvgDialog.js.map +0 -1
- package/dist/BreakpointSplitView/components/Overlay.js.map +0 -1
- package/dist/BreakpointSplitView/components/Translocations.js.map +0 -1
- package/dist/BreakpointSplitView/components/util.js.map +0 -1
- package/dist/BreakpointSplitView/index.js.map +0 -1
- package/dist/BreakpointSplitView/model.js.map +0 -1
- package/dist/BreakpointSplitView/svgcomponents/SVGBackground.js.map +0 -1
- package/dist/BreakpointSplitView/svgcomponents/SVGBreakpointSplitView.js.map +0 -1
- package/dist/BreakpointSplitView/util.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/esm/BreakpointAlignmentsFeatureDetail/BreakpointAlignmentsFeatureDetail.js.map +0 -1
- package/esm/BreakpointAlignmentsFeatureDetail/index.js.map +0 -1
- package/esm/BreakpointSplitView/BreakpointSplitView.js.map +0 -1
- package/esm/BreakpointSplitView/components/AlignmentConnections.js.map +0 -1
- package/esm/BreakpointSplitView/components/Breakends.js.map +0 -1
- package/esm/BreakpointSplitView/components/BreakpointSplitView.js.map +0 -1
- package/esm/BreakpointSplitView/components/ExportSvgDialog.js.map +0 -1
- package/esm/BreakpointSplitView/components/Overlay.js.map +0 -1
- package/esm/BreakpointSplitView/components/Translocations.js.map +0 -1
- package/esm/BreakpointSplitView/components/util.js.map +0 -1
- package/esm/BreakpointSplitView/index.js.map +0 -1
- package/esm/BreakpointSplitView/model.js.map +0 -1
- package/esm/BreakpointSplitView/svgcomponents/SVGBackground.js.map +0 -1
- package/esm/BreakpointSplitView/svgcomponents/SVGBreakpointSplitView.js.map +0 -1
- package/esm/BreakpointSplitView/util.js.map +0 -1
- package/esm/index.js.map +0 -1
- package/src/BreakpointAlignmentsFeatureDetail/BreakpointAlignmentsFeatureDetail.tsx +0 -24
- package/src/BreakpointAlignmentsFeatureDetail/index.ts +0 -35
- package/src/BreakpointSplitView/BreakpointSplitView.ts +0 -100
- package/src/BreakpointSplitView/components/AlignmentConnections.tsx +0 -166
- package/src/BreakpointSplitView/components/Breakends.tsx +0 -141
- package/src/BreakpointSplitView/components/BreakpointSplitView.tsx +0 -95
- package/src/BreakpointSplitView/components/ExportSvgDialog.tsx +0 -149
- package/src/BreakpointSplitView/components/Overlay.tsx +0 -29
- package/src/BreakpointSplitView/components/Translocations.tsx +0 -147
- package/src/BreakpointSplitView/components/util.ts +0 -127
- package/src/BreakpointSplitView/index.ts +0 -17
- package/src/BreakpointSplitView/model.ts +0 -333
- package/src/BreakpointSplitView/svgcomponents/SVGBackground.tsx +0 -21
- package/src/BreakpointSplitView/svgcomponents/SVGBreakpointSplitView.tsx +0 -179
- package/src/BreakpointSplitView/util.ts +0 -89
- package/src/index.test.ts +0 -3
- package/src/index.ts +0 -15
|
@@ -1,24 +0,0 @@
|
|
|
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
|
|
@@ -1,35 +0,0 @@
|
|
|
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
|
-
}
|
|
@@ -1,100 +0,0 @@
|
|
|
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
|
-
}
|
|
@@ -1,166 +0,0 @@
|
|
|
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
|
|
@@ -1,141 +0,0 @@
|
|
|
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
|
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
import React, { useRef } from 'react'
|
|
2
|
-
import { observer } from 'mobx-react'
|
|
3
|
-
import { getEnv } from 'mobx-state-tree'
|
|
4
|
-
import { makeStyles } from 'tss-react/mui'
|
|
5
|
-
|
|
6
|
-
// locals
|
|
7
|
-
import { BreakpointViewModel } from '../model'
|
|
8
|
-
import Overlay from './Overlay'
|
|
9
|
-
|
|
10
|
-
const useStyles = makeStyles()(theme => ({
|
|
11
|
-
viewDivider: {
|
|
12
|
-
background: theme.palette.secondary.main,
|
|
13
|
-
height: 3,
|
|
14
|
-
},
|
|
15
|
-
container: {
|
|
16
|
-
display: 'grid',
|
|
17
|
-
},
|
|
18
|
-
overlay: {
|
|
19
|
-
display: 'flex',
|
|
20
|
-
width: '100%',
|
|
21
|
-
gridArea: '1/1',
|
|
22
|
-
'& path': {
|
|
23
|
-
cursor: 'crosshair',
|
|
24
|
-
fill: 'none',
|
|
25
|
-
},
|
|
26
|
-
},
|
|
27
|
-
content: {
|
|
28
|
-
gridArea: '1/1',
|
|
29
|
-
},
|
|
30
|
-
}))
|
|
31
|
-
|
|
32
|
-
const BreakpointSplitView = observer(function ({
|
|
33
|
-
model,
|
|
34
|
-
}: {
|
|
35
|
-
model: BreakpointViewModel
|
|
36
|
-
}) {
|
|
37
|
-
const { classes } = useStyles()
|
|
38
|
-
const { views } = model
|
|
39
|
-
const { pluginManager } = getEnv(model)
|
|
40
|
-
const ref = useRef(null)
|
|
41
|
-
|
|
42
|
-
return (
|
|
43
|
-
<div>
|
|
44
|
-
<div className={classes.container}>
|
|
45
|
-
<div className={classes.content}>
|
|
46
|
-
<div style={{ position: 'relative' }}>
|
|
47
|
-
{views.map((view, idx) => {
|
|
48
|
-
const { ReactComponent } = pluginManager.getViewType(view.type)
|
|
49
|
-
const viewComponent = (
|
|
50
|
-
<ReactComponent key={view.id} model={view} />
|
|
51
|
-
)
|
|
52
|
-
if (idx === views.length - 1) {
|
|
53
|
-
return viewComponent
|
|
54
|
-
}
|
|
55
|
-
return [
|
|
56
|
-
viewComponent,
|
|
57
|
-
<div
|
|
58
|
-
key={`${view.id}-divider`}
|
|
59
|
-
className={classes.viewDivider}
|
|
60
|
-
/>,
|
|
61
|
-
]
|
|
62
|
-
})}
|
|
63
|
-
</div>
|
|
64
|
-
</div>
|
|
65
|
-
<div className={classes.overlay}>
|
|
66
|
-
<svg
|
|
67
|
-
ref={ref}
|
|
68
|
-
style={{
|
|
69
|
-
width: '100%',
|
|
70
|
-
zIndex: 10,
|
|
71
|
-
pointerEvents: model.interactToggled ? undefined : 'none',
|
|
72
|
-
}}
|
|
73
|
-
>
|
|
74
|
-
{model.matchedTracks.map(track => (
|
|
75
|
-
// note: we must pass ref down, because the child component
|
|
76
|
-
// needs to getBoundingClientRect on the this components SVG,
|
|
77
|
-
// and we cannot rely on using getBoundingClientRect in this
|
|
78
|
-
// component to make sure this works because if it gets
|
|
79
|
-
// shifted around by another element, this will not re-render
|
|
80
|
-
// necessarily
|
|
81
|
-
<Overlay
|
|
82
|
-
parentRef={ref}
|
|
83
|
-
key={track.configuration.trackId}
|
|
84
|
-
model={model}
|
|
85
|
-
trackId={track.configuration.trackId}
|
|
86
|
-
/>
|
|
87
|
-
))}
|
|
88
|
-
</svg>
|
|
89
|
-
</div>
|
|
90
|
-
</div>
|
|
91
|
-
</div>
|
|
92
|
-
)
|
|
93
|
-
})
|
|
94
|
-
|
|
95
|
-
export default BreakpointSplitView
|