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.
Files changed (169) hide show
  1. package/dist/BigMafAdapter/BigMafAdapter.js +4 -5
  2. package/dist/BigMafAdapter/BigMafAdapter.js.map +1 -1
  3. package/dist/BigMafAdapter/configSchema.d.ts +2 -2
  4. package/dist/LinearMafDisplay/components/LinearMafDisplayComponent.js +38 -108
  5. package/dist/LinearMafDisplay/components/LinearMafDisplayComponent.js.map +1 -1
  6. package/dist/LinearMafDisplay/components/MAFTooltip.d.ts +0 -3
  7. package/dist/LinearMafDisplay/components/MAFTooltip.js.map +1 -1
  8. package/dist/LinearMafDisplay/components/MsaHighlightOverlay.d.ts +9 -0
  9. package/dist/LinearMafDisplay/components/MsaHighlightOverlay.js +34 -0
  10. package/dist/LinearMafDisplay/components/MsaHighlightOverlay.js.map +1 -0
  11. package/dist/LinearMafDisplay/components/Sidebar/SvgWrapper.js +1 -1
  12. package/dist/LinearMafDisplay/components/Sidebar/SvgWrapper.js.map +1 -1
  13. package/dist/LinearMafDisplay/components/useDragSelection.d.ts +25 -0
  14. package/dist/LinearMafDisplay/components/useDragSelection.js +103 -0
  15. package/dist/LinearMafDisplay/components/useDragSelection.js.map +1 -0
  16. package/dist/LinearMafDisplay/configSchema.d.ts +3 -30
  17. package/dist/LinearMafDisplay/stateModel.d.ts +1043 -121
  18. package/dist/LinearMafDisplay/stateModel.js +85 -41
  19. package/dist/LinearMafDisplay/stateModel.js.map +1 -1
  20. package/dist/LinearMafDisplay/types.d.ts +2 -2
  21. package/dist/LinearMafDisplay/util.d.ts +5 -0
  22. package/dist/LinearMafDisplay/util.js +25 -4
  23. package/dist/LinearMafDisplay/util.js.map +1 -1
  24. package/dist/LinearMafRenderer/LinearMafRenderer.d.ts +41 -5
  25. package/dist/LinearMafRenderer/LinearMafRenderer.js +1 -1
  26. package/dist/LinearMafRenderer/LinearMafRenderer.js.map +1 -1
  27. package/dist/LinearMafRenderer/components/LinearMafRendering.d.ts +14 -5
  28. package/dist/LinearMafRenderer/components/LinearMafRendering.js +21 -19
  29. package/dist/LinearMafRenderer/components/LinearMafRendering.js.map +1 -1
  30. package/dist/LinearMafRenderer/configSchema.d.ts +1 -6
  31. package/dist/LinearMafRenderer/configSchema.js +1 -6
  32. package/dist/LinearMafRenderer/configSchema.js.map +1 -1
  33. package/dist/LinearMafRenderer/rendering/insertions.d.ts +1 -1
  34. package/dist/LinearMafRenderer/rendering/insertions.js +2 -2
  35. package/dist/LinearMafRenderer/rendering/mismatches.d.ts +1 -1
  36. package/dist/LinearMafRenderer/rendering/mismatches.js +3 -3
  37. package/dist/LinearMafRenderer/rendering/types.d.ts +1 -1
  38. package/dist/MafAddTrackWorkflow/AddTrackWorkflow.js +1 -1
  39. package/dist/MafAddTrackWorkflow/AddTrackWorkflow.js.map +1 -1
  40. package/dist/MafAddTrackWorkflow/index.js +1 -1
  41. package/dist/MafAddTrackWorkflow/index.js.map +1 -1
  42. package/dist/MafGetSequences/MafGetSequences.d.ts +1 -0
  43. package/dist/MafGetSequences/MafGetSequences.js +2 -1
  44. package/dist/MafGetSequences/MafGetSequences.js.map +1 -1
  45. package/dist/MafSequenceWidget/LabelsCanvas.d.ts +8 -0
  46. package/dist/MafSequenceWidget/LabelsCanvas.js +37 -0
  47. package/dist/MafSequenceWidget/LabelsCanvas.js.map +1 -0
  48. package/dist/MafSequenceWidget/MafSequenceHoverHighlight.d.ts +6 -0
  49. package/dist/MafSequenceWidget/MafSequenceHoverHighlight.js +52 -0
  50. package/dist/MafSequenceWidget/MafSequenceHoverHighlight.js.map +1 -0
  51. package/dist/MafSequenceWidget/MafSequenceHoverHighlightExtension.d.ts +2 -0
  52. package/dist/MafSequenceWidget/MafSequenceHoverHighlightExtension.js +12 -0
  53. package/dist/MafSequenceWidget/MafSequenceHoverHighlightExtension.js.map +1 -0
  54. package/dist/MafSequenceWidget/MafSequenceWidget.d.ts +6 -0
  55. package/dist/MafSequenceWidget/MafSequenceWidget.js +189 -0
  56. package/dist/MafSequenceWidget/MafSequenceWidget.js.map +1 -0
  57. package/dist/MafSequenceWidget/SequenceCanvas.d.ts +12 -0
  58. package/dist/MafSequenceWidget/SequenceCanvas.js +86 -0
  59. package/dist/MafSequenceWidget/SequenceCanvas.js.map +1 -0
  60. package/dist/MafSequenceWidget/SequenceDisplay.d.ts +12 -0
  61. package/dist/MafSequenceWidget/SequenceDisplay.js +117 -0
  62. package/dist/MafSequenceWidget/SequenceDisplay.js.map +1 -0
  63. package/dist/MafSequenceWidget/SequenceTooltip.d.ts +11 -0
  64. package/dist/MafSequenceWidget/SequenceTooltip.js +39 -0
  65. package/dist/MafSequenceWidget/SequenceTooltip.js.map +1 -0
  66. package/dist/MafSequenceWidget/baseColors.d.ts +3 -0
  67. package/dist/MafSequenceWidget/baseColors.js +64 -0
  68. package/dist/MafSequenceWidget/baseColors.js.map +1 -0
  69. package/dist/MafSequenceWidget/colToGenomePos.d.ts +13 -0
  70. package/dist/MafSequenceWidget/colToGenomePos.js +32 -0
  71. package/dist/MafSequenceWidget/colToGenomePos.js.map +1 -0
  72. package/dist/MafSequenceWidget/colToGenomePos.test.d.ts +1 -0
  73. package/dist/MafSequenceWidget/colToGenomePos.test.js +136 -0
  74. package/dist/MafSequenceWidget/colToGenomePos.test.js.map +1 -0
  75. package/dist/MafSequenceWidget/configSchema.d.ts +1 -0
  76. package/dist/MafSequenceWidget/configSchema.js +3 -0
  77. package/dist/MafSequenceWidget/configSchema.js.map +1 -0
  78. package/dist/MafSequenceWidget/constants.d.ts +4 -0
  79. package/dist/MafSequenceWidget/constants.js +5 -0
  80. package/dist/MafSequenceWidget/constants.js.map +1 -0
  81. package/dist/MafSequenceWidget/index.d.ts +2 -0
  82. package/dist/MafSequenceWidget/index.js +16 -0
  83. package/dist/MafSequenceWidget/index.js.map +1 -0
  84. package/dist/MafSequenceWidget/stateModelFactory.d.ts +67 -0
  85. package/dist/MafSequenceWidget/stateModelFactory.js +21 -0
  86. package/dist/MafSequenceWidget/stateModelFactory.js.map +1 -0
  87. package/dist/MafTabixAdapter/MafTabixAdapter.js +4 -35
  88. package/dist/MafTabixAdapter/MafTabixAdapter.js.map +1 -1
  89. package/dist/MafTabixAdapter/configSchema.d.ts +4 -4
  90. package/dist/MafTrack/configSchema.d.ts +16 -11
  91. package/dist/index.js +2 -0
  92. package/dist/index.js.map +1 -1
  93. package/dist/jbrowse-plugin-mafviewer.umd.production.min.js +12 -24
  94. package/dist/jbrowse-plugin-mafviewer.umd.production.min.js.map +4 -4
  95. package/dist/util/clipboard.d.ts +2 -0
  96. package/dist/util/clipboard.js +28 -0
  97. package/dist/util/clipboard.js.map +1 -0
  98. package/dist/util/fastaUtils.d.ts +2 -1
  99. package/dist/util/fastaUtils.js +72 -2
  100. package/dist/util/fastaUtils.js.map +1 -1
  101. package/dist/util/fastaUtils.test.js +190 -0
  102. package/dist/util/fastaUtils.test.js.map +1 -1
  103. package/dist/util/parseAssemblyName.d.ts +32 -0
  104. package/dist/util/parseAssemblyName.js +87 -0
  105. package/dist/util/parseAssemblyName.js.map +1 -0
  106. package/dist/util/parseAssemblyName.test.d.ts +1 -0
  107. package/dist/util/parseAssemblyName.test.js +269 -0
  108. package/dist/util/parseAssemblyName.test.js.map +1 -0
  109. package/package.json +7 -7
  110. package/src/BigMafAdapter/BigMafAdapter.ts +5 -5
  111. package/src/LinearMafDisplay/components/LinearMafDisplayComponent.tsx +62 -144
  112. package/src/LinearMafDisplay/components/MAFTooltip.tsx +0 -3
  113. package/src/LinearMafDisplay/components/MsaHighlightOverlay.tsx +62 -0
  114. package/src/LinearMafDisplay/components/Sidebar/SvgWrapper.tsx +1 -1
  115. package/src/LinearMafDisplay/components/useDragSelection.ts +159 -0
  116. package/src/LinearMafDisplay/stateModel.ts +135 -48
  117. package/src/LinearMafDisplay/types.ts +2 -2
  118. package/src/LinearMafDisplay/util.ts +31 -5
  119. package/src/LinearMafRenderer/LinearMafRenderer.ts +1 -1
  120. package/src/LinearMafRenderer/components/LinearMafRendering.tsx +38 -24
  121. package/src/LinearMafRenderer/configSchema.ts +1 -6
  122. package/src/LinearMafRenderer/rendering/insertions.ts +2 -2
  123. package/src/LinearMafRenderer/rendering/mismatches.ts +3 -3
  124. package/src/LinearMafRenderer/rendering/types.ts +1 -1
  125. package/src/MafAddTrackWorkflow/AddTrackWorkflow.tsx +1 -1
  126. package/src/MafAddTrackWorkflow/index.ts +1 -1
  127. package/src/MafGetSequences/MafGetSequences.ts +10 -2
  128. package/src/MafSequenceWidget/LabelsCanvas.tsx +58 -0
  129. package/src/MafSequenceWidget/MafSequenceHoverHighlight.tsx +83 -0
  130. package/src/MafSequenceWidget/MafSequenceHoverHighlightExtension.tsx +24 -0
  131. package/src/MafSequenceWidget/MafSequenceWidget.tsx +294 -0
  132. package/src/MafSequenceWidget/SequenceCanvas.tsx +136 -0
  133. package/src/MafSequenceWidget/SequenceDisplay.tsx +188 -0
  134. package/src/MafSequenceWidget/SequenceTooltip.tsx +70 -0
  135. package/src/MafSequenceWidget/baseColors.ts +76 -0
  136. package/src/MafSequenceWidget/colToGenomePos.test.ts +166 -0
  137. package/src/MafSequenceWidget/colToGenomePos.ts +40 -0
  138. package/src/MafSequenceWidget/configSchema.ts +3 -0
  139. package/src/MafSequenceWidget/constants.ts +4 -0
  140. package/src/MafSequenceWidget/index.ts +24 -0
  141. package/src/MafSequenceWidget/stateModelFactory.ts +43 -0
  142. package/src/MafTabixAdapter/MafTabixAdapter.ts +12 -51
  143. package/src/index.ts +2 -0
  144. package/src/util/__snapshots__/fastaUtils.test.ts.snap +35 -0
  145. package/src/util/clipboard.ts +35 -0
  146. package/src/util/fastaUtils.test.ts +199 -0
  147. package/src/util/fastaUtils.ts +94 -1
  148. package/src/util/parseAssemblyName.test.ts +350 -0
  149. package/src/util/parseAssemblyName.ts +106 -0
  150. package/dist/LinearMafDisplay/components/GetSequenceDialog/GetSequenceDialog.d.ts +0 -11
  151. package/dist/LinearMafDisplay/components/GetSequenceDialog/GetSequenceDialog.js +0 -97
  152. package/dist/LinearMafDisplay/components/GetSequenceDialog/GetSequenceDialog.js.map +0 -1
  153. package/dist/LinearMafDisplay/components/InsertionSequenceDialog/InsertionSequenceDialog.d.ts +0 -14
  154. package/dist/LinearMafDisplay/components/InsertionSequenceDialog/InsertionSequenceDialog.js +0 -69
  155. package/dist/LinearMafDisplay/components/InsertionSequenceDialog/InsertionSequenceDialog.js.map +0 -1
  156. package/dist/LinearMafDisplay/components/util.d.ts +0 -1
  157. package/dist/LinearMafDisplay/components/util.js +0 -8
  158. package/dist/LinearMafDisplay/components/util.js.map +0 -1
  159. package/dist/util/fetchSequences.d.ts +0 -18
  160. package/dist/util/fetchSequences.js +0 -39
  161. package/dist/util/fetchSequences.js.map +0 -1
  162. package/dist/util/useSequences.d.ts +0 -21
  163. package/dist/util/useSequences.js +0 -64
  164. package/dist/util/useSequences.js.map +0 -1
  165. package/src/LinearMafDisplay/components/GetSequenceDialog/GetSequenceDialog.tsx +0 -175
  166. package/src/LinearMafDisplay/components/InsertionSequenceDialog/InsertionSequenceDialog.tsx +0 -105
  167. package/src/LinearMafDisplay/components/util.ts +0 -7
  168. package/src/util/fetchSequences.ts +0 -57
  169. 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 { getEnv, getSession, max, measureText } from '@jbrowse/core/util'
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
- import type { Instance } from 'mobx-state-tree'
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: 15,
75
+ rowHeight: defaultRowHeight,
63
76
  /**
64
77
  * #property
65
78
  */
66
- rowProportion: 0.8,
79
+ rowProportion: defaultRowProportion,
67
80
  /**
68
81
  * #property
69
82
  */
70
- showAllLetters: false,
83
+ showAllLetters: defaultShowAllLetters,
71
84
  /**
72
85
  * #property
73
86
  */
74
- mismatchRendering: true,
87
+ mismatchRendering: defaultMismatchRendering,
75
88
 
76
89
  /**
77
90
  * #property
78
91
  */
79
- showBranchLen: false,
92
+ showBranchLen: defaultShowBranchLen,
80
93
 
81
94
  /**
82
95
  * #property
83
96
  */
84
- treeAreaWidth: 80,
97
+ treeAreaWidth: defaultTreeAreaWidth,
85
98
  /**
86
99
  * #property
87
100
  */
88
- showAsUpperCase: true,
101
+ showAsUpperCase: defaultShowAsUpperCase,
89
102
  /**
90
103
  * #property
91
104
  */
92
- showSidebar: true,
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 any,
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({ samples, tree }: { samples: Sample[]; tree: unknown }) {
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
- getSession(self).queueDialog(handleClose => [
201
- InsertionSequenceDialog,
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
- model: self,
204
- onClose: handleClose,
205
- insertionData,
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
- // todo: investigate whether needed, typescript says children always true
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
- setBrLength(r, (r.data.length = 0), width / maxLength(r))
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
- computeDescendants(this.hierarchy)
350
+ return computeNodeDescendantNames(this.hierarchy)
332
351
  }
333
- return map
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) || super.notReady,
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.setMessage(message)
553
+ self.setStatusMessage(message)
504
554
  }
505
555
  },
506
- })) as { samples: Sample[]; tree: unknown },
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: NodeWithIds[]
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: NodeWithIdsAndLength[]
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
- [key: string]: unknown // Allow additional properties for compatibility
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 = (y0 += Math.max(d.data.length || 0, 0)) * k
82
+ d.len = newY0 * k
80
83
 
81
84
  if (d.children) {
82
- d.children.forEach(d => {
83
- setBrLength(d, y0, k)
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),