react-msaview 8.2.0 → 8.4.0

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 (255) hide show
  1. package/bundle/index.js +77 -300
  2. package/bundle/index.js.map +4 -4
  3. package/dist/blockFasta.d.ts +6 -0
  4. package/dist/blockFasta.js +12 -0
  5. package/dist/blockFasta.js.map +1 -0
  6. package/dist/colorSchemes.d.ts +7 -0
  7. package/dist/colorSchemes.js +15 -0
  8. package/dist/colorSchemes.js.map +1 -1
  9. package/dist/columnCounts.d.ts +4 -3
  10. package/dist/columnCounts.js +11 -6
  11. package/dist/columnCounts.js.map +1 -1
  12. package/dist/components/CopyButton.d.ts +1 -1
  13. package/dist/components/CopyButton.js +1 -1
  14. package/dist/components/CopyButton.js.map +1 -1
  15. package/dist/components/DragHandle.js +14 -2
  16. package/dist/components/DragHandle.js.map +1 -1
  17. package/dist/components/Loading.js +10 -11
  18. package/dist/components/Loading.js.map +1 -1
  19. package/dist/components/MSAView.js +15 -9
  20. package/dist/components/MSAView.js.map +1 -1
  21. package/dist/components/MSAViewer.d.ts +39 -7
  22. package/dist/components/MSAViewer.js +72 -22
  23. package/dist/components/MSAViewer.js.map +1 -1
  24. package/dist/components/MetadataTable.d.ts +5 -0
  25. package/dist/components/MetadataTable.js +29 -0
  26. package/dist/components/MetadataTable.js.map +1 -0
  27. package/dist/components/SequenceTextArea.d.ts +11 -0
  28. package/dist/components/SequenceTextArea.js +40 -8
  29. package/dist/components/SequenceTextArea.js.map +1 -1
  30. package/dist/components/StatusMessage.d.ts +12 -0
  31. package/dist/components/StatusMessage.js +43 -0
  32. package/dist/components/StatusMessage.js.map +1 -0
  33. package/dist/components/Track.js +13 -4
  34. package/dist/components/Track.js.map +1 -1
  35. package/dist/components/VerticalScrollbar.js +6 -1
  36. package/dist/components/VerticalScrollbar.js.map +1 -1
  37. package/dist/components/dialogs/ExportSVGDialog.d.ts +3 -2
  38. package/dist/components/dialogs/ExportSVGDialog.js +4 -2
  39. package/dist/components/dialogs/ExportSVGDialog.js.map +1 -1
  40. package/dist/components/dialogs/FeatureDialog.js +15 -10
  41. package/dist/components/dialogs/FeatureDialog.js.map +1 -1
  42. package/dist/components/dialogs/MetadataDialog.js +5 -6
  43. package/dist/components/dialogs/MetadataDialog.js.map +1 -1
  44. package/dist/components/getVisibleLeaves.d.ts +1 -1
  45. package/dist/components/header/ColorSchemeMenu.d.ts +6 -0
  46. package/dist/components/header/ColorSchemeMenu.js +23 -5
  47. package/dist/components/header/ColorSchemeMenu.js.map +1 -1
  48. package/dist/components/header/FileMenu.js +9 -0
  49. package/dist/components/header/FileMenu.js.map +1 -1
  50. package/dist/components/header/GappynessSlider.js +15 -3
  51. package/dist/components/header/GappynessSlider.js.map +1 -1
  52. package/dist/components/header/{HeaderStatusArea.d.ts → GoToBox.d.ts} +2 -2
  53. package/dist/components/header/GoToBox.js +21 -0
  54. package/dist/components/header/GoToBox.js.map +1 -0
  55. package/dist/components/header/Header.js +8 -7
  56. package/dist/components/header/Header.js.map +1 -1
  57. package/dist/components/header/SelectionMenu.d.ts +12 -0
  58. package/dist/components/header/SelectionMenu.js +72 -0
  59. package/dist/components/header/SelectionMenu.js.map +1 -0
  60. package/dist/components/header/goTo.d.ts +20 -0
  61. package/dist/components/header/goTo.js +69 -0
  62. package/dist/components/header/goTo.js.map +1 -0
  63. package/dist/components/header/settingsMenuItems.js +57 -1
  64. package/dist/components/header/settingsMenuItems.js.map +1 -1
  65. package/dist/components/import/ImportForm.js +10 -4
  66. package/dist/components/import/ImportForm.js.map +1 -1
  67. package/dist/components/import/ImportFormExamples.js +19 -27
  68. package/dist/components/import/ImportFormExamples.js.map +1 -1
  69. package/dist/components/minimap/Minimap.js +6 -1
  70. package/dist/components/minimap/Minimap.js.map +1 -1
  71. package/dist/components/msa/AnnotationLegend.js +11 -5
  72. package/dist/components/msa/AnnotationLegend.js.map +1 -1
  73. package/dist/components/msa/MSACanvas.js +17 -1
  74. package/dist/components/msa/MSACanvas.js.map +1 -1
  75. package/dist/components/msa/MSACanvasBlock.js +6 -6
  76. package/dist/components/msa/MSACanvasBlock.js.map +1 -1
  77. package/dist/components/msa/msaKeys.d.ts +5 -0
  78. package/dist/components/msa/msaKeys.js +41 -0
  79. package/dist/components/msa/msaKeys.js.map +1 -0
  80. package/dist/components/msa/msaRaster.js +1 -0
  81. package/dist/components/msa/msaRaster.js.map +1 -1
  82. package/dist/components/msa/renderHighlights.d.ts +14 -3
  83. package/dist/components/msa/renderHighlights.js +29 -4
  84. package/dist/components/msa/renderHighlights.js.map +1 -1
  85. package/dist/components/msa/tileColor.js +9 -8
  86. package/dist/components/msa/tileColor.js.map +1 -1
  87. package/dist/components/msa/useMsaBlockMouse.d.ts +2 -4
  88. package/dist/components/msa/useMsaBlockMouse.js +34 -14
  89. package/dist/components/msa/useMsaBlockMouse.js.map +1 -1
  90. package/dist/components/overlayColors.d.ts +2 -0
  91. package/dist/components/overlayColors.js +3 -0
  92. package/dist/components/overlayColors.js.map +1 -1
  93. package/dist/components/rowpanels/RowPanelBlock.js +10 -14
  94. package/dist/components/rowpanels/RowPanelBlock.js.map +1 -1
  95. package/dist/components/selectionDrag.d.ts +17 -0
  96. package/dist/components/selectionDrag.js +30 -0
  97. package/dist/components/selectionDrag.js.map +1 -0
  98. package/dist/components/tracks/drawTracks.d.ts +1 -0
  99. package/dist/components/tracks/drawTracks.js +14 -2
  100. package/dist/components/tracks/drawTracks.js.map +1 -1
  101. package/dist/components/tracks/useTrackHover.d.ts +4 -6
  102. package/dist/components/tracks/useTrackHover.js +30 -10
  103. package/dist/components/tracks/useTrackHover.js.map +1 -1
  104. package/dist/components/tree/ScaleBarSVG.d.ts +14 -0
  105. package/dist/components/tree/ScaleBarSVG.js +11 -0
  106. package/dist/components/tree/ScaleBarSVG.js.map +1 -0
  107. package/dist/components/tree/TreeCanvas.js +8 -26
  108. package/dist/components/tree/TreeCanvas.js.map +1 -1
  109. package/dist/components/tree/TreeCanvasBlock.js +11 -20
  110. package/dist/components/tree/TreeCanvasBlock.js.map +1 -1
  111. package/dist/components/tree/{TreeBranchMenu.d.ts → TreeMenu.d.ts} +5 -4
  112. package/dist/components/tree/TreeMenu.js +48 -0
  113. package/dist/components/tree/TreeMenu.js.map +1 -0
  114. package/dist/components/tree/TreeRuler.js +3 -3
  115. package/dist/components/tree/TreeRuler.js.map +1 -1
  116. package/dist/components/tree/dialogs/TreeNodeInfoDialog.js +9 -8
  117. package/dist/components/tree/dialogs/TreeNodeInfoDialog.js.map +1 -1
  118. package/dist/components/tree/renderTreeCanvas.js +19 -9
  119. package/dist/components/tree/renderTreeCanvas.js.map +1 -1
  120. package/dist/components/tree/renderTreeMouseover.d.ts +5 -0
  121. package/dist/components/tree/renderTreeMouseover.js +20 -0
  122. package/dist/components/tree/renderTreeMouseover.js.map +1 -0
  123. package/dist/components/tree/useTreeHover.d.ts +3 -8
  124. package/dist/components/tree/useTreeHover.js +8 -10
  125. package/dist/components/tree/useTreeHover.js.map +1 -1
  126. package/dist/components/useHoverAnchor.d.ts +19 -0
  127. package/dist/components/useHoverAnchor.js +18 -0
  128. package/dist/components/useHoverAnchor.js.map +1 -0
  129. package/dist/constants.d.ts +3 -0
  130. package/dist/constants.js +12 -3
  131. package/dist/constants.js.map +1 -1
  132. package/dist/element.d.ts +7 -3
  133. package/dist/element.js +32 -4
  134. package/dist/element.js.map +1 -1
  135. package/dist/expandSpec.js +3 -3
  136. package/dist/expandSpec.js.map +1 -1
  137. package/dist/exportFileName.d.ts +3 -3
  138. package/dist/exportFileName.js +4 -4
  139. package/dist/exportFileName.js.map +1 -1
  140. package/dist/featuresLayer.d.ts +8 -0
  141. package/dist/featuresLayer.js +42 -0
  142. package/dist/featuresLayer.js.map +1 -0
  143. package/dist/hierarchy.d.ts +51 -0
  144. package/dist/hierarchy.js +125 -0
  145. package/dist/hierarchy.js.map +1 -1
  146. package/dist/index.d.ts +2 -2
  147. package/dist/model/DataModel.d.ts +1 -1
  148. package/dist/model/DataModel.js +3 -3
  149. package/dist/model/DataModel.js.map +1 -1
  150. package/dist/model/msaModel.d.ts +14 -1
  151. package/dist/model/msaModel.js +17 -1
  152. package/dist/model/msaModel.js.map +1 -1
  153. package/dist/model/treeModel.d.ts +12 -0
  154. package/dist/model/treeModel.js +14 -1
  155. package/dist/model/treeModel.js.map +1 -1
  156. package/dist/model.d.ts +285 -53
  157. package/dist/model.js +693 -231
  158. package/dist/model.js.map +1 -1
  159. package/dist/renderToSvg.js +2 -4
  160. package/dist/renderToSvg.js.map +1 -1
  161. package/dist/rerootTree.d.ts +33 -0
  162. package/dist/rerootTree.js +144 -0
  163. package/dist/rerootTree.js.map +1 -0
  164. package/dist/svgTestUtil.d.ts +172 -26
  165. package/dist/types.d.ts +75 -12
  166. package/dist/useMsaSvgFigure.d.ts +2 -1
  167. package/dist/useMsaSvgFigure.js +11 -4
  168. package/dist/useMsaSvgFigure.js.map +1 -1
  169. package/dist/version.d.ts +1 -1
  170. package/dist/version.js +1 -1
  171. package/dist/writeNewick.d.ts +8 -0
  172. package/dist/writeNewick.js +34 -0
  173. package/dist/writeNewick.js.map +1 -0
  174. package/package.json +3 -3
  175. package/src/blockFasta.ts +18 -0
  176. package/src/colorSchemes.ts +20 -0
  177. package/src/columnCounts.ts +14 -6
  178. package/src/components/CopyButton.tsx +2 -2
  179. package/src/components/DragHandle.tsx +21 -1
  180. package/src/components/Loading.tsx +14 -30
  181. package/src/components/MSAView.tsx +16 -9
  182. package/src/components/MSAViewer.tsx +121 -26
  183. package/src/components/MetadataTable.tsx +55 -0
  184. package/src/components/SequenceTextArea.tsx +54 -8
  185. package/src/components/StatusMessage.tsx +70 -0
  186. package/src/components/Track.tsx +21 -4
  187. package/src/components/VerticalScrollbar.tsx +7 -0
  188. package/src/components/dialogs/ExportSVGDialog.tsx +5 -2
  189. package/src/components/dialogs/FeatureDialog.tsx +59 -38
  190. package/src/components/dialogs/MetadataDialog.tsx +9 -8
  191. package/src/components/header/ColorSchemeMenu.tsx +31 -12
  192. package/src/components/header/FileMenu.tsx +9 -0
  193. package/src/components/header/GappynessSlider.tsx +17 -4
  194. package/src/components/header/GoToBox.tsx +50 -0
  195. package/src/components/header/Header.tsx +11 -8
  196. package/src/components/header/SelectionMenu.tsx +117 -0
  197. package/src/components/header/goTo.ts +75 -0
  198. package/src/components/header/settingsMenuItems.ts +66 -0
  199. package/src/components/import/ImportForm.tsx +14 -7
  200. package/src/components/import/ImportFormExamples.tsx +20 -28
  201. package/src/components/minimap/Minimap.tsx +7 -0
  202. package/src/components/msa/AnnotationLegend.tsx +12 -6
  203. package/src/components/msa/MSACanvas.tsx +19 -1
  204. package/src/components/msa/MSACanvasBlock.tsx +9 -11
  205. package/src/components/msa/msaKeys.ts +45 -0
  206. package/src/components/msa/msaRaster.ts +1 -0
  207. package/src/components/msa/renderHighlights.ts +46 -4
  208. package/src/components/msa/tileColor.ts +9 -8
  209. package/src/components/msa/useMsaBlockMouse.ts +44 -31
  210. package/src/components/overlayColors.ts +4 -0
  211. package/src/components/rowpanels/RowPanelBlock.tsx +16 -23
  212. package/src/components/selectionDrag.ts +42 -0
  213. package/src/components/tracks/drawTracks.ts +15 -2
  214. package/src/components/tracks/useTrackHover.ts +32 -10
  215. package/src/components/tree/ScaleBarSVG.tsx +30 -0
  216. package/src/components/tree/TreeCanvas.tsx +9 -40
  217. package/src/components/tree/TreeCanvasBlock.tsx +19 -39
  218. package/src/components/tree/TreeMenu.tsx +129 -0
  219. package/src/components/tree/TreeRuler.tsx +7 -13
  220. package/src/components/tree/dialogs/TreeNodeInfoDialog.tsx +14 -16
  221. package/src/components/tree/renderTreeCanvas.ts +20 -8
  222. package/src/components/tree/renderTreeMouseover.ts +41 -0
  223. package/src/components/tree/useTreeHover.ts +8 -17
  224. package/src/components/useHoverAnchor.ts +31 -0
  225. package/src/constants.ts +14 -3
  226. package/src/element.ts +45 -5
  227. package/src/expandSpec.ts +3 -3
  228. package/src/exportFileName.ts +4 -3
  229. package/src/featuresLayer.ts +48 -0
  230. package/src/hierarchy.ts +151 -4
  231. package/src/index.ts +8 -1
  232. package/src/model/DataModel.ts +3 -3
  233. package/src/model/msaModel.ts +19 -1
  234. package/src/model/treeModel.ts +22 -0
  235. package/src/model.ts +841 -272
  236. package/src/renderToSvg.tsx +6 -9
  237. package/src/rerootTree.ts +172 -0
  238. package/src/types.ts +92 -12
  239. package/src/useMsaSvgFigure.ts +11 -4
  240. package/src/version.ts +1 -1
  241. package/src/writeNewick.ts +38 -0
  242. package/dist/components/PortalTooltip.d.ts +0 -11
  243. package/dist/components/PortalTooltip.js +0 -29
  244. package/dist/components/PortalTooltip.js.map +0 -1
  245. package/dist/components/header/HeaderStatusArea.js +0 -21
  246. package/dist/components/header/HeaderStatusArea.js.map +0 -1
  247. package/dist/components/tree/TreeBranchMenu.js +0 -29
  248. package/dist/components/tree/TreeBranchMenu.js.map +0 -1
  249. package/dist/components/tree/TreeNodeMenu.d.ts +0 -13
  250. package/dist/components/tree/TreeNodeMenu.js +0 -41
  251. package/dist/components/tree/TreeNodeMenu.js.map +0 -1
  252. package/src/components/PortalTooltip.tsx +0 -47
  253. package/src/components/header/HeaderStatusArea.tsx +0 -32
  254. package/src/components/tree/TreeBranchMenu.tsx +0 -68
  255. package/src/components/tree/TreeNodeMenu.tsx +0 -95
@@ -14,6 +14,7 @@ import { visibleColRange } from './components/msa/visibleColRange.ts'
14
14
  import { headerRunsAcross } from './components/rowpanels/headerLayout.ts'
15
15
  import { renderRowPanels } from './components/rowpanels/renderRowPanel.ts'
16
16
  import { renderAllTracks } from './components/tracks/drawTracks.ts'
17
+ import ScaleBarSVG from './components/tree/ScaleBarSVG.tsx'
17
18
  import {
18
19
  bracketGap,
19
20
  cladeHeight,
@@ -323,17 +324,13 @@ function TreeScaleBarSVG({
323
324
  if (!bar) {
324
325
  return null
325
326
  }
326
- const y = treeOverviewHeight + treeScaleBarHeight
327
- const color = theme.palette.text.primary
328
327
  return (
329
328
  <g id="tree-scalebar">
330
- <text x={marginLeft} y={y - 9} fontSize={10} fill={color}>
331
- {bar.label}
332
- </text>
333
- <path
334
- d={`M${marginLeft} ${y - 7} v6 h${bar.px} v-6`}
335
- fill="none"
336
- stroke={color}
329
+ <ScaleBarSVG
330
+ bar={bar}
331
+ marginLeft={marginLeft}
332
+ y={treeOverviewHeight + treeScaleBarHeight}
333
+ color={theme.palette.text.primary}
337
334
  />
338
335
  </g>
339
336
  )
@@ -0,0 +1,172 @@
1
+ /**
2
+ * Rerooting over the plain node `parseNewick` produces, the shape
3
+ * `@gmod/newick` and jbrowse-components' tree sidebar share, so these can move
4
+ * into that package unchanged.
5
+ *
6
+ * Every traversal is iterative, for the reason the ones in `@gmod/newick` are: a
7
+ * caterpillar tree is as deep as it has leaves, and recursion overflows the
8
+ * stack past about 5000 of them.
9
+ */
10
+ export interface TreeNode {
11
+ name?: string
12
+ length?: number
13
+ children?: TreeNode[]
14
+ }
15
+
16
+ function preorder<N extends TreeNode>(root: N) {
17
+ const order: N[] = []
18
+ const parents = new Map<N, N>()
19
+ const stack = [root]
20
+ while (stack.length > 0) {
21
+ const node = stack.pop()!
22
+ order.push(node)
23
+ for (const child of (node.children ?? []) as N[]) {
24
+ parents.set(child, node)
25
+ stack.push(child)
26
+ }
27
+ }
28
+ return { order, parents }
29
+ }
30
+
31
+ function hasLengths(order: TreeNode[]) {
32
+ return order.some(n => n.length !== undefined)
33
+ }
34
+
35
+ // the root's two children make one branch of the unrooted tree, which is the
36
+ // branch a rerooting at either of them splits
37
+ function mergedSibling<N extends TreeNode>(root: N, target: N) {
38
+ return root.children?.length === 2
39
+ ? (root.children.find(c => c !== target) as N | undefined)
40
+ : undefined
41
+ }
42
+
43
+ /**
44
+ * The tree rooted on the branch above `target`, `offset` up from it, or at the
45
+ * middle of that branch without one. Where the root has two children, their
46
+ * branches are one branch of the unrooted tree, and `offset` runs across both.
47
+ *
48
+ * The nodes between `target` and the old root turn over. A name on one of them
49
+ * labels the branch above it, which is where a support value sits, so the name
50
+ * follows that branch to the node now at its lower end. A two-child root has
51
+ * nothing left to split once one child leaves, so it drops out and its
52
+ * branches join.
53
+ */
54
+ export function rerootTree<N extends TreeNode>(
55
+ root: N,
56
+ target: N,
57
+ offset?: number,
58
+ ): TreeNode {
59
+ const { order, parents } = preorder(root)
60
+ if (!parents.has(target)) {
61
+ return root
62
+ }
63
+ const lengths = hasLengths(order)
64
+ const sibling =
65
+ parents.get(target) === root ? mergedSibling(root, target) : undefined
66
+ const branch = (target.length ?? 0) + (sibling?.length ?? 0)
67
+ const at = Math.min(Math.max(offset ?? branch / 2, 0), branch)
68
+ const length = (n: number) => (lengths ? n : undefined)
69
+
70
+ const path = [target]
71
+ for (let n = parents.get(target); n; n = parents.get(n)) {
72
+ path.push(n)
73
+ }
74
+ // Built from the old root down, so the node each turned node now hangs
75
+ // `above` it already exists. undefined where the old root dropped out.
76
+ let above: TreeNode | undefined
77
+ for (let i = path.length - 1; i >= 1; i--) {
78
+ const node = path[i]!
79
+ const below = path[i - 1]!
80
+ const children = node.children!.filter(c => c !== below)
81
+ if (above) {
82
+ children.push(above)
83
+ }
84
+ if (children.length === 0) {
85
+ above = undefined
86
+ } else if (i === path.length - 1 && children.length === 1) {
87
+ const only = children[0]!
88
+ above = {
89
+ ...only,
90
+ length: length(
91
+ (only.length ?? 0) + (below.length ?? 0) - (i === 1 ? at : 0),
92
+ ),
93
+ }
94
+ } else {
95
+ above = {
96
+ ...node,
97
+ name: i === 1 ? undefined : below.name,
98
+ children,
99
+ length: length((below.length ?? 0) - (i === 1 ? at : 0)),
100
+ }
101
+ }
102
+ }
103
+ return {
104
+ children: [{ ...target, length: length(at) }, ...(above ? [above] : [])],
105
+ }
106
+ }
107
+
108
+ /**
109
+ * The tree rooted halfway along the longest path between two tips, which is
110
+ * where a clock would put the root: the usual choice for the unrooted tree
111
+ * FastTree, IQ-TREE or neighbor joining writes. A tree with no branch lengths
112
+ * counts branches.
113
+ */
114
+ export function midpointRoot<N extends TreeNode>(root: N): TreeNode {
115
+ const { order, parents } = preorder(root)
116
+ const tips = order.filter(n => !n.children?.length)
117
+ if (tips.length < 2) {
118
+ return root
119
+ }
120
+ const lengths = hasLengths(order)
121
+ const weight = (n: N) => (lengths ? (n.length ?? 0) : 1)
122
+
123
+ // distance from `from` to every node over the unrooted tree, and the
124
+ // neighbor each was reached through
125
+ function reach(from: N) {
126
+ const dist = new Map<N, number>([[from, 0]])
127
+ const via = new Map<N, N>()
128
+ const stack = [from]
129
+ while (stack.length > 0) {
130
+ const node = stack.pop()!
131
+ const d = dist.get(node)!
132
+ const parent = parents.get(node)
133
+ if (parent && !dist.has(parent)) {
134
+ dist.set(parent, d + weight(node))
135
+ via.set(parent, node)
136
+ stack.push(parent)
137
+ }
138
+ for (const child of (node.children ?? []) as N[]) {
139
+ if (!dist.has(child)) {
140
+ dist.set(child, d + weight(child))
141
+ via.set(child, node)
142
+ stack.push(child)
143
+ }
144
+ }
145
+ }
146
+ return { dist, via }
147
+ }
148
+ function farthest(dist: Map<N, number>) {
149
+ return tips.reduce((best, tip) =>
150
+ dist.get(tip)! > dist.get(best)! ? tip : best,
151
+ )
152
+ }
153
+
154
+ const a = farthest(reach(tips[0]!).dist)
155
+ const { dist, via } = reach(a)
156
+ const b = farthest(dist)
157
+ if (b === a) {
158
+ return root
159
+ }
160
+ const half = dist.get(b)! / 2
161
+ let node = b
162
+ let next = via.get(node)!
163
+ while (dist.get(next)! > half) {
164
+ node = next
165
+ next = via.get(node)!
166
+ }
167
+ // the midpoint lies on the branch between `node` and `next`, and whichever
168
+ // of them is the child carries that branch
169
+ return parents.get(node) === next
170
+ ? rerootTree(root, node, dist.get(node)! - half)
171
+ : rerootTree(root, next, half - dist.get(next)!)
172
+ }
package/src/types.ts CHANGED
@@ -17,6 +17,13 @@ export type TrackKind = 'text' | 'bar' | 'logo' | 'arc' | 'ruler'
17
17
  */
18
18
  export type ResidueEncoding = 'fill' | 'color'
19
19
 
20
+ /**
21
+ * The residue colors: a scheme name from the built-in table, or a scale's
22
+ * `{map}` of letter to color, which leaves every letter it does not list
23
+ * uncolored
24
+ */
25
+ export type ColorScheme = string | Required<Pick<ScaleSpec, 'map'>>
26
+
20
27
  export interface BasicTrackModel {
21
28
  id: string
22
29
  name: string
@@ -324,13 +331,59 @@ export interface Highlight {
324
331
  color?: string
325
332
  }
326
333
 
334
+ /**
335
+ * A feature on one row as data, the JSON form of a GFF line: `start` and `end`
336
+ * are residues of `row`, 1-based and inclusive. `name` is the key the legend,
337
+ * the palette and the filter dialog group features by, `color` overrides the
338
+ * palette and any scale, and a `strand` draws the span as an arrow. Every other
339
+ * field is a column the `featureFill` and `featureLabel` encodings can read.
340
+ */
341
+ export interface Feature {
342
+ row: string
343
+ start: number
344
+ end: number
345
+ name?: string
346
+ description?: string
347
+ /** a GFF feature type; `exon`, `CDS` and the UTRs draw as numbered segments */
348
+ type?: string
349
+ strand?: '+' | '-'
350
+ color?: string
351
+ [field: string]: string | number | undefined
352
+ }
353
+
327
354
  /**
328
355
  * What the viewer draws over a clade. `highlight` fills the rows behind it,
329
356
  * across the tree and the alignment. `bracket` draws a bar and the clade's
330
- * `label` in the gutter right of the tip labels. `collapse` and `focus` seed
331
- * the collapsed list and the subtree in focus at load.
357
+ * `label` in the gutter right of the tip labels. `collapse`, `focus` and
358
+ * `rotate` seed the collapsed list, the subtree in focus and the rotated nodes
359
+ * at load.
360
+ */
361
+ export type CladeMark =
362
+ | 'highlight'
363
+ | 'bracket'
364
+ | 'collapse'
365
+ | 'focus'
366
+ | 'rotate'
367
+
368
+ /**
369
+ * The order each node's children draw in, top to bottom. `branchLength` puts
370
+ * the shortest branch first, `input` keeps the file's order, `ladderize` puts
371
+ * the clade with the fewest tips first, so the deep clades step down toward
372
+ * the bottom, and `ladderizeReverse` puts the one with the most tips first.
332
373
  */
333
- export type CladeMark = 'highlight' | 'bracket' | 'collapse' | 'focus'
374
+ export type TreeOrder =
375
+ | 'branchLength'
376
+ | 'input'
377
+ | 'ladderize'
378
+ | 'ladderizeReverse'
379
+
380
+ /**
381
+ * Where the tree is rooted: `midpoint` halfway along the longest path between
382
+ * two tips, and `{outgroup}` on the branch above those tips' most recent common
383
+ * ancestor, or above the rest of the tips' where the outgroup straddles the
384
+ * root the file gives.
385
+ */
386
+ export type TreeRoot = 'midpoint' | { outgroup: string[] }
334
387
 
335
388
  /**
336
389
  * A clade of the tree and the mark drawn over it. `mrca` names tips whose most
@@ -375,11 +428,33 @@ export interface Region {
375
428
  end: number
376
429
  }
377
430
 
431
+ /**
432
+ * A block of the alignment the reader selected: columns `start` to `end` of the
433
+ * file, 1-based and inclusive like a Highlight without a row, across the rows
434
+ * `rows` names, or across every row where `rows` is absent.
435
+ */
436
+ export interface MsaSelection {
437
+ start: number
438
+ end: number
439
+ rows?: string[]
440
+ }
441
+
442
+ /**
443
+ * An MsaSelection resolved to what is on screen: the visible columns it covers
444
+ * and its rows as runs of consecutive row indices, top to bottom
445
+ */
446
+ export interface ResolvedSelection {
447
+ startCol: number
448
+ endCol: number
449
+ rowRuns: [number, number][]
450
+ }
451
+
378
452
  /**
379
453
  * A cell of the alignment in the host's coordinates, 1-based like Highlight:
380
454
  * `column` counts every column of the file, hidden gappy ones included, and
381
- * `residue` counts the row's own letters, absent on a gap. A pointer over a
382
- * track has a column and no row.
455
+ * `residue` counts the row's own letters, absent on a gap. `letter` is the
456
+ * character in the cell, the gap character (`-` or `.`) on a gap. A pointer
457
+ * over a track has a column and no row.
383
458
  */
384
459
  export interface Cell {
385
460
  column: number
@@ -394,6 +469,16 @@ export interface Viewport {
394
469
  endColumn: number
395
470
  }
396
471
 
472
+ /**
473
+ * A row table as a host passes it: fields per row name. A number or a boolean
474
+ * reads as its string, the way a GFF value does, and the viewer leaves out any
475
+ * other value. See docs/layers.md
476
+ */
477
+ export type RowDataInput = Record<
478
+ string,
479
+ Record<string, string | number | boolean | null>
480
+ >
481
+
397
482
  /**
398
483
  * A channel of a mark the viewer always draws. `tipLabel` colors each tip label
399
484
  * in the tree, `rowTint` washes the row across the tree gutter and the
@@ -444,10 +529,5 @@ export interface NodeWithIds {
444
529
  noTree?: boolean
445
530
  }
446
531
 
447
- export interface NodeWithIdsAndLength {
448
- id: string
449
- name: string
450
- children: NodeWithIdsAndLength[]
451
- noTree?: boolean
452
- length: number
453
- }
532
+ /** @deprecated the same type as `NodeWithIds` */
533
+ export type NodeWithIdsAndLength = NodeWithIds
@@ -2,7 +2,7 @@ import { useEffect, useState } from 'react'
2
2
 
3
3
  import { createJBrowseTheme } from '@jbrowse/core/ui/theme'
4
4
  import { getSnapshot } from '@jbrowse/mobx-state-tree'
5
- import { compareStructural, reaction } from 'mobx'
5
+ import { compareShallow, reaction } from 'mobx'
6
6
 
7
7
  import { renderToSvg } from './renderToSvg.tsx'
8
8
 
@@ -18,7 +18,8 @@ export interface MsaSvgFigure {
18
18
  /**
19
19
  * The viewer's SVG export of its viewport, as markup for the page to insert.
20
20
  * It redraws after the view settles: `delay` ms after the last scroll, zoom,
21
- * resize or change to the snapshot. A draw a later one overtakes is dropped.
21
+ * resize, highlight or change to the snapshot. A draw a later one overtakes is
22
+ * dropped.
22
23
  *
23
24
  * The figure's row names and letters are SVG text, so the browser's find
24
25
  * locates them, a reader can select them, and print stylesheets apply.
@@ -37,7 +38,13 @@ export function useMsaSvgFigure(
37
38
  const dispose = reaction(
38
39
  () =>
39
40
  model.viewInitialized && model.dataInitialized
40
- ? [getSnapshot(model), model.width, model.height]
41
+ ? [
42
+ getSnapshot(model),
43
+ model.width,
44
+ model.height,
45
+ model.resolvedHighlights,
46
+ model.highlightedColumnRuns,
47
+ ]
41
48
  : undefined,
42
49
  key => {
43
50
  if (!key) {
@@ -61,7 +68,7 @@ export function useMsaSvgFigure(
61
68
  },
62
69
  )
63
70
  },
64
- { delay, fireImmediately: true, equals: compareStructural },
71
+ { delay, fireImmediately: true, equals: compareShallow },
65
72
  )
66
73
  return () => {
67
74
  latest = -1
package/src/version.ts CHANGED
@@ -1 +1 @@
1
- export const version = '8.2.0'
1
+ export const version = '8.4.0'
@@ -0,0 +1,38 @@
1
+ import { descendants } from './hierarchy.ts'
2
+
3
+ import type { HierarchyNode } from './hierarchy.ts'
4
+
5
+ // a name holding any of these reads back as grammar unless it is quoted
6
+ const needsQuotes = /[\s(),:;[\]']/
7
+
8
+ function quote(name: string) {
9
+ return needsQuotes.test(name) ? `'${name.replaceAll("'", "''")}'` : name
10
+ }
11
+
12
+ /**
13
+ * The tree as Newick, in the order it is drawn. An internal node whose name is
14
+ * only its generated id goes unnamed. Each subtree's string is dropped once its
15
+ * parent takes it in, since a caterpillar tree is as deep as it has tips and
16
+ * keeping every one is quadratic.
17
+ */
18
+ export function writeNewick(root: HierarchyNode) {
19
+ const written = new Map<HierarchyNode, string>()
20
+ for (const node of descendants(root).reverse()) {
21
+ const { id, name, length } = node.data
22
+ let text = ''
23
+ if (node.children) {
24
+ text = `(${node.children.map(c => written.get(c)!).join(',')})`
25
+ for (const child of node.children) {
26
+ written.delete(child)
27
+ }
28
+ }
29
+ if (!(node.children && name === id)) {
30
+ text += quote(name)
31
+ }
32
+ if (length !== undefined) {
33
+ text += `:${length}`
34
+ }
35
+ written.set(node, text)
36
+ }
37
+ return `${written.get(root)!};`
38
+ }
@@ -1,11 +0,0 @@
1
- import React from 'react';
2
- /**
3
- * A tooltip beside the pointer, portaled to the body. A panel's scroll
4
- * transform is a containing block, so a fixed tooltip left inside it would
5
- * position against the scrolled content instead of the window.
6
- */
7
- export default function PortalTooltip({ clientX, clientY, children, }: {
8
- clientX: number;
9
- clientY: number;
10
- children: React.ReactNode;
11
- }): React.ReactPortal;
@@ -1,29 +0,0 @@
1
- import React from 'react';
2
- import { makeStyles } from '@jbrowse/core/util/tss-react';
3
- import { createPortal } from 'react-dom';
4
- const useStyles = makeStyles()(theme => ({
5
- tooltip: {
6
- position: 'fixed',
7
- pointerEvents: 'none',
8
- zIndex: 10000,
9
- backgroundColor: theme.palette.grey[700],
10
- color: theme.palette.common.white,
11
- padding: '4px 8px',
12
- borderRadius: 4,
13
- fontSize: 12,
14
- whiteSpace: 'nowrap',
15
- maxWidth: 300,
16
- overflow: 'hidden',
17
- textOverflow: 'ellipsis',
18
- },
19
- }));
20
- /**
21
- * A tooltip beside the pointer, portaled to the body. A panel's scroll
22
- * transform is a containing block, so a fixed tooltip left inside it would
23
- * position against the scrolled content instead of the window.
24
- */
25
- export default function PortalTooltip({ clientX, clientY, children, }) {
26
- const { classes } = useStyles();
27
- return createPortal(React.createElement("div", { className: classes.tooltip, style: { left: clientX + 12, top: clientY + 12 } }, children), document.body);
28
- }
29
- //# sourceMappingURL=PortalTooltip.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"PortalTooltip.js","sourceRoot":"","sources":["../../src/components/PortalTooltip.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAEzB,OAAO,EAAE,UAAU,EAAE,MAAM,8BAA8B,CAAA;AACzD,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAA;AAExC,MAAM,SAAS,GAAG,UAAU,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IACvC,OAAO,EAAE;QACP,QAAQ,EAAE,OAAO;QACjB,aAAa,EAAE,MAAM;QACrB,MAAM,EAAE,KAAK;QACb,eAAe,EAAE,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC;QACxC,KAAK,EAAE,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK;QACjC,OAAO,EAAE,SAAS;QAClB,YAAY,EAAE,CAAC;QACf,QAAQ,EAAE,EAAE;QACZ,UAAU,EAAE,QAAQ;QACpB,QAAQ,EAAE,GAAG;QACb,QAAQ,EAAE,QAAQ;QAClB,YAAY,EAAE,UAAU;KACzB;CACF,CAAC,CAAC,CAAA;AAEH;;;;GAIG;AACH,MAAM,CAAC,OAAO,UAAU,aAAa,CAAC,EACpC,OAAO,EACP,OAAO,EACP,QAAQ,GAKT;IACC,MAAM,EAAE,OAAO,EAAE,GAAG,SAAS,EAAE,CAAA;IAC/B,OAAO,YAAY,CACjB,6BACE,SAAS,EAAE,OAAO,CAAC,OAAO,EAC1B,KAAK,EAAE,EAAE,IAAI,EAAE,OAAO,GAAG,EAAE,EAAE,GAAG,EAAE,OAAO,GAAG,EAAE,EAAE,IAE/C,QAAQ,CACL,EACN,QAAQ,CAAC,IAAI,CACd,CAAA;AACH,CAAC"}
@@ -1,21 +0,0 @@
1
- import React from 'react';
2
- import { LoadingEllipses } from '@jbrowse/core/ui';
3
- import { makeStyles } from '@jbrowse/core/util/tss-react';
4
- import { Button, Typography } from '@mui/material';
5
- import { observer } from 'mobx-react';
6
- const useStyles = makeStyles()({
7
- margin: {
8
- margin: 'auto',
9
- marginLeft: 10,
10
- },
11
- });
12
- const HeaderStatusArea = observer(({ model }) => {
13
- const { status } = model;
14
- const { classes } = useStyles();
15
- return status ? (React.createElement(Typography, { className: classes.margin },
16
- React.createElement(LoadingEllipses, { message: status.msg, component: "span" }),
17
- ' ',
18
- status.onCancel ? (React.createElement(Button, { size: "small", onClick: () => status.onCancel?.() }, "Cancel")) : null)) : null;
19
- });
20
- export default HeaderStatusArea;
21
- //# sourceMappingURL=HeaderStatusArea.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"HeaderStatusArea.js","sourceRoot":"","sources":["../../../src/components/header/HeaderStatusArea.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAEzB,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAA;AAClD,OAAO,EAAE,UAAU,EAAE,MAAM,8BAA8B,CAAA;AACzD,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,eAAe,CAAA;AAClD,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AAIrC,MAAM,SAAS,GAAG,UAAU,EAAE,CAAC;IAC7B,MAAM,EAAE;QACN,MAAM,EAAE,MAAM;QACd,UAAU,EAAE,EAAE;KACf;CACF,CAAC,CAAA;AAEF,MAAM,gBAAgB,GAAG,QAAQ,CAAC,CAAC,EAAE,KAAK,EAA2B,EAAE,EAAE;IACvE,MAAM,EAAE,MAAM,EAAE,GAAG,KAAK,CAAA;IACxB,MAAM,EAAE,OAAO,EAAE,GAAG,SAAS,EAAE,CAAA;IAC/B,OAAO,MAAM,CAAC,CAAC,CAAC,CACd,oBAAC,UAAU,IAAC,SAAS,EAAE,OAAO,CAAC,MAAM;QACnC,oBAAC,eAAe,IAAC,OAAO,EAAE,MAAM,CAAC,GAAG,EAAE,SAAS,EAAC,MAAM,GAAG;QAAC,GAAG;QAC5D,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CACjB,oBAAC,MAAM,IAAC,IAAI,EAAC,OAAO,EAAC,OAAO,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,QAAQ,EAAE,EAAE,aAE9C,CACV,CAAC,CAAC,CAAC,IAAI,CACG,CACd,CAAC,CAAC,CAAC,IAAI,CAAA;AACV,CAAC,CAAC,CAAA;AAEF,eAAe,gBAAgB,CAAA"}
@@ -1,29 +0,0 @@
1
- import React from 'react';
2
- import { Menu, MenuItem } from '@mui/material';
3
- import { observer } from 'mobx-react';
4
- const TreeBranchMenu = observer(function ({ node, model, onClose, }) {
5
- return (React.createElement(Menu, { anchorReference: "anchorPosition", anchorPosition: {
6
- left: node.x,
7
- top: node.y,
8
- }, transitionDuration: 0, open: true, onClose: onClose },
9
- React.createElement(MenuItem, { dense: true, disabled: true }, node.name),
10
- React.createElement(MenuItem, { dense: true, onClick: () => {
11
- model.toggleCollapsed(node.id);
12
- onClose();
13
- } }, model.collapsed.includes(node.id)
14
- ? 'Expand this node'
15
- : 'Collapse this node'),
16
- React.createElement(MenuItem, { dense: true, onClick: () => {
17
- if (model.showOnly === node.id) {
18
- model.setShowOnly(undefined);
19
- }
20
- else {
21
- model.setShowOnly(node.id);
22
- }
23
- onClose();
24
- } }, model.showOnly === node.id
25
- ? 'Disable show only this node'
26
- : 'Show only this node')));
27
- });
28
- export default TreeBranchMenu;
29
- //# sourceMappingURL=TreeBranchMenu.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"TreeBranchMenu.js","sourceRoot":"","sources":["../../../src/components/tree/TreeBranchMenu.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAEzB,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AAC9C,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AAWrC,MAAM,cAAc,GAAG,QAAQ,CAAC,UAAU,EACxC,IAAI,EACJ,KAAK,EACL,OAAO,GAKR;IACC,OAAO,CACL,oBAAC,IAAI,IACH,eAAe,EAAC,gBAAgB,EAChC,cAAc,EAAE;YACd,IAAI,EAAE,IAAI,CAAC,CAAC;YACZ,GAAG,EAAE,IAAI,CAAC,CAAC;SACZ,EACD,kBAAkB,EAAE,CAAC,EACrB,IAAI,QACJ,OAAO,EAAE,OAAO;QAEhB,oBAAC,QAAQ,IAAC,KAAK,QAAC,QAAQ,UACrB,IAAI,CAAC,IAAI,CACD;QACX,oBAAC,QAAQ,IACP,KAAK,QACL,OAAO,EAAE,GAAG,EAAE;gBACZ,KAAK,CAAC,eAAe,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;gBAC9B,OAAO,EAAE,CAAA;YACX,CAAC,IAEA,KAAK,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;YAChC,CAAC,CAAC,kBAAkB;YACpB,CAAC,CAAC,oBAAoB,CACf;QACX,oBAAC,QAAQ,IACP,KAAK,QACL,OAAO,EAAE,GAAG,EAAE;gBACZ,IAAI,KAAK,CAAC,QAAQ,KAAK,IAAI,CAAC,EAAE,EAAE,CAAC;oBAC/B,KAAK,CAAC,WAAW,CAAC,SAAS,CAAC,CAAA;gBAC9B,CAAC;qBAAM,CAAC;oBACN,KAAK,CAAC,WAAW,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;gBAC5B,CAAC;gBACD,OAAO,EAAE,CAAA;YACX,CAAC,IAEA,KAAK,CAAC,QAAQ,KAAK,IAAI,CAAC,EAAE;YACzB,CAAC,CAAC,6BAA6B;YAC/B,CAAC,CAAC,qBAAqB,CAChB,CACN,CACR,CAAA;AACH,CAAC,CAAC,CAAA;AAEF,eAAe,cAAc,CAAA"}
@@ -1,13 +0,0 @@
1
- import React from 'react';
2
- import type { MsaViewModel } from '../../model.ts';
3
- declare const TreeMenu: ({ node, onClose, model, }: {
4
- node: {
5
- x: number;
6
- y: number;
7
- name: string;
8
- id: string;
9
- };
10
- model: MsaViewModel;
11
- onClose: () => void;
12
- }) => React.JSX.Element;
13
- export default TreeMenu;
@@ -1,41 +0,0 @@
1
- import React, { lazy } from 'react';
2
- import { Menu, MenuItem } from '@mui/material';
3
- import { observer } from 'mobx-react';
4
- // lazies
5
- const TreeNodeInfoDialog = lazy(() => import('./dialogs/TreeNodeInfoDialog.js'));
6
- const TreeMenu = observer(function ({ node, onClose, model, }) {
7
- const { collapsed } = model;
8
- const { name, id } = node;
9
- const isCollapsed = collapsed.includes(id);
10
- return (React.createElement(Menu, { anchorReference: "anchorPosition", anchorPosition: {
11
- top: node.y,
12
- left: node.x,
13
- }, transitionDuration: 0, open: true, onClose: onClose },
14
- React.createElement(MenuItem, { dense: true, disabled: true }, name),
15
- React.createElement(MenuItem, { dense: true, onClick: () => {
16
- model.queueDialog(onClose => [
17
- TreeNodeInfoDialog,
18
- {
19
- info: model.getRowData(name),
20
- model,
21
- nodeName: name,
22
- onClose,
23
- },
24
- ]);
25
- onClose();
26
- } }, "More info..."),
27
- React.createElement(MenuItem, { dense: true, onClick: () => {
28
- model.toggleCollapsed(id);
29
- onClose();
30
- } }, isCollapsed ? 'Expand subtree' : 'Collapse subtree'),
31
- React.createElement(MenuItem, { dense: true, onClick: () => {
32
- model.drawRelativeTo(node.name);
33
- onClose();
34
- } }, "Indicate differences from this row"),
35
- model.relativeTo ? (React.createElement(MenuItem, { dense: true, onClick: () => {
36
- model.drawRelativeTo(undefined);
37
- onClose();
38
- } }, "Clear reference row")) : null));
39
- });
40
- export default TreeMenu;
41
- //# sourceMappingURL=TreeNodeMenu.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"TreeNodeMenu.js","sourceRoot":"","sources":["../../../src/components/tree/TreeNodeMenu.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,IAAI,EAAE,MAAM,OAAO,CAAA;AAEnC,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AAC9C,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AAIrC,SAAS;AACT,MAAM,kBAAkB,GAAG,IAAI,CAC7B,GAAG,EAAE,CAAC,MAAM,CAAC,iCAAkC,CAAC,CACjD,CAAA;AAED,MAAM,QAAQ,GAAG,QAAQ,CAAC,UAAU,EAClC,IAAI,EACJ,OAAO,EACP,KAAK,GAUN;IACC,MAAM,EAAE,SAAS,EAAE,GAAG,KAAK,CAAA;IAC3B,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,GAAG,IAAI,CAAA;IACzB,MAAM,WAAW,GAAG,SAAS,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAA;IAC1C,OAAO,CACL,oBAAC,IAAI,IACH,eAAe,EAAC,gBAAgB,EAChC,cAAc,EAAE;YACd,GAAG,EAAE,IAAI,CAAC,CAAC;YACX,IAAI,EAAE,IAAI,CAAC,CAAC;SACb,EACD,kBAAkB,EAAE,CAAC,EACrB,IAAI,QACJ,OAAO,EAAE,OAAO;QAEhB,oBAAC,QAAQ,IAAC,KAAK,QAAC,QAAQ,UACrB,IAAI,CACI;QAEX,oBAAC,QAAQ,IACP,KAAK,QACL,OAAO,EAAE,GAAG,EAAE;gBACZ,KAAK,CAAC,WAAW,CAAC,OAAO,CAAC,EAAE,CAAC;oBAC3B,kBAAkB;oBAClB;wBACE,IAAI,EAAE,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC;wBAC5B,KAAK;wBACL,QAAQ,EAAE,IAAI;wBACd,OAAO;qBACR;iBACF,CAAC,CAAA;gBACF,OAAO,EAAE,CAAA;YACX,CAAC,mBAGQ;QACX,oBAAC,QAAQ,IACP,KAAK,QACL,OAAO,EAAE,GAAG,EAAE;gBACZ,KAAK,CAAC,eAAe,CAAC,EAAE,CAAC,CAAA;gBACzB,OAAO,EAAE,CAAA;YACX,CAAC,IAEA,WAAW,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,kBAAkB,CAC3C;QACX,oBAAC,QAAQ,IACP,KAAK,QACL,OAAO,EAAE,GAAG,EAAE;gBACZ,KAAK,CAAC,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;gBAC/B,OAAO,EAAE,CAAA;YACX,CAAC,yCAGQ;QACV,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,CAClB,oBAAC,QAAQ,IACP,KAAK,QACL,OAAO,EAAE,GAAG,EAAE;gBACZ,KAAK,CAAC,cAAc,CAAC,SAAS,CAAC,CAAA;gBAC/B,OAAO,EAAE,CAAA;YACX,CAAC,0BAGQ,CACZ,CAAC,CAAC,CAAC,IAAI,CACH,CACR,CAAA;AACH,CAAC,CAAC,CAAA;AAEF,eAAe,QAAQ,CAAA"}
@@ -1,47 +0,0 @@
1
- import React from 'react'
2
-
3
- import { makeStyles } from '@jbrowse/core/util/tss-react'
4
- import { createPortal } from 'react-dom'
5
-
6
- const useStyles = makeStyles()(theme => ({
7
- tooltip: {
8
- position: 'fixed',
9
- pointerEvents: 'none',
10
- zIndex: 10000,
11
- backgroundColor: theme.palette.grey[700],
12
- color: theme.palette.common.white,
13
- padding: '4px 8px',
14
- borderRadius: 4,
15
- fontSize: 12,
16
- whiteSpace: 'nowrap',
17
- maxWidth: 300,
18
- overflow: 'hidden',
19
- textOverflow: 'ellipsis',
20
- },
21
- }))
22
-
23
- /**
24
- * A tooltip beside the pointer, portaled to the body. A panel's scroll
25
- * transform is a containing block, so a fixed tooltip left inside it would
26
- * position against the scrolled content instead of the window.
27
- */
28
- export default function PortalTooltip({
29
- clientX,
30
- clientY,
31
- children,
32
- }: {
33
- clientX: number
34
- clientY: number
35
- children: React.ReactNode
36
- }) {
37
- const { classes } = useStyles()
38
- return createPortal(
39
- <div
40
- className={classes.tooltip}
41
- style={{ left: clientX + 12, top: clientY + 12 }}
42
- >
43
- {children}
44
- </div>,
45
- document.body,
46
- )
47
- }