jbrowse-plugin-mafviewer 1.4.5 → 1.4.6
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/BigMafAdapter/BigMafAdapter.js +4 -5
- package/dist/BigMafAdapter/BigMafAdapter.js.map +1 -1
- package/dist/BigMafAdapter/configSchema.d.ts +2 -2
- package/dist/LinearMafDisplay/components/LinearMafDisplayComponent.js +38 -108
- package/dist/LinearMafDisplay/components/LinearMafDisplayComponent.js.map +1 -1
- package/dist/LinearMafDisplay/components/MAFTooltip.d.ts +0 -3
- package/dist/LinearMafDisplay/components/MAFTooltip.js.map +1 -1
- package/dist/LinearMafDisplay/components/MsaHighlightOverlay.d.ts +9 -0
- package/dist/LinearMafDisplay/components/MsaHighlightOverlay.js +34 -0
- package/dist/LinearMafDisplay/components/MsaHighlightOverlay.js.map +1 -0
- package/dist/LinearMafDisplay/components/Sidebar/SvgWrapper.js +1 -1
- package/dist/LinearMafDisplay/components/Sidebar/SvgWrapper.js.map +1 -1
- package/dist/LinearMafDisplay/components/useDragSelection.d.ts +25 -0
- package/dist/LinearMafDisplay/components/useDragSelection.js +103 -0
- package/dist/LinearMafDisplay/components/useDragSelection.js.map +1 -0
- package/dist/LinearMafDisplay/configSchema.d.ts +3 -30
- package/dist/LinearMafDisplay/stateModel.d.ts +1043 -121
- package/dist/LinearMafDisplay/stateModel.js +85 -41
- package/dist/LinearMafDisplay/stateModel.js.map +1 -1
- package/dist/LinearMafDisplay/types.d.ts +2 -2
- package/dist/LinearMafDisplay/util.d.ts +5 -0
- package/dist/LinearMafDisplay/util.js +25 -4
- package/dist/LinearMafDisplay/util.js.map +1 -1
- package/dist/LinearMafRenderer/LinearMafRenderer.d.ts +41 -5
- package/dist/LinearMafRenderer/LinearMafRenderer.js +1 -1
- package/dist/LinearMafRenderer/LinearMafRenderer.js.map +1 -1
- package/dist/LinearMafRenderer/components/LinearMafRendering.d.ts +14 -5
- package/dist/LinearMafRenderer/components/LinearMafRendering.js +21 -19
- package/dist/LinearMafRenderer/components/LinearMafRendering.js.map +1 -1
- package/dist/LinearMafRenderer/configSchema.d.ts +1 -6
- package/dist/LinearMafRenderer/configSchema.js +1 -6
- package/dist/LinearMafRenderer/configSchema.js.map +1 -1
- package/dist/LinearMafRenderer/rendering/insertions.d.ts +1 -1
- package/dist/LinearMafRenderer/rendering/insertions.js +2 -2
- package/dist/LinearMafRenderer/rendering/mismatches.d.ts +1 -1
- package/dist/LinearMafRenderer/rendering/mismatches.js +3 -3
- package/dist/LinearMafRenderer/rendering/types.d.ts +1 -1
- package/dist/MafAddTrackWorkflow/AddTrackWorkflow.js +1 -1
- package/dist/MafAddTrackWorkflow/AddTrackWorkflow.js.map +1 -1
- package/dist/MafAddTrackWorkflow/index.js +1 -1
- package/dist/MafAddTrackWorkflow/index.js.map +1 -1
- package/dist/MafGetSequences/MafGetSequences.d.ts +1 -0
- package/dist/MafGetSequences/MafGetSequences.js +2 -1
- package/dist/MafGetSequences/MafGetSequences.js.map +1 -1
- package/dist/MafSequenceWidget/LabelsCanvas.d.ts +8 -0
- package/dist/MafSequenceWidget/LabelsCanvas.js +37 -0
- package/dist/MafSequenceWidget/LabelsCanvas.js.map +1 -0
- package/dist/MafSequenceWidget/MafSequenceHoverHighlight.d.ts +6 -0
- package/dist/MafSequenceWidget/MafSequenceHoverHighlight.js +52 -0
- package/dist/MafSequenceWidget/MafSequenceHoverHighlight.js.map +1 -0
- package/dist/MafSequenceWidget/MafSequenceHoverHighlightExtension.d.ts +2 -0
- package/dist/MafSequenceWidget/MafSequenceHoverHighlightExtension.js +12 -0
- package/dist/MafSequenceWidget/MafSequenceHoverHighlightExtension.js.map +1 -0
- package/dist/MafSequenceWidget/MafSequenceWidget.d.ts +6 -0
- package/dist/MafSequenceWidget/MafSequenceWidget.js +189 -0
- package/dist/MafSequenceWidget/MafSequenceWidget.js.map +1 -0
- package/dist/MafSequenceWidget/SequenceCanvas.d.ts +12 -0
- package/dist/MafSequenceWidget/SequenceCanvas.js +86 -0
- package/dist/MafSequenceWidget/SequenceCanvas.js.map +1 -0
- package/dist/MafSequenceWidget/SequenceDisplay.d.ts +12 -0
- package/dist/MafSequenceWidget/SequenceDisplay.js +117 -0
- package/dist/MafSequenceWidget/SequenceDisplay.js.map +1 -0
- package/dist/MafSequenceWidget/SequenceTooltip.d.ts +11 -0
- package/dist/MafSequenceWidget/SequenceTooltip.js +39 -0
- package/dist/MafSequenceWidget/SequenceTooltip.js.map +1 -0
- package/dist/MafSequenceWidget/baseColors.d.ts +3 -0
- package/dist/MafSequenceWidget/baseColors.js +64 -0
- package/dist/MafSequenceWidget/baseColors.js.map +1 -0
- package/dist/MafSequenceWidget/colToGenomePos.d.ts +13 -0
- package/dist/MafSequenceWidget/colToGenomePos.js +32 -0
- package/dist/MafSequenceWidget/colToGenomePos.js.map +1 -0
- package/dist/MafSequenceWidget/colToGenomePos.test.d.ts +1 -0
- package/dist/MafSequenceWidget/colToGenomePos.test.js +136 -0
- package/dist/MafSequenceWidget/colToGenomePos.test.js.map +1 -0
- package/dist/MafSequenceWidget/configSchema.d.ts +1 -0
- package/dist/MafSequenceWidget/configSchema.js +3 -0
- package/dist/MafSequenceWidget/configSchema.js.map +1 -0
- package/dist/MafSequenceWidget/constants.d.ts +4 -0
- package/dist/MafSequenceWidget/constants.js +5 -0
- package/dist/MafSequenceWidget/constants.js.map +1 -0
- package/dist/MafSequenceWidget/index.d.ts +2 -0
- package/dist/MafSequenceWidget/index.js +16 -0
- package/dist/MafSequenceWidget/index.js.map +1 -0
- package/dist/MafSequenceWidget/stateModelFactory.d.ts +67 -0
- package/dist/MafSequenceWidget/stateModelFactory.js +21 -0
- package/dist/MafSequenceWidget/stateModelFactory.js.map +1 -0
- package/dist/MafTabixAdapter/MafTabixAdapter.js +4 -35
- package/dist/MafTabixAdapter/MafTabixAdapter.js.map +1 -1
- package/dist/MafTabixAdapter/configSchema.d.ts +4 -4
- package/dist/MafTrack/configSchema.d.ts +16 -11
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/jbrowse-plugin-mafviewer.umd.production.min.js +12 -24
- package/dist/jbrowse-plugin-mafviewer.umd.production.min.js.map +4 -4
- package/dist/util/clipboard.d.ts +2 -0
- package/dist/util/clipboard.js +28 -0
- package/dist/util/clipboard.js.map +1 -0
- package/dist/util/fastaUtils.d.ts +2 -1
- package/dist/util/fastaUtils.js +72 -2
- package/dist/util/fastaUtils.js.map +1 -1
- package/dist/util/fastaUtils.test.js +190 -0
- package/dist/util/fastaUtils.test.js.map +1 -1
- package/dist/util/parseAssemblyName.d.ts +32 -0
- package/dist/util/parseAssemblyName.js +87 -0
- package/dist/util/parseAssemblyName.js.map +1 -0
- package/dist/util/parseAssemblyName.test.d.ts +1 -0
- package/dist/util/parseAssemblyName.test.js +269 -0
- package/dist/util/parseAssemblyName.test.js.map +1 -0
- package/package.json +7 -7
- package/src/BigMafAdapter/BigMafAdapter.ts +5 -5
- package/src/LinearMafDisplay/components/LinearMafDisplayComponent.tsx +62 -144
- package/src/LinearMafDisplay/components/MAFTooltip.tsx +0 -3
- package/src/LinearMafDisplay/components/MsaHighlightOverlay.tsx +62 -0
- package/src/LinearMafDisplay/components/Sidebar/SvgWrapper.tsx +1 -1
- package/src/LinearMafDisplay/components/useDragSelection.ts +159 -0
- package/src/LinearMafDisplay/stateModel.ts +135 -48
- package/src/LinearMafDisplay/types.ts +2 -2
- package/src/LinearMafDisplay/util.ts +31 -5
- package/src/LinearMafRenderer/LinearMafRenderer.ts +1 -1
- package/src/LinearMafRenderer/components/LinearMafRendering.tsx +38 -24
- package/src/LinearMafRenderer/configSchema.ts +1 -6
- package/src/LinearMafRenderer/rendering/insertions.ts +2 -2
- package/src/LinearMafRenderer/rendering/mismatches.ts +3 -3
- package/src/LinearMafRenderer/rendering/types.ts +1 -1
- package/src/MafAddTrackWorkflow/AddTrackWorkflow.tsx +1 -1
- package/src/MafAddTrackWorkflow/index.ts +1 -1
- package/src/MafGetSequences/MafGetSequences.ts +10 -2
- package/src/MafSequenceWidget/LabelsCanvas.tsx +58 -0
- package/src/MafSequenceWidget/MafSequenceHoverHighlight.tsx +83 -0
- package/src/MafSequenceWidget/MafSequenceHoverHighlightExtension.tsx +24 -0
- package/src/MafSequenceWidget/MafSequenceWidget.tsx +294 -0
- package/src/MafSequenceWidget/SequenceCanvas.tsx +136 -0
- package/src/MafSequenceWidget/SequenceDisplay.tsx +188 -0
- package/src/MafSequenceWidget/SequenceTooltip.tsx +70 -0
- package/src/MafSequenceWidget/baseColors.ts +76 -0
- package/src/MafSequenceWidget/colToGenomePos.test.ts +166 -0
- package/src/MafSequenceWidget/colToGenomePos.ts +40 -0
- package/src/MafSequenceWidget/configSchema.ts +3 -0
- package/src/MafSequenceWidget/constants.ts +4 -0
- package/src/MafSequenceWidget/index.ts +24 -0
- package/src/MafSequenceWidget/stateModelFactory.ts +43 -0
- package/src/MafTabixAdapter/MafTabixAdapter.ts +12 -51
- package/src/index.ts +2 -0
- package/src/util/__snapshots__/fastaUtils.test.ts.snap +35 -0
- package/src/util/clipboard.ts +35 -0
- package/src/util/fastaUtils.test.ts +199 -0
- package/src/util/fastaUtils.ts +94 -1
- package/src/util/parseAssemblyName.test.ts +350 -0
- package/src/util/parseAssemblyName.ts +106 -0
- package/dist/LinearMafDisplay/components/GetSequenceDialog/GetSequenceDialog.d.ts +0 -11
- package/dist/LinearMafDisplay/components/GetSequenceDialog/GetSequenceDialog.js +0 -97
- package/dist/LinearMafDisplay/components/GetSequenceDialog/GetSequenceDialog.js.map +0 -1
- package/dist/LinearMafDisplay/components/InsertionSequenceDialog/InsertionSequenceDialog.d.ts +0 -14
- package/dist/LinearMafDisplay/components/InsertionSequenceDialog/InsertionSequenceDialog.js +0 -69
- package/dist/LinearMafDisplay/components/InsertionSequenceDialog/InsertionSequenceDialog.js.map +0 -1
- package/dist/LinearMafDisplay/components/util.d.ts +0 -1
- package/dist/LinearMafDisplay/components/util.js +0 -8
- package/dist/LinearMafDisplay/components/util.js.map +0 -1
- package/dist/util/fetchSequences.d.ts +0 -18
- package/dist/util/fetchSequences.js +0 -39
- package/dist/util/fetchSequences.js.map +0 -1
- package/dist/util/useSequences.d.ts +0 -21
- package/dist/util/useSequences.js +0 -64
- package/dist/util/useSequences.js.map +0 -1
- package/src/LinearMafDisplay/components/GetSequenceDialog/GetSequenceDialog.tsx +0 -175
- package/src/LinearMafDisplay/components/InsertionSequenceDialog/InsertionSequenceDialog.tsx +0 -105
- package/src/LinearMafDisplay/components/util.ts +0 -7
- package/src/util/fetchSequences.ts +0 -57
- package/src/util/useSequences.ts +0 -90
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
|
|
3
|
+
import { observer } from 'mobx-react'
|
|
4
|
+
|
|
5
|
+
import type { LinearMafDisplayModel } from '../stateModel'
|
|
6
|
+
import type { LinearGenomeViewModel } from '@jbrowse/plugin-linear-genome-view'
|
|
7
|
+
|
|
8
|
+
const MsaHighlightOverlay = observer(function MsaHighlightOverlay({
|
|
9
|
+
model,
|
|
10
|
+
view,
|
|
11
|
+
height,
|
|
12
|
+
}: {
|
|
13
|
+
model: LinearMafDisplayModel
|
|
14
|
+
view: LinearGenomeViewModel
|
|
15
|
+
height: number
|
|
16
|
+
}) {
|
|
17
|
+
const { msaHighlights } = model
|
|
18
|
+
if (msaHighlights.length === 0) {
|
|
19
|
+
return null
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const { offsetPx } = view
|
|
23
|
+
const displayedRegion = view.displayedRegions[0]
|
|
24
|
+
if (!displayedRegion) {
|
|
25
|
+
return null
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return (
|
|
29
|
+
<>
|
|
30
|
+
{msaHighlights.map((highlight, idx) => {
|
|
31
|
+
// Check if highlight is on the displayed refName
|
|
32
|
+
if (highlight.refName !== displayedRegion.refName) {
|
|
33
|
+
return null
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const startPx =
|
|
37
|
+
(highlight.start - displayedRegion.start) / view.bpPerPx - offsetPx
|
|
38
|
+
const endPx =
|
|
39
|
+
(highlight.end - displayedRegion.start) / view.bpPerPx - offsetPx
|
|
40
|
+
const widthPx = Math.max(endPx - startPx, 2)
|
|
41
|
+
|
|
42
|
+
return (
|
|
43
|
+
<div
|
|
44
|
+
key={idx}
|
|
45
|
+
style={{
|
|
46
|
+
position: 'absolute',
|
|
47
|
+
left: startPx,
|
|
48
|
+
top: 0,
|
|
49
|
+
width: widthPx,
|
|
50
|
+
height,
|
|
51
|
+
backgroundColor: 'rgba(255, 165, 0, 0.4)',
|
|
52
|
+
border: '1px solid rgba(255, 165, 0, 0.8)',
|
|
53
|
+
pointerEvents: 'none',
|
|
54
|
+
}}
|
|
55
|
+
/>
|
|
56
|
+
)
|
|
57
|
+
})}
|
|
58
|
+
</>
|
|
59
|
+
)
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
export default MsaHighlightOverlay
|
|
@@ -2,9 +2,9 @@ import React, { useEffect, useRef } from 'react'
|
|
|
2
2
|
|
|
3
3
|
import { ResizeHandle } from '@jbrowse/core/ui'
|
|
4
4
|
import { getContainingView } from '@jbrowse/core/util'
|
|
5
|
+
import { isAlive } from '@jbrowse/mobx-state-tree'
|
|
5
6
|
import { autorun } from 'mobx'
|
|
6
7
|
import { observer } from 'mobx-react'
|
|
7
|
-
import { isAlive } from 'mobx-state-tree'
|
|
8
8
|
import { makeStyles } from 'tss-react/mui'
|
|
9
9
|
|
|
10
10
|
import type { LinearMafDisplayModel } from '../../stateModel'
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import { useCallback, useEffect, useState } from 'react'
|
|
2
|
+
|
|
3
|
+
const MIN_DRAG_DISTANCE = 3
|
|
4
|
+
|
|
5
|
+
interface DragSelectionState {
|
|
6
|
+
isDragging: boolean
|
|
7
|
+
dragStartX: number | undefined
|
|
8
|
+
dragEndX: number | undefined
|
|
9
|
+
showSelectionBox: boolean
|
|
10
|
+
mouseX: number | undefined
|
|
11
|
+
mouseY: number | undefined
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
interface DragSelectionHandlers {
|
|
15
|
+
handleMouseDown: (event: React.MouseEvent) => void
|
|
16
|
+
handleMouseMove: (event: React.MouseEvent) => void
|
|
17
|
+
handleMouseUp: (event: React.MouseEvent) => void
|
|
18
|
+
handleMouseLeave: () => void
|
|
19
|
+
clearSelectionBox: () => void
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
interface ContextCoord {
|
|
23
|
+
coord: [number, number]
|
|
24
|
+
dragStartX: number
|
|
25
|
+
dragEndX: number
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function useDragSelection(
|
|
29
|
+
ref: React.RefObject<HTMLDivElement | null>,
|
|
30
|
+
): DragSelectionState &
|
|
31
|
+
DragSelectionHandlers & {
|
|
32
|
+
contextCoord: ContextCoord | undefined
|
|
33
|
+
setContextCoord: (coord: ContextCoord | undefined) => void
|
|
34
|
+
} {
|
|
35
|
+
const [isDragging, setIsDragging] = useState(false)
|
|
36
|
+
const [dragStartX, setDragStartX] = useState<number>()
|
|
37
|
+
const [dragEndX, setDragEndX] = useState<number>()
|
|
38
|
+
const [showSelectionBox, setShowSelectionBox] = useState(false)
|
|
39
|
+
const [mouseX, setMouseX] = useState<number>()
|
|
40
|
+
const [mouseY, setMouseY] = useState<number>()
|
|
41
|
+
const [contextCoord, setContextCoord] = useState<ContextCoord>()
|
|
42
|
+
|
|
43
|
+
const clearSelectionBox = useCallback(() => {
|
|
44
|
+
setShowSelectionBox(false)
|
|
45
|
+
setDragStartX(undefined)
|
|
46
|
+
setDragEndX(undefined)
|
|
47
|
+
}, [])
|
|
48
|
+
|
|
49
|
+
const handleMouseDown = useCallback(
|
|
50
|
+
(event: React.MouseEvent) => {
|
|
51
|
+
if (event.shiftKey) {
|
|
52
|
+
return
|
|
53
|
+
}
|
|
54
|
+
const rect = ref.current?.getBoundingClientRect()
|
|
55
|
+
const left = rect?.left || 0
|
|
56
|
+
const clientX = event.clientX - left
|
|
57
|
+
|
|
58
|
+
setShowSelectionBox(false)
|
|
59
|
+
setIsDragging(true)
|
|
60
|
+
setDragStartX(clientX)
|
|
61
|
+
setDragEndX(clientX)
|
|
62
|
+
event.stopPropagation()
|
|
63
|
+
},
|
|
64
|
+
[ref],
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
const handleMouseMove = useCallback(
|
|
68
|
+
(event: React.MouseEvent) => {
|
|
69
|
+
const rect = ref.current?.getBoundingClientRect()
|
|
70
|
+
const top = rect?.top || 0
|
|
71
|
+
const left = rect?.left || 0
|
|
72
|
+
const clientX = event.clientX - left
|
|
73
|
+
const clientY = event.clientY - top
|
|
74
|
+
|
|
75
|
+
setMouseY(clientY)
|
|
76
|
+
setMouseX(clientX)
|
|
77
|
+
|
|
78
|
+
if (isDragging) {
|
|
79
|
+
setDragEndX(clientX)
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
[ref, isDragging],
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
const handleMouseUp = useCallback(
|
|
86
|
+
(event: React.MouseEvent) => {
|
|
87
|
+
if (isDragging && dragStartX !== undefined && dragEndX !== undefined) {
|
|
88
|
+
const dragDistanceX = Math.abs(dragEndX - dragStartX)
|
|
89
|
+
|
|
90
|
+
if (dragDistanceX > MIN_DRAG_DISTANCE) {
|
|
91
|
+
setContextCoord({
|
|
92
|
+
coord: [event.clientX, event.clientY],
|
|
93
|
+
dragEndX: event.clientX,
|
|
94
|
+
dragStartX: dragStartX,
|
|
95
|
+
})
|
|
96
|
+
setShowSelectionBox(true)
|
|
97
|
+
} else {
|
|
98
|
+
clearSelectionBox()
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
setIsDragging(false)
|
|
102
|
+
},
|
|
103
|
+
[isDragging, dragStartX, dragEndX, clearSelectionBox],
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
const handleMouseLeave = useCallback(() => {
|
|
107
|
+
setMouseY(undefined)
|
|
108
|
+
setMouseX(undefined)
|
|
109
|
+
setIsDragging(false)
|
|
110
|
+
}, [])
|
|
111
|
+
|
|
112
|
+
useEffect(() => {
|
|
113
|
+
const handleKeyDown = (event: KeyboardEvent) => {
|
|
114
|
+
if (event.key === 'Escape' && showSelectionBox) {
|
|
115
|
+
clearSelectionBox()
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const handleClickOutside = (event: MouseEvent) => {
|
|
120
|
+
if (
|
|
121
|
+
ref.current &&
|
|
122
|
+
!ref.current.contains(event.target as Node) &&
|
|
123
|
+
showSelectionBox
|
|
124
|
+
) {
|
|
125
|
+
clearSelectionBox()
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
document.addEventListener('keydown', handleKeyDown)
|
|
130
|
+
document.addEventListener('click', handleClickOutside)
|
|
131
|
+
|
|
132
|
+
return () => {
|
|
133
|
+
document.removeEventListener('keydown', handleKeyDown)
|
|
134
|
+
document.removeEventListener('click', handleClickOutside)
|
|
135
|
+
}
|
|
136
|
+
}, [ref, showSelectionBox, clearSelectionBox])
|
|
137
|
+
|
|
138
|
+
const dragDistance =
|
|
139
|
+
dragStartX !== undefined && dragEndX !== undefined
|
|
140
|
+
? Math.abs(dragEndX - dragStartX)
|
|
141
|
+
: 0
|
|
142
|
+
const hasDraggedEnough = dragDistance > MIN_DRAG_DISTANCE
|
|
143
|
+
|
|
144
|
+
return {
|
|
145
|
+
isDragging: isDragging && hasDraggedEnough,
|
|
146
|
+
dragStartX,
|
|
147
|
+
dragEndX,
|
|
148
|
+
showSelectionBox,
|
|
149
|
+
mouseX,
|
|
150
|
+
mouseY,
|
|
151
|
+
contextCoord,
|
|
152
|
+
setContextCoord,
|
|
153
|
+
handleMouseDown,
|
|
154
|
+
handleMouseMove,
|
|
155
|
+
handleMouseUp,
|
|
156
|
+
handleMouseLeave,
|
|
157
|
+
clearSelectionBox,
|
|
158
|
+
}
|
|
159
|
+
}
|
|
@@ -1,15 +1,23 @@
|
|
|
1
1
|
import { lazy } from 'react'
|
|
2
2
|
|
|
3
3
|
import { ConfigurationReference, getConf } from '@jbrowse/core/configuration'
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
SessionWithWidgets,
|
|
6
|
+
getContainingTrack,
|
|
7
|
+
getContainingView,
|
|
8
|
+
getEnv,
|
|
9
|
+
getSession,
|
|
10
|
+
max,
|
|
11
|
+
measureText,
|
|
12
|
+
} from '@jbrowse/core/util'
|
|
5
13
|
import { getRpcSessionId } from '@jbrowse/core/util/tracks'
|
|
14
|
+
import { addDisposer, isAlive, types } from '@jbrowse/mobx-state-tree'
|
|
6
15
|
import { ascending } from 'd3-array'
|
|
7
16
|
import { cluster, hierarchy } from 'd3-hierarchy'
|
|
8
17
|
import deepEqual from 'fast-deep-equal'
|
|
9
18
|
import { autorun } from 'mobx'
|
|
10
|
-
import { addDisposer, isAlive, types } from 'mobx-state-tree'
|
|
11
19
|
|
|
12
|
-
import { maxLength, setBrLength } from './util'
|
|
20
|
+
import { computeNodeDescendantNames, maxLength, setBrLength } from './util'
|
|
13
21
|
import { normalize } from '../util'
|
|
14
22
|
|
|
15
23
|
import type { NodeWithIds, NodeWithIdsAndLength, Sample } from './types'
|
|
@@ -18,18 +26,23 @@ import type {
|
|
|
18
26
|
AnyConfigurationModel,
|
|
19
27
|
AnyConfigurationSchemaType,
|
|
20
28
|
} from '@jbrowse/core/configuration'
|
|
29
|
+
import type { Instance } from '@jbrowse/mobx-state-tree'
|
|
21
30
|
import type { ExportSvgDisplayOptions } from '@jbrowse/plugin-linear-genome-view'
|
|
22
31
|
import type { HierarchyNode } from 'd3-hierarchy'
|
|
23
|
-
|
|
32
|
+
|
|
33
|
+
const defaultRowHeight = 15
|
|
34
|
+
const defaultRowProportion = 0.8
|
|
35
|
+
const defaultShowAllLetters = false
|
|
36
|
+
const defaultMismatchRendering = true
|
|
37
|
+
const defaultShowBranchLen = false
|
|
38
|
+
const defaultTreeAreaWidth = 80
|
|
39
|
+
const defaultShowAsUpperCase = true
|
|
40
|
+
const defaultShowSidebar = true
|
|
24
41
|
|
|
25
42
|
const SetRowHeightDialog = lazy(
|
|
26
43
|
() => import('./components/SetRowHeightDialog/SetRowHeightDialog'),
|
|
27
44
|
)
|
|
28
45
|
|
|
29
|
-
const InsertionSequenceDialog = lazy(
|
|
30
|
-
() => import('./components/InsertionSequenceDialog/InsertionSequenceDialog'),
|
|
31
|
-
)
|
|
32
|
-
|
|
33
46
|
/**
|
|
34
47
|
* #stateModel LinearMafDisplay
|
|
35
48
|
* extends LinearBasicDisplay
|
|
@@ -59,37 +72,37 @@ export default function stateModelFactory(
|
|
|
59
72
|
/**
|
|
60
73
|
* #property
|
|
61
74
|
*/
|
|
62
|
-
rowHeight:
|
|
75
|
+
rowHeight: defaultRowHeight,
|
|
63
76
|
/**
|
|
64
77
|
* #property
|
|
65
78
|
*/
|
|
66
|
-
rowProportion:
|
|
79
|
+
rowProportion: defaultRowProportion,
|
|
67
80
|
/**
|
|
68
81
|
* #property
|
|
69
82
|
*/
|
|
70
|
-
showAllLetters:
|
|
83
|
+
showAllLetters: defaultShowAllLetters,
|
|
71
84
|
/**
|
|
72
85
|
* #property
|
|
73
86
|
*/
|
|
74
|
-
mismatchRendering:
|
|
87
|
+
mismatchRendering: defaultMismatchRendering,
|
|
75
88
|
|
|
76
89
|
/**
|
|
77
90
|
* #property
|
|
78
91
|
*/
|
|
79
|
-
showBranchLen:
|
|
92
|
+
showBranchLen: defaultShowBranchLen,
|
|
80
93
|
|
|
81
94
|
/**
|
|
82
95
|
* #property
|
|
83
96
|
*/
|
|
84
|
-
treeAreaWidth:
|
|
97
|
+
treeAreaWidth: defaultTreeAreaWidth,
|
|
85
98
|
/**
|
|
86
99
|
* #property
|
|
87
100
|
*/
|
|
88
|
-
showAsUpperCase:
|
|
101
|
+
showAsUpperCase: defaultShowAsUpperCase,
|
|
89
102
|
/**
|
|
90
103
|
* #property
|
|
91
104
|
*/
|
|
92
|
-
showSidebar:
|
|
105
|
+
showSidebar: defaultShowSidebar,
|
|
93
106
|
}),
|
|
94
107
|
)
|
|
95
108
|
.volatile(() => ({
|
|
@@ -108,7 +121,7 @@ export default function stateModelFactory(
|
|
|
108
121
|
/**
|
|
109
122
|
* #volatile
|
|
110
123
|
*/
|
|
111
|
-
volatileTree: undefined as
|
|
124
|
+
volatileTree: undefined as NodeWithIds | undefined,
|
|
112
125
|
/**
|
|
113
126
|
* #volatile
|
|
114
127
|
*/
|
|
@@ -152,7 +165,13 @@ export default function stateModelFactory(
|
|
|
152
165
|
/**
|
|
153
166
|
* #action
|
|
154
167
|
*/
|
|
155
|
-
setSamples({
|
|
168
|
+
setSamples({
|
|
169
|
+
samples,
|
|
170
|
+
tree,
|
|
171
|
+
}: {
|
|
172
|
+
samples: Sample[]
|
|
173
|
+
tree: NodeWithIds | undefined
|
|
174
|
+
}) {
|
|
156
175
|
if (!deepEqual(samples, self.volatileSamples)) {
|
|
157
176
|
self.volatileSamples = samples
|
|
158
177
|
}
|
|
@@ -197,14 +216,30 @@ export default function stateModelFactory(
|
|
|
197
216
|
chr: string
|
|
198
217
|
pos: number
|
|
199
218
|
}) {
|
|
200
|
-
|
|
201
|
-
|
|
219
|
+
const { sequence, sampleLabel, chr, pos } = insertionData
|
|
220
|
+
const session = getSession(self) as SessionWithWidgets
|
|
221
|
+
const featureWidget = session.addWidget(
|
|
222
|
+
'BaseFeatureWidget',
|
|
223
|
+
'baseFeature',
|
|
202
224
|
{
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
225
|
+
featureData: {
|
|
226
|
+
uniqueId: `insertion-${chr}-${pos}-${sampleLabel}`,
|
|
227
|
+
type: 'insertion',
|
|
228
|
+
refName: chr,
|
|
229
|
+
start: pos,
|
|
230
|
+
end: pos + 1,
|
|
231
|
+
sample: sampleLabel,
|
|
232
|
+
insertionLength: sequence.length,
|
|
233
|
+
sequence: self.showAsUpperCase
|
|
234
|
+
? sequence.toUpperCase()
|
|
235
|
+
: sequence.toLowerCase(),
|
|
236
|
+
},
|
|
237
|
+
view: getContainingView(self),
|
|
238
|
+
track: getContainingTrack(self),
|
|
206
239
|
},
|
|
207
|
-
|
|
240
|
+
)
|
|
241
|
+
|
|
242
|
+
session.showWidget(featureWidget)
|
|
208
243
|
},
|
|
209
244
|
}))
|
|
210
245
|
.views(self => ({
|
|
@@ -239,8 +274,7 @@ export default function stateModelFactory(
|
|
|
239
274
|
get root() {
|
|
240
275
|
return self.volatileTree
|
|
241
276
|
? hierarchy(self.volatileTree, d => d.children)
|
|
242
|
-
|
|
243
|
-
.sum(d => (d.children ? 0 : 1))
|
|
277
|
+
.sum(d => (d.children?.length ? 0 : 1))
|
|
244
278
|
.sort((a, b) => ascending(a.data.length || 1, b.data.length || 1))
|
|
245
279
|
: undefined
|
|
246
280
|
},
|
|
@@ -264,7 +298,8 @@ export default function stateModelFactory(
|
|
|
264
298
|
for (const node of r.descendants()) {
|
|
265
299
|
node.x = node.x! + self.rowHeight / 2
|
|
266
300
|
}
|
|
267
|
-
|
|
301
|
+
r.data.length = 0
|
|
302
|
+
setBrLength(r, 0, width / maxLength(r))
|
|
268
303
|
return r as HierarchyNode<NodeWithIdsAndLength>
|
|
269
304
|
} else {
|
|
270
305
|
return undefined
|
|
@@ -311,26 +346,10 @@ export default function stateModelFactory(
|
|
|
311
346
|
* Precomputed map from hierarchy node to its descendant leaf names
|
|
312
347
|
*/
|
|
313
348
|
get nodeDescendantNames() {
|
|
314
|
-
const map = new Map<unknown, string[]>()
|
|
315
|
-
function computeDescendants(
|
|
316
|
-
node: HierarchyNode<NodeWithIdsAndLength>,
|
|
317
|
-
): string[] {
|
|
318
|
-
if (!node.children || node.children.length === 0) {
|
|
319
|
-
const names = [node.data.name]
|
|
320
|
-
map.set(node, names)
|
|
321
|
-
return names
|
|
322
|
-
}
|
|
323
|
-
const names: string[] = []
|
|
324
|
-
for (const child of node.children) {
|
|
325
|
-
names.push(...computeDescendants(child))
|
|
326
|
-
}
|
|
327
|
-
map.set(node, names)
|
|
328
|
-
return names
|
|
329
|
-
}
|
|
330
349
|
if (this.hierarchy) {
|
|
331
|
-
|
|
350
|
+
return computeNodeDescendantNames(this.hierarchy)
|
|
332
351
|
}
|
|
333
|
-
return
|
|
352
|
+
return new Map<HierarchyNode<NodeWithIdsAndLength>, string[]>()
|
|
334
353
|
},
|
|
335
354
|
/**
|
|
336
355
|
* #getter
|
|
@@ -370,7 +389,7 @@ export default function stateModelFactory(
|
|
|
370
389
|
return {
|
|
371
390
|
...s,
|
|
372
391
|
notReady:
|
|
373
|
-
(!self.volatileSamples && !self.volatileTree) ||
|
|
392
|
+
(!self.volatileSamples && !self.volatileTree) || s.notReady,
|
|
374
393
|
config: rendererConfig,
|
|
375
394
|
samples,
|
|
376
395
|
rowHeight,
|
|
@@ -455,6 +474,37 @@ export default function stateModelFactory(
|
|
|
455
474
|
}
|
|
456
475
|
})
|
|
457
476
|
.views(self => ({
|
|
477
|
+
/**
|
|
478
|
+
* #getter
|
|
479
|
+
* Get highlight regions from connected MSA views
|
|
480
|
+
*/
|
|
481
|
+
get msaHighlights() {
|
|
482
|
+
const session = getSession(self)
|
|
483
|
+
const view = getContainingView(self)
|
|
484
|
+
const highlights: { refName: string; start: number; end: number }[] = []
|
|
485
|
+
|
|
486
|
+
// Find MSA views that are connected to our parent view
|
|
487
|
+
for (const v of session.views) {
|
|
488
|
+
if (
|
|
489
|
+
(v as { type?: string }).type === 'MsaView' &&
|
|
490
|
+
(v as { connectedViewId?: string }).connectedViewId === view.id
|
|
491
|
+
) {
|
|
492
|
+
const msaView = v as {
|
|
493
|
+
connectedHighlights?: {
|
|
494
|
+
refName: string
|
|
495
|
+
start: number
|
|
496
|
+
end: number
|
|
497
|
+
}[]
|
|
498
|
+
}
|
|
499
|
+
if (msaView.connectedHighlights) {
|
|
500
|
+
for (const h of msaView.connectedHighlights) {
|
|
501
|
+
highlights.push(h)
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
return highlights
|
|
507
|
+
},
|
|
458
508
|
/**
|
|
459
509
|
* #getter
|
|
460
510
|
*/
|
|
@@ -500,10 +550,10 @@ export default function stateModelFactory(
|
|
|
500
550
|
adapterConfig: self.adapterConfig,
|
|
501
551
|
statusCallback: (message: string) => {
|
|
502
552
|
if (isAlive(self)) {
|
|
503
|
-
self.
|
|
553
|
+
self.setStatusMessage(message)
|
|
504
554
|
}
|
|
505
555
|
},
|
|
506
|
-
})) as { samples: Sample[]; tree:
|
|
556
|
+
})) as { samples: Sample[]; tree: NodeWithIds | undefined },
|
|
507
557
|
)
|
|
508
558
|
} catch (e) {
|
|
509
559
|
console.error(e)
|
|
@@ -526,6 +576,43 @@ export default function stateModelFactory(
|
|
|
526
576
|
},
|
|
527
577
|
}
|
|
528
578
|
})
|
|
579
|
+
.postProcessSnapshot(snap => {
|
|
580
|
+
const {
|
|
581
|
+
rowHeight,
|
|
582
|
+
rowProportion,
|
|
583
|
+
showAllLetters,
|
|
584
|
+
mismatchRendering,
|
|
585
|
+
showBranchLen,
|
|
586
|
+
treeAreaWidth,
|
|
587
|
+
showAsUpperCase,
|
|
588
|
+
showSidebar,
|
|
589
|
+
...rest
|
|
590
|
+
} = snap as typeof snap & {
|
|
591
|
+
rowHeight?: number
|
|
592
|
+
rowProportion?: number
|
|
593
|
+
showAllLetters?: boolean
|
|
594
|
+
mismatchRendering?: boolean
|
|
595
|
+
showBranchLen?: boolean
|
|
596
|
+
treeAreaWidth?: number
|
|
597
|
+
showAsUpperCase?: boolean
|
|
598
|
+
showSidebar?: boolean
|
|
599
|
+
}
|
|
600
|
+
return {
|
|
601
|
+
...(rest as Omit<typeof rest, symbol>),
|
|
602
|
+
...(rowHeight !== defaultRowHeight ? { rowHeight } : {}),
|
|
603
|
+
...(rowProportion !== defaultRowProportion ? { rowProportion } : {}),
|
|
604
|
+
...(showAllLetters !== defaultShowAllLetters ? { showAllLetters } : {}),
|
|
605
|
+
...(mismatchRendering !== defaultMismatchRendering
|
|
606
|
+
? { mismatchRendering }
|
|
607
|
+
: {}),
|
|
608
|
+
...(showBranchLen !== defaultShowBranchLen ? { showBranchLen } : {}),
|
|
609
|
+
...(treeAreaWidth !== defaultTreeAreaWidth ? { treeAreaWidth } : {}),
|
|
610
|
+
...(showAsUpperCase !== defaultShowAsUpperCase
|
|
611
|
+
? { showAsUpperCase }
|
|
612
|
+
: {}),
|
|
613
|
+
...(showSidebar !== defaultShowSidebar ? { showSidebar } : {}),
|
|
614
|
+
}
|
|
615
|
+
})
|
|
529
616
|
}
|
|
530
617
|
|
|
531
618
|
export type LinearMafDisplayStateModel = ReturnType<typeof stateModelFactory>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export interface NodeWithIds {
|
|
2
2
|
id: string
|
|
3
3
|
name: string
|
|
4
|
-
children
|
|
4
|
+
children?: NodeWithIds[]
|
|
5
5
|
length?: number
|
|
6
6
|
noTree?: boolean
|
|
7
7
|
}
|
|
@@ -9,7 +9,7 @@ export interface NodeWithIds {
|
|
|
9
9
|
export interface NodeWithIdsAndLength {
|
|
10
10
|
id: string
|
|
11
11
|
name: string
|
|
12
|
-
children
|
|
12
|
+
children?: NodeWithIdsAndLength[]
|
|
13
13
|
noTree?: boolean
|
|
14
14
|
length: number
|
|
15
15
|
}
|
|
@@ -10,7 +10,9 @@ export interface HoveredInfo {
|
|
|
10
10
|
pos: number
|
|
11
11
|
base: string
|
|
12
12
|
chr: string
|
|
13
|
-
|
|
13
|
+
isInsertion?: boolean
|
|
14
|
+
isLargeInsertion?: boolean
|
|
15
|
+
[key: string]: unknown
|
|
14
16
|
}
|
|
15
17
|
|
|
16
18
|
export interface GenomicPosition {
|
|
@@ -75,12 +77,36 @@ export function setBrLength(
|
|
|
75
77
|
y0: number,
|
|
76
78
|
k: number,
|
|
77
79
|
) {
|
|
80
|
+
const newY0 = y0 + Math.max(d.data.length || 0, 0)
|
|
78
81
|
// @ts-expect-error
|
|
79
|
-
d.len =
|
|
82
|
+
d.len = newY0 * k
|
|
80
83
|
|
|
81
84
|
if (d.children) {
|
|
82
|
-
d.children
|
|
83
|
-
setBrLength(
|
|
84
|
-
}
|
|
85
|
+
for (const child of d.children) {
|
|
86
|
+
setBrLength(child, newY0, k)
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export function computeNodeDescendantNames<T extends { name: string }>(
|
|
92
|
+
root: HierarchyNode<T>,
|
|
93
|
+
): Map<HierarchyNode<T>, string[]> {
|
|
94
|
+
const map = new Map<HierarchyNode<T>, string[]>()
|
|
95
|
+
function visit(node: HierarchyNode<T>): string[] {
|
|
96
|
+
if (!node.children || node.children.length === 0) {
|
|
97
|
+
const names = [node.data.name]
|
|
98
|
+
map.set(node, names)
|
|
99
|
+
return names
|
|
100
|
+
}
|
|
101
|
+
const names: string[] = []
|
|
102
|
+
for (const child of node.children) {
|
|
103
|
+
for (const name of visit(child)) {
|
|
104
|
+
names.push(name)
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
map.set(node, names)
|
|
108
|
+
return names
|
|
85
109
|
}
|
|
110
|
+
visit(root)
|
|
111
|
+
return map
|
|
86
112
|
}
|
|
@@ -26,7 +26,7 @@ export default class LinearMafRenderer extends FeatureRendererType {
|
|
|
26
26
|
const bpExpansion = 1
|
|
27
27
|
|
|
28
28
|
return {
|
|
29
|
-
// xref https://github.com/mobxjs/mobx-state-tree/issues/1524 for Omit
|
|
29
|
+
// xref https://github.com/mobxjs/@jbrowse/mobx-state-tree/issues/1524 for Omit
|
|
30
30
|
...(region as Omit<typeof region, symbol>),
|
|
31
31
|
start: Math.floor(Math.max(start - bpExpansion, 0)),
|
|
32
32
|
end: Math.ceil(end + bpExpansion),
|