react-msaview 7.0.0 → 8.1.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 (335) hide show
  1. package/README.md +11 -10
  2. package/bundle/index.js +122 -111
  3. package/bundle/index.js.map +4 -4
  4. package/dist/calculateBlocks.js +5 -3
  5. package/dist/calculateBlocks.js.map +1 -1
  6. package/dist/colorSchemes.js +1 -2
  7. package/dist/colorSchemes.js.map +1 -1
  8. package/dist/columnStats.d.ts +28 -0
  9. package/dist/columnStats.js +34 -0
  10. package/dist/columnStats.js.map +1 -0
  11. package/dist/components/DragHandle.d.ts +3 -2
  12. package/dist/components/DragHandle.js +11 -5
  13. package/dist/components/DragHandle.js.map +1 -1
  14. package/dist/components/Loading.js +2 -8
  15. package/dist/components/Loading.js.map +1 -1
  16. package/dist/components/MSAView.js +17 -6
  17. package/dist/components/MSAView.js.map +1 -1
  18. package/dist/components/MSAViewer.d.ts +64 -7
  19. package/dist/components/MSAViewer.js +128 -23
  20. package/dist/components/MSAViewer.js.map +1 -1
  21. package/dist/components/PortalTooltip.d.ts +11 -0
  22. package/dist/components/PortalTooltip.js +29 -0
  23. package/dist/components/PortalTooltip.js.map +1 -0
  24. package/dist/components/ResizeHandles.js +11 -29
  25. package/dist/components/ResizeHandles.js.map +1 -1
  26. package/dist/components/SequenceTextArea.js +3 -4
  27. package/dist/components/SequenceTextArea.js.map +1 -1
  28. package/dist/components/Track.js +43 -24
  29. package/dist/components/Track.js.map +1 -1
  30. package/dist/components/dialogs/AboutDialog.js +6 -6
  31. package/dist/components/dialogs/AboutDialog.js.map +1 -1
  32. package/dist/components/dialogs/AnnotationFileDialog.js +5 -2
  33. package/dist/components/dialogs/AnnotationFileDialog.js.map +1 -1
  34. package/dist/components/dialogs/ExportSVGDialog.js +6 -4
  35. package/dist/components/dialogs/ExportSVGDialog.js.map +1 -1
  36. package/dist/components/dialogs/FeatureDialog.js +8 -3
  37. package/dist/components/dialogs/FeatureDialog.js.map +1 -1
  38. package/dist/components/dialogs/TrackInfoDialog.js +1 -1
  39. package/dist/components/dialogs/TrackInfoDialog.js.map +1 -1
  40. package/dist/components/header/GappynessSlider.js +6 -11
  41. package/dist/components/header/GappynessSlider.js.map +1 -1
  42. package/dist/components/header/Header.js +4 -1
  43. package/dist/components/header/Header.js.map +1 -1
  44. package/dist/components/header/LoadWarnings.js +6 -23
  45. package/dist/components/header/LoadWarnings.js.map +1 -1
  46. package/dist/components/header/ScrollZoomIcons.d.ts +6 -0
  47. package/dist/components/header/ScrollZoomIcons.js +35 -0
  48. package/dist/components/header/ScrollZoomIcons.js.map +1 -0
  49. package/dist/components/header/TreeSettingsMenu.js +1 -3
  50. package/dist/components/header/TreeSettingsMenu.js.map +1 -1
  51. package/dist/components/header/UnshareableDataWarning.js +2 -4
  52. package/dist/components/header/UnshareableDataWarning.js.map +1 -1
  53. package/dist/components/header/ZoomControls.js +49 -6
  54. package/dist/components/header/ZoomControls.js.map +1 -1
  55. package/dist/components/header/getAnnotationsMenu.js +3 -6
  56. package/dist/components/header/getAnnotationsMenu.js.map +1 -1
  57. package/dist/components/header/settingsMenuItems.js +12 -9
  58. package/dist/components/header/settingsMenuItems.js.map +1 -1
  59. package/dist/components/import/ImportForm.js +1 -1
  60. package/dist/components/import/ImportForm.js.map +1 -1
  61. package/dist/components/import/util.js +1 -2
  62. package/dist/components/import/util.js.map +1 -1
  63. package/dist/components/minimap/Minimap.js +1 -2
  64. package/dist/components/minimap/Minimap.js.map +1 -1
  65. package/dist/components/msa/AnnotationLegend.js +27 -19
  66. package/dist/components/msa/AnnotationLegend.js.map +1 -1
  67. package/dist/components/msa/MSACanvas.js +2 -4
  68. package/dist/components/msa/MSACanvas.js.map +1 -1
  69. package/dist/components/msa/MSACanvasBlock.js +13 -15
  70. package/dist/components/msa/MSACanvasBlock.js.map +1 -1
  71. package/dist/components/msa/MSAMouseoverCanvas.js +2 -1
  72. package/dist/components/msa/MSAMouseoverCanvas.js.map +1 -1
  73. package/dist/components/msa/domainBandCursor.d.ts +7 -11
  74. package/dist/components/msa/domainBandCursor.js +7 -11
  75. package/dist/components/msa/domainBandCursor.js.map +1 -1
  76. package/dist/components/msa/drawFeatureSpans.d.ts +59 -0
  77. package/dist/components/msa/drawFeatureSpans.js +113 -0
  78. package/dist/components/msa/drawFeatureSpans.js.map +1 -0
  79. package/dist/components/msa/legendRows.d.ts +8 -0
  80. package/dist/components/msa/legendRows.js +16 -0
  81. package/dist/components/msa/legendRows.js.map +1 -0
  82. package/dist/components/msa/msaRaster.d.ts +24 -31
  83. package/dist/components/msa/msaRaster.js +30 -39
  84. package/dist/components/msa/msaRaster.js.map +1 -1
  85. package/dist/components/msa/packDomainLanes.d.ts +20 -0
  86. package/dist/components/msa/packDomainLanes.js +33 -0
  87. package/dist/components/msa/packDomainLanes.js.map +1 -0
  88. package/dist/components/msa/renderBoxFeatureCanvasBlock.d.ts +3 -1
  89. package/dist/components/msa/renderBoxFeatureCanvasBlock.js +50 -87
  90. package/dist/components/msa/renderBoxFeatureCanvasBlock.js.map +1 -1
  91. package/dist/components/msa/renderHighlights.d.ts +15 -13
  92. package/dist/components/msa/renderHighlights.js +43 -20
  93. package/dist/components/msa/renderHighlights.js.map +1 -1
  94. package/dist/components/msa/renderMSABlock.js +12 -12
  95. package/dist/components/msa/renderMSABlock.js.map +1 -1
  96. package/dist/components/msa/tileColor.js +4 -7
  97. package/dist/components/msa/tileColor.js.map +1 -1
  98. package/dist/components/msa/useMsaBlockMouse.d.ts +5 -3
  99. package/dist/components/msa/useMsaBlockMouse.js +6 -6
  100. package/dist/components/msa/useMsaBlockMouse.js.map +1 -1
  101. package/dist/components/rowpanels/RowPanelBlock.d.ts +9 -0
  102. package/dist/components/rowpanels/RowPanelBlock.js +86 -0
  103. package/dist/components/rowpanels/RowPanelBlock.js.map +1 -0
  104. package/dist/components/rowpanels/RowPanelHeaders.d.ts +12 -0
  105. package/dist/components/rowpanels/RowPanelHeaders.js +38 -0
  106. package/dist/components/rowpanels/RowPanelHeaders.js.map +1 -0
  107. package/dist/components/{msa/ColumnStats.d.ts → rowpanels/RowPanels.d.ts} +2 -2
  108. package/dist/components/rowpanels/RowPanels.js +32 -0
  109. package/dist/components/rowpanels/RowPanels.js.map +1 -0
  110. package/dist/components/rowpanels/headerLayout.d.ts +1 -0
  111. package/dist/components/rowpanels/headerLayout.js +5 -0
  112. package/dist/components/rowpanels/headerLayout.js.map +1 -0
  113. package/dist/components/rowpanels/renderFeaturePanel.d.ts +21 -0
  114. package/dist/components/rowpanels/renderFeaturePanel.js +37 -0
  115. package/dist/components/rowpanels/renderFeaturePanel.js.map +1 -0
  116. package/dist/components/rowpanels/renderRowPanel.d.ts +23 -0
  117. package/dist/components/rowpanels/renderRowPanel.js +22 -0
  118. package/dist/components/rowpanels/renderRowPanel.js.map +1 -0
  119. package/dist/components/rowpanels/renderStrip.d.ts +20 -0
  120. package/dist/components/rowpanels/renderStrip.js +26 -0
  121. package/dist/components/rowpanels/renderStrip.js.map +1 -0
  122. package/dist/components/tracks/TrackBlocks.js +3 -5
  123. package/dist/components/tracks/TrackBlocks.js.map +1 -1
  124. package/dist/components/tracks/TrackTooltipContent.d.ts +15 -0
  125. package/dist/components/tracks/TrackTooltipContent.js +109 -0
  126. package/dist/components/tracks/TrackTooltipContent.js.map +1 -0
  127. package/dist/components/tracks/drawTracks.d.ts +4 -4
  128. package/dist/components/tracks/drawTracks.js +25 -39
  129. package/dist/components/tracks/drawTracks.js.map +1 -1
  130. package/dist/components/tracks/useTrackHover.d.ts +22 -0
  131. package/dist/components/tracks/useTrackHover.js +30 -0
  132. package/dist/components/tracks/useTrackHover.js.map +1 -0
  133. package/dist/components/tree/CladeLabels.d.ts +12 -0
  134. package/dist/components/tree/CladeLabels.js +48 -0
  135. package/dist/components/tree/CladeLabels.js.map +1 -0
  136. package/dist/components/tree/TreeBranchMenu.js +1 -1
  137. package/dist/components/tree/TreeBranchMenu.js.map +1 -1
  138. package/dist/components/tree/TreeCanvas.js +5 -4
  139. package/dist/components/tree/TreeCanvas.js.map +1 -1
  140. package/dist/components/tree/TreeCanvasBlock.js +4 -21
  141. package/dist/components/tree/TreeCanvasBlock.js.map +1 -1
  142. package/dist/components/tree/TreeNodeMenu.js +1 -1
  143. package/dist/components/tree/TreeNodeMenu.js.map +1 -1
  144. package/dist/components/tree/TreeOverview.d.ts +14 -0
  145. package/dist/components/tree/TreeOverview.js +75 -0
  146. package/dist/components/tree/TreeOverview.js.map +1 -0
  147. package/dist/components/tree/TreeRuler.js +9 -13
  148. package/dist/components/tree/TreeRuler.js.map +1 -1
  149. package/dist/components/tree/cladeBrackets.d.ts +25 -0
  150. package/dist/components/tree/cladeBrackets.js +51 -0
  151. package/dist/components/tree/cladeBrackets.js.map +1 -0
  152. package/dist/components/tree/dialogs/TreeNodeInfoDialog.js +2 -2
  153. package/dist/components/tree/dialogs/TreeNodeInfoDialog.js.map +1 -1
  154. package/dist/components/tree/renderTreeCanvas.js +160 -25
  155. package/dist/components/tree/renderTreeCanvas.js.map +1 -1
  156. package/dist/components/tree/renderTreeOverview.d.ts +52 -0
  157. package/dist/components/tree/renderTreeOverview.js +138 -0
  158. package/dist/components/tree/renderTreeOverview.js.map +1 -0
  159. package/dist/components/tree/useTreeHover.js +2 -4
  160. package/dist/components/tree/useTreeHover.js.map +1 -1
  161. package/dist/constants.d.ts +16 -0
  162. package/dist/constants.js +52 -22
  163. package/dist/constants.js.map +1 -1
  164. package/dist/createPaletteMap.d.ts +7 -6
  165. package/dist/createPaletteMap.js +10 -9
  166. package/dist/createPaletteMap.js.map +1 -1
  167. package/dist/element.d.ts +13 -0
  168. package/dist/element.js +269 -0
  169. package/dist/element.js.map +1 -0
  170. package/dist/featureFields.d.ts +11 -0
  171. package/dist/featureFields.js +17 -0
  172. package/dist/featureFields.js.map +1 -0
  173. package/dist/flatToTree.js +1 -2
  174. package/dist/flatToTree.js.map +1 -1
  175. package/dist/ggplotPalettes.d.ts +10 -0
  176. package/dist/ggplotPalettes.js +58 -0
  177. package/dist/ggplotPalettes.js.map +1 -1
  178. package/dist/hierarchy.d.ts +27 -0
  179. package/dist/hierarchy.js +66 -9
  180. package/dist/hierarchy.js.map +1 -1
  181. package/dist/index.d.ts +9 -1
  182. package/dist/index.js +5 -3
  183. package/dist/index.js.map +1 -1
  184. package/dist/model/DataModel.js +2 -5
  185. package/dist/model/DataModel.js.map +1 -1
  186. package/dist/model/msaModel.d.ts +3 -2
  187. package/dist/model/msaModel.js +3 -2
  188. package/dist/model/msaModel.js.map +1 -1
  189. package/dist/model/treeModel.d.ts +30 -0
  190. package/dist/model/treeModel.js +37 -1
  191. package/dist/model/treeModel.js.map +1 -1
  192. package/dist/model.d.ts +549 -294
  193. package/dist/model.js +1412 -621
  194. package/dist/model.js.map +1 -1
  195. package/dist/mount.d.ts +11 -0
  196. package/dist/mount.js +22 -0
  197. package/dist/mount.js.map +1 -0
  198. package/dist/neighborJoining.js +8 -32
  199. package/dist/neighborJoining.js.map +1 -1
  200. package/dist/renderToStaticMarkup.d.ts +10 -19
  201. package/dist/renderToStaticMarkup.js +13 -24
  202. package/dist/renderToStaticMarkup.js.map +1 -1
  203. package/dist/renderToSvg.js +180 -56
  204. package/dist/renderToSvg.js.map +1 -1
  205. package/dist/rowCoordinateCalculations.d.ts +4 -6
  206. package/dist/rowCoordinateCalculations.js +4 -6
  207. package/dist/rowCoordinateCalculations.js.map +1 -1
  208. package/dist/scales.d.ts +22 -0
  209. package/dist/scales.js +30 -0
  210. package/dist/scales.js.map +1 -0
  211. package/dist/seqPosToGlobalCol.d.ts +2 -3
  212. package/dist/seqPosToGlobalCol.js +2 -3
  213. package/dist/seqPosToGlobalCol.js.map +1 -1
  214. package/dist/sequenceLogo.d.ts +6 -11
  215. package/dist/sequenceLogo.js +6 -11
  216. package/dist/sequenceLogo.js.map +1 -1
  217. package/dist/stripDefault.d.ts +7 -14
  218. package/dist/stripDefault.js +7 -14
  219. package/dist/stripDefault.js.map +1 -1
  220. package/dist/svgTestUtil.d.ts +186 -70
  221. package/dist/types.d.ts +221 -21
  222. package/dist/umd.d.ts +2 -0
  223. package/dist/umd.js +4 -2
  224. package/dist/umd.js.map +1 -1
  225. package/dist/useCanvasAutorun.d.ts +5 -10
  226. package/dist/useCanvasAutorun.js +5 -10
  227. package/dist/useCanvasAutorun.js.map +1 -1
  228. package/dist/useMsaSvgFigure.d.ts +20 -0
  229. package/dist/useMsaSvgFigure.js +46 -0
  230. package/dist/useMsaSvgFigure.js.map +1 -0
  231. package/dist/useWheelScroll.js +0 -4
  232. package/dist/useWheelScroll.js.map +1 -1
  233. package/dist/util.d.ts +5 -6
  234. package/dist/util.js +13 -6
  235. package/dist/util.js.map +1 -1
  236. package/dist/version.d.ts +1 -1
  237. package/dist/version.js +1 -1
  238. package/package.json +19 -12
  239. package/src/calculateBlocks.ts +5 -3
  240. package/src/colorSchemes.ts +1 -2
  241. package/src/columnStats.ts +58 -0
  242. package/src/components/DragHandle.tsx +18 -7
  243. package/src/components/Loading.tsx +4 -36
  244. package/src/components/MSAView.tsx +19 -5
  245. package/src/components/MSAViewer.tsx +240 -28
  246. package/src/components/PortalTooltip.tsx +47 -0
  247. package/src/components/ResizeHandles.tsx +17 -32
  248. package/src/components/SequenceTextArea.tsx +3 -4
  249. package/src/components/Track.tsx +57 -19
  250. package/src/components/dialogs/AboutDialog.tsx +35 -6
  251. package/src/components/dialogs/AnnotationFileDialog.tsx +10 -1
  252. package/src/components/dialogs/ExportSVGDialog.tsx +8 -3
  253. package/src/components/dialogs/FeatureDialog.tsx +11 -3
  254. package/src/components/dialogs/TrackInfoDialog.tsx +1 -1
  255. package/src/components/header/GappynessSlider.tsx +6 -11
  256. package/src/components/header/Header.tsx +7 -1
  257. package/src/components/header/LoadWarnings.tsx +11 -25
  258. package/src/components/header/ScrollZoomIcons.tsx +52 -0
  259. package/src/components/header/TreeSettingsMenu.tsx +1 -3
  260. package/src/components/header/UnshareableDataWarning.tsx +2 -4
  261. package/src/components/header/ZoomControls.tsx +74 -14
  262. package/src/components/header/getAnnotationsMenu.ts +3 -6
  263. package/src/components/header/settingsMenuItems.ts +13 -8
  264. package/src/components/import/ImportForm.tsx +3 -5
  265. package/src/components/import/ImportFormExamples.tsx +2 -2
  266. package/src/components/import/util.ts +1 -2
  267. package/src/components/minimap/Minimap.tsx +1 -2
  268. package/src/components/msa/AnnotationLegend.tsx +48 -38
  269. package/src/components/msa/MSACanvas.tsx +3 -8
  270. package/src/components/msa/MSACanvasBlock.tsx +13 -16
  271. package/src/components/msa/MSAMouseoverCanvas.tsx +2 -1
  272. package/src/components/msa/domainBandCursor.ts +7 -11
  273. package/src/components/msa/drawFeatureSpans.ts +205 -0
  274. package/src/components/msa/legendRows.ts +24 -0
  275. package/src/components/msa/msaRaster.ts +30 -39
  276. package/src/components/msa/packDomainLanes.ts +36 -0
  277. package/src/components/msa/renderBoxFeatureCanvasBlock.ts +67 -123
  278. package/src/components/msa/renderHighlights.ts +53 -20
  279. package/src/components/msa/renderMSABlock.ts +14 -11
  280. package/src/components/msa/tileColor.ts +4 -7
  281. package/src/components/msa/useMsaBlockMouse.ts +6 -6
  282. package/src/components/rowpanels/RowPanelBlock.tsx +139 -0
  283. package/src/components/rowpanels/RowPanelHeaders.tsx +57 -0
  284. package/src/components/rowpanels/RowPanels.tsx +77 -0
  285. package/src/components/rowpanels/headerLayout.ts +5 -0
  286. package/src/components/rowpanels/renderFeaturePanel.ts +66 -0
  287. package/src/components/rowpanels/renderRowPanel.ts +41 -0
  288. package/src/components/rowpanels/renderStrip.ts +46 -0
  289. package/src/components/tracks/TrackBlocks.tsx +3 -5
  290. package/src/components/tracks/TrackTooltipContent.tsx +156 -0
  291. package/src/components/tracks/drawTracks.ts +25 -50
  292. package/src/components/tracks/useTrackHover.ts +42 -0
  293. package/src/components/tree/CladeLabels.tsx +77 -0
  294. package/src/components/tree/TreeBranchMenu.tsx +1 -2
  295. package/src/components/tree/TreeCanvas.tsx +2 -3
  296. package/src/components/tree/TreeCanvasBlock.tsx +8 -32
  297. package/src/components/tree/TreeNodeMenu.tsx +1 -2
  298. package/src/components/tree/TreeOverview.tsx +106 -0
  299. package/src/components/tree/TreeRuler.tsx +11 -22
  300. package/src/components/tree/cladeBrackets.ts +73 -0
  301. package/src/components/tree/dialogs/TreeNodeInfoDialog.tsx +2 -2
  302. package/src/components/tree/renderTreeCanvas.ts +242 -21
  303. package/src/components/tree/renderTreeOverview.ts +212 -0
  304. package/src/components/tree/useTreeHover.ts +2 -4
  305. package/src/constants.ts +61 -22
  306. package/src/createPaletteMap.ts +10 -9
  307. package/src/element.ts +321 -0
  308. package/src/featureFields.ts +19 -0
  309. package/src/flatToTree.ts +1 -2
  310. package/src/ggplotPalettes.ts +60 -0
  311. package/src/hierarchy.ts +77 -9
  312. package/src/index.ts +31 -7
  313. package/src/model/DataModel.ts +2 -5
  314. package/src/model/msaModel.ts +3 -2
  315. package/src/model/treeModel.ts +42 -0
  316. package/src/model.ts +1677 -643
  317. package/src/mount.tsx +32 -0
  318. package/src/neighborJoining.ts +8 -32
  319. package/src/renderToStaticMarkup.ts +13 -24
  320. package/src/renderToSvg.tsx +335 -68
  321. package/src/rowCoordinateCalculations.ts +4 -6
  322. package/src/scales.ts +47 -0
  323. package/src/seqPosToGlobalCol.ts +2 -3
  324. package/src/sequenceLogo.ts +6 -11
  325. package/src/stripDefault.ts +7 -14
  326. package/src/types.ts +260 -45
  327. package/src/umd.ts +4 -2
  328. package/src/useCanvasAutorun.ts +5 -10
  329. package/src/useMsaSvgFigure.ts +72 -0
  330. package/src/useWheelScroll.ts +0 -4
  331. package/src/util.ts +15 -6
  332. package/src/version.ts +1 -1
  333. package/dist/components/msa/ColumnStats.js +0 -42
  334. package/dist/components/msa/ColumnStats.js.map +0 -1
  335. package/src/components/msa/ColumnStats.tsx +0 -54
@@ -0,0 +1,77 @@
1
+ import React, { useCallback, useRef } from 'react'
2
+
3
+ import { observer } from 'mobx-react'
4
+
5
+ import { useWheelScroll } from '../../useWheelScroll.ts'
6
+ import RowPanelBlock from './RowPanelBlock.tsx'
7
+
8
+ import type { MsaViewModel } from '../../model.ts'
9
+ import type { ResolvedRowPanel } from '../../types.ts'
10
+
11
+ const RowPanel = observer(function ({
12
+ model,
13
+ panel,
14
+ }: {
15
+ model: MsaViewModel
16
+ panel: ResolvedRowPanel
17
+ }) {
18
+ const ref = useRef<HTMLDivElement>(null)
19
+ const { height, blocksY, scrollY } = model
20
+ const onScrollY = useCallback(
21
+ (d: number) => {
22
+ model.doScrollY(d)
23
+ },
24
+ [model],
25
+ )
26
+ const { onMouseDown, onMouseUp } = useWheelScroll({ ref, onScrollY })
27
+
28
+ return (
29
+ <div
30
+ ref={ref}
31
+ data-testid={`rowpanel_${panel.id}`}
32
+ onMouseDown={onMouseDown}
33
+ onMouseUp={onMouseUp}
34
+ style={{
35
+ height,
36
+ width: panel.width,
37
+ flexShrink: 0,
38
+ overflow: 'hidden',
39
+ position: 'relative',
40
+ }}
41
+ >
42
+ {/* one transform for the whole block set, matching TreeCanvas: the
43
+ strips scroll in lockstep with the tree and the alignment */}
44
+ <div
45
+ style={{
46
+ position: 'absolute',
47
+ top: 0,
48
+ left: 0,
49
+ transform: `translateY(${scrollY}px)`,
50
+ willChange: 'transform',
51
+ }}
52
+ >
53
+ {blocksY.map(block => (
54
+ <RowPanelBlock
55
+ key={block}
56
+ model={model}
57
+ panel={panel}
58
+ offsetY={block}
59
+ />
60
+ ))}
61
+ </div>
62
+ </div>
63
+ )
64
+ })
65
+
66
+ const RowPanels = observer(function ({ model }: { model: MsaViewModel }) {
67
+ const { resolvedRowPanels } = model
68
+ return resolvedRowPanels.length > 0 ? (
69
+ <div style={{ display: 'flex', flexShrink: 0 }}>
70
+ {resolvedRowPanels.map(panel => (
71
+ <RowPanel key={panel.id} model={model} panel={panel} />
72
+ ))}
73
+ </div>
74
+ ) : null
75
+ })
76
+
77
+ export default RowPanels
@@ -0,0 +1,5 @@
1
+ import { rowPanelHeaderHeight } from '../../constants.ts'
2
+
3
+ export function headerRunsAcross(panelWidth: number) {
4
+ return panelWidth > rowPanelHeaderHeight
5
+ }
@@ -0,0 +1,66 @@
1
+ import { contrastTextFn } from '../../util.ts'
2
+ import { getVisibleLeaves } from '../getVisibleLeaves.ts'
3
+ import { drawFeatureSpans, spanRows } from '../msa/drawFeatureSpans.ts'
4
+
5
+ import type { MsaViewModel } from '../../model.ts'
6
+ import type { ResolvedFeaturePanel, RowPanelSpan } from '../../types.ts'
7
+ import type { RenderCtx } from '../renderCtx.ts'
8
+ import type { Theme } from '@mui/material'
9
+
10
+ // a span keeps a pixel clear above and below it, so stacked lanes and adjacent
11
+ // rows read apart
12
+ const rowPadding = 2
13
+
14
+ /**
15
+ * The spans of a `features` panel, drawn with the mark the alignment overlay
16
+ * draws: an arrow where the feature carries a strand, a box otherwise, labeled
17
+ * where the text fits. The x mapping is the panel's own, resolved in
18
+ * `resolvedRowPanels`; a row needing several lanes divides its height between
19
+ * them.
20
+ */
21
+ export function renderFeaturePanel({
22
+ ctx,
23
+ model,
24
+ theme,
25
+ panel,
26
+ x,
27
+ offsetY,
28
+ blockSizeYOverride,
29
+ highResScaleFactorOverride,
30
+ }: {
31
+ ctx: RenderCtx
32
+ model: MsaViewModel
33
+ theme: Theme
34
+ panel: ResolvedFeaturePanel
35
+ x: number
36
+ offsetY: number
37
+ blockSizeYOverride?: number
38
+ highResScaleFactorOverride?: number
39
+ }) {
40
+ const { rowHeight, blockSize, highResScaleFactor } = model
41
+ const by = blockSizeYOverride ?? blockSize
42
+ const k = highResScaleFactorOverride ?? highResScaleFactor
43
+ ctx.resetTransform()
44
+ ctx.scale(k, k)
45
+ ctx.translate(x, rowHeight / 2 - offsetY)
46
+
47
+ drawFeatureSpans<RowPanelSpan>({
48
+ ctx,
49
+ rows: spanRows(
50
+ getVisibleLeaves({ model, offsetY, blockSizeY: by }),
51
+ panel.spans,
52
+ ),
53
+ layout: {
54
+ height: laneCount => Math.max(1, rowHeight / laneCount - rowPadding),
55
+ top: (y, lane, h) =>
56
+ y - rowHeight + lane * (h + rowPadding) + rowPadding / 2,
57
+ headLength: h => h,
58
+ },
59
+ xOf: span => [span.xStart, span.xEnd],
60
+ colors: panel.colors,
61
+ labelOf: span => panel.labels?.get(span.annotation),
62
+ contrastText: contrastTextFn(theme),
63
+ xMin: 0,
64
+ xMax: panel.width,
65
+ })
66
+ }
@@ -0,0 +1,41 @@
1
+ import { renderFeaturePanel } from './renderFeaturePanel.ts'
2
+ import { renderStrip } from './renderStrip.ts'
3
+
4
+ import type { MsaViewModel } from '../../model.ts'
5
+ import type { ResolvedRowPanel } from '../../types.ts'
6
+ import type { RenderCtx } from '../renderCtx.ts'
7
+ import type { Theme } from '@mui/material'
8
+
9
+ interface PanelArgs {
10
+ ctx: RenderCtx
11
+ model: MsaViewModel
12
+ theme: Theme
13
+ panel: ResolvedRowPanel
14
+ x: number
15
+ offsetY: number
16
+ blockSizeYOverride?: number
17
+ highResScaleFactorOverride?: number
18
+ }
19
+
20
+ /**
21
+ * One row panel, by kind. `x` is the panel's left edge in the context: the
22
+ * live view gives each panel a canvas of its own and passes 0, and the export
23
+ * packs every panel into one context and passes the panel's own offset.
24
+ */
25
+ export function renderRowPanel({ panel, ...rest }: PanelArgs) {
26
+ if (panel.kind === 'features') {
27
+ renderFeaturePanel({ panel, ...rest })
28
+ } else {
29
+ renderStrip({ panel, ...rest })
30
+ }
31
+ }
32
+
33
+ /** every row panel side by side in one context, for the SVG export */
34
+ export function renderRowPanels({
35
+ model,
36
+ ...rest
37
+ }: Omit<PanelArgs, 'panel' | 'x'>) {
38
+ for (const panel of model.resolvedRowPanels) {
39
+ renderRowPanel({ model, panel, x: panel.offsetX, ...rest })
40
+ }
41
+ }
@@ -0,0 +1,46 @@
1
+ import { getVisibleLeaves } from '../getVisibleLeaves.ts'
2
+
3
+ import type { MsaViewModel } from '../../model.ts'
4
+ import type { ResolvedStripPanel } from '../../types.ts'
5
+ import type { RenderCtx } from '../renderCtx.ts'
6
+
7
+ /**
8
+ * One cell per row of a `strip` panel, in the color its field takes through
9
+ * the panel's scale. A row the table has no value for leaves the cell empty.
10
+ *
11
+ * `x` is the panel's left edge in the context: the live view gives each panel
12
+ * a canvas of its own and passes 0, and the export packs every panel into one
13
+ * context and passes the panel's own offset.
14
+ */
15
+ export function renderStrip({
16
+ ctx,
17
+ model,
18
+ panel,
19
+ x,
20
+ offsetY,
21
+ blockSizeYOverride,
22
+ highResScaleFactorOverride,
23
+ }: {
24
+ ctx: RenderCtx
25
+ model: MsaViewModel
26
+ panel: ResolvedStripPanel
27
+ x: number
28
+ offsetY: number
29
+ blockSizeYOverride?: number
30
+ highResScaleFactorOverride?: number
31
+ }) {
32
+ const { rowHeight, blockSize, highResScaleFactor } = model
33
+ const by = blockSizeYOverride ?? blockSize
34
+ ctx.resetTransform()
35
+ const k = highResScaleFactorOverride ?? highResScaleFactor
36
+ ctx.scale(k, k)
37
+ ctx.translate(x, -offsetY)
38
+ for (const leaf of getVisibleLeaves({ model, offsetY, blockSizeY: by })) {
39
+ const color = panel.colors.get(leaf.data.name)
40
+ if (color) {
41
+ ctx.fillStyle = color
42
+ // leaf.x is the row's center, at rowHeight*(index+0.5)
43
+ ctx.fillRect(0, leaf.x! - rowHeight / 2, panel.width, rowHeight)
44
+ }
45
+ }
46
+ }
@@ -64,11 +64,9 @@ const TrackBlocks = observer(function ({
64
64
  model: MsaViewModel
65
65
  track: BasicTrack
66
66
  }) {
67
- // msaCanvasWidth, not msaAreaWidth: a track row is a legend for the columns
68
- // directly below it, so it has to stop where the alignment canvas does rather
69
- // than running on another 20px under the vertical scrollbar -- otherwise the
70
- // last columns appear in the tracks but not in the alignment, and the hovered
71
- // -column indicator (sized to the canvas) stops short of the track it crosses
67
+ // msaCanvasWidth, not msaAreaWidth: tracks stop where the alignment canvas
68
+ // does, not 20px further under the vertical scrollbar, so their columns and
69
+ // the hover indicator line up with the alignment
72
70
  const { blocksX, msaCanvasWidth, scrollX } = model
73
71
  const { kind, height, data, arcs } = track.model
74
72
 
@@ -0,0 +1,156 @@
1
+ import React from 'react'
2
+
3
+ import { observer } from 'mobx-react'
4
+
5
+ import { columnLogoStack } from '../../sequenceLogo.ts'
6
+ import { barTrackValues } from './drawTracks.ts'
7
+
8
+ import type { MsaViewModel } from '../../model.ts'
9
+ import type { BasicTrack } from '../../types.ts'
10
+
11
+ const pct = (n: number) => `${Math.round(n * 100)}%`
12
+
13
+ const MAX_PARTNERS = 8
14
+
15
+ function ColumnLabel({ model, col }: { model: MsaViewModel; col: number }) {
16
+ const { relativeTo } = model
17
+ const pos = relativeTo
18
+ ? model.visibleColToSeqPosOneBased(relativeTo, col)
19
+ : undefined
20
+ return (
21
+ <b>
22
+ Column {col + 1}
23
+ {pos === undefined ? null : ` (${relativeTo}:${pos})`}
24
+ </b>
25
+ )
26
+ }
27
+
28
+ function BarTooltip({
29
+ model,
30
+ track,
31
+ col,
32
+ }: {
33
+ model: MsaViewModel
34
+ track: BasicTrack
35
+ col: number
36
+ }) {
37
+ const { id, name } = track.model
38
+ const value = barTrackValues(model, id)[col]
39
+ return value === undefined ? null : (
40
+ <div>
41
+ <ColumnLabel model={model} col={col} />
42
+ <br />
43
+ {name}: {value.toFixed(2)}
44
+ </div>
45
+ )
46
+ }
47
+
48
+ function LogoTooltip({ model, col }: { model: MsaViewModel; col: number }) {
49
+ const stats = model.columnStatsAt(col)
50
+ if (!stats) {
51
+ return null
52
+ }
53
+ const { total, consensusLetter, consensusFraction, gapFraction } = stats
54
+ const stack = columnLogoStack(model.colStats, col, model.alphabetMaxBits)
55
+ const bits = stack.reduce((a, b) => a + b.bits, 0)
56
+ return (
57
+ <div>
58
+ <ColumnLabel model={model} col={col} /> ({total} seqs)
59
+ <br />
60
+ Information: {bits.toFixed(2)} bits
61
+ <br />
62
+ Consensus: {consensusLetter || '-'} ({pct(consensusFraction)})
63
+ <br />
64
+ Gaps: {pct(gapFraction)}
65
+ <br />
66
+ {stats.distribution.slice(0, 6).map(([letter, count]) => (
67
+ <span key={letter} style={{ marginRight: 8 }}>
68
+ {letter} {pct(count / total)}
69
+ </span>
70
+ ))}
71
+ </div>
72
+ )
73
+ }
74
+
75
+ function TextTooltip({
76
+ model,
77
+ track,
78
+ col,
79
+ }: {
80
+ model: MsaViewModel
81
+ track: BasicTrack
82
+ col: number
83
+ }) {
84
+ const { name, data } = track.model
85
+ const letter = data?.[col]
86
+ return letter === undefined ? null : (
87
+ <div>
88
+ <ColumnLabel model={model} col={col} />
89
+ <br />
90
+ {name}: {letter === ' ' ? 'blank' : letter}
91
+ </div>
92
+ )
93
+ }
94
+
95
+ function ArcTooltip({
96
+ model,
97
+ track,
98
+ col,
99
+ }: {
100
+ model: MsaViewModel
101
+ track: BasicTrack
102
+ col: number
103
+ }) {
104
+ const { name, arcs } = track.model
105
+ const partners = (arcs ?? [])
106
+ .filter(arc => arc.start === col || arc.end === col)
107
+ .map(arc => (arc.start === col ? arc.end : arc.start) + 1)
108
+ return partners.length === 0 ? null : (
109
+ <div>
110
+ <ColumnLabel model={model} col={col} />
111
+ <br />
112
+ {name}: pairs with {partners.slice(0, MAX_PARTNERS).join(', ')}
113
+ {partners.length > MAX_PARTNERS ? '...' : null}
114
+ </div>
115
+ )
116
+ }
117
+
118
+ /**
119
+ * One track's reading at the hovered column: a bar track's value, the logo's
120
+ * information content and composition, the columns an arc joins, the letter on a
121
+ * text track. `drawTrackBlock` dispatches on the same `kind`, so a new kind
122
+ * needs a case in both.
123
+ */
124
+ const TrackTooltipContent = observer(function ({
125
+ model,
126
+ track,
127
+ col,
128
+ }: {
129
+ model: MsaViewModel
130
+ track: BasicTrack
131
+ col: number
132
+ }) {
133
+ switch (track.model.kind) {
134
+ case 'bar': {
135
+ return <BarTooltip model={model} track={track} col={col} />
136
+ }
137
+ case 'logo': {
138
+ return <LogoTooltip model={model} col={col} />
139
+ }
140
+ case 'text': {
141
+ return <TextTooltip model={model} track={track} col={col} />
142
+ }
143
+ case 'arc': {
144
+ return <ArcTooltip model={model} track={track} col={col} />
145
+ }
146
+ case 'ruler': {
147
+ return (
148
+ <div>
149
+ <ColumnLabel model={model} col={col} />
150
+ </div>
151
+ )
152
+ }
153
+ }
154
+ })
155
+
156
+ export default TrackTooltipContent
@@ -41,8 +41,8 @@ export function drawConservationBars({
41
41
  }
42
42
  }
43
43
 
44
- // the bar-track values live on model getters (kept off the plain track object so
45
- // the live canvas autorun stays reactive); select the right one by track id
44
+ // bar-track values are model getters, off the plain track object, so the canvas
45
+ // autorun stays reactive
46
46
  export function barTrackValues(model: MsaViewModel, trackId: string) {
47
47
  const supplied = model.columnTrackContent.get(trackId)?.values
48
48
  if (supplied) {
@@ -53,10 +53,8 @@ export function barTrackValues(model: MsaViewModel, trackId: string) {
53
53
  : model.conservation
54
54
  }
55
55
 
56
- // An arc's apex scales with how far it reaches, so a short helix stays shallow
57
- // and a long-range pair sweeps the full track height. The scale is sqrt rather
58
- // than linear because a contact map's spans are heavily skewed towards short
59
- // ones, which linear scaling flattens into a smear along the baseline.
56
+ // An arc's apex scales with the square root of its span. Contact-map spans skew
57
+ // short, and a linear scale flattens them along the baseline.
60
58
  export function drawArcs({
61
59
  ctx,
62
60
  arcs,
@@ -145,17 +143,13 @@ export function drawTextTrackContent({
145
143
  ctx.fillRect(x, 0, colWidth, rowHeight)
146
144
  }
147
145
  if (drawLetters) {
148
- // a letter on a colored tile needs that tile's contrast color; otherwise
149
- // it sits on the plain background and takes the theme's text color
150
146
  ctx.fillStyle = contrastText(fill)
151
147
  ctx.fillText(letter, x + colWidth / 2, rowHeight / 2)
152
148
  }
153
149
  }
154
150
  }
155
151
 
156
- // Column numbers every `step` columns, with a tick under each. The step is the
157
- // smallest 1/2/5 x 10^n that leaves room for a label, so a zoom changes how many
158
- // numbers appear rather than crowding them.
152
+ // the smallest 1/2/5 x 10^n column step that leaves room for a label
159
153
  export function rulerStep(colWidth: number, minPixels = 55) {
160
154
  let step = 1
161
155
  const mantissas = [1, 2, 5]
@@ -215,17 +209,11 @@ export function drawColumnRuler({
215
209
  }
216
210
  }
217
211
 
218
- // Cap height as a fraction of the font size. Every logo letter is scaled from
219
- // one reference font size to the height its frequency earns, and that scale is
220
- // only right if we know how tall the glyph actually draws. Measuring per letter
221
- // would be exact but `measureText` reports no vertical box in the SVG export
222
- // backend, so the ratio is a constant: the cap height of the sans-serif faces
223
- // both backends use, within a percent or two. It only has to be consistent --
224
- // the same ratio for every letter is what makes the slices sum to the stack
225
- // height instead of drifting apart.
212
+ // Cap height as a fraction of the font size, within a percent or two for the
213
+ // sans-serif faces both backends use. `measureText` reports no vertical box in
214
+ // the SVG export backend, so one constant ratio scales every logo letter, and
215
+ // the slices sum to the stack height.
226
216
  const CAP_HEIGHT_RATIO = 0.72
227
- // letters below this many pixels are noise, and a stack that thin reads better
228
- // as the bar chart it approximates
229
217
  const MIN_LETTER_PX = 3
230
218
 
231
219
  export function drawSequenceLogo({
@@ -256,8 +244,8 @@ export function drawSequenceLogo({
256
244
  })
257
245
  const end = Math.min(xEnd, colStats.numColumns)
258
246
 
259
- // one reference size for every glyph; each letter's own transform takes it
260
- // from here to the height it earned, so the font is set once for the block
247
+ // one reference font size for the block; each letter's transform scales it to
248
+ // a height proportional to its frequency
261
249
  const referenceFontSize = trackHeight
262
250
  setFontSize(ctx, referenceFontSize)
263
251
  ctx.textAlign = 'left'
@@ -270,9 +258,7 @@ export function drawSequenceLogo({
270
258
  continue
271
259
  }
272
260
  const x = col * colWidth
273
- // stack up from the baseline: the array is ascending, so the tallest letter
274
- // lands on top, which is the convention that makes a logo readable at a
275
- // glance
261
+ // the stack is ascending, so the tallest letter lands on top
276
262
  let bottom = trackHeight
277
263
  for (const { letter, bits } of stack) {
278
264
  const h = (bits / maxBits) * trackHeight
@@ -300,9 +286,9 @@ export function drawSequenceLogo({
300
286
 
301
287
  /**
302
288
  * Draw one track's content into a block of the alignment's column space, at
303
- * `offsetY` within the ctx. Owns the transform, so both callers -- the live
304
- * canvas blocks and the SVG export -- draw into the same coordinate space, and
305
- * adding a track kind means a `kind`, a draw function above, and a case here.
289
+ * `offsetY` within the ctx. Owns the transform, so the live canvas blocks and
290
+ * the SVG export draw into the same coordinate space. A new track kind needs a
291
+ * `kind`, a draw function above, and a case here.
306
292
  */
307
293
  export function drawTrackBlock({
308
294
  model,
@@ -323,18 +309,9 @@ export function drawTrackBlock({
323
309
  blockSizeXOverride?: number
324
310
  highResScaleFactorOverride?: number
325
311
  }) {
326
- const {
327
- blockSize,
328
- bgColor,
329
- colWidth,
330
- colStats,
331
- colorScheme: modelColorScheme,
332
- fontSize,
333
- rowHeight,
334
- showMsaLetters,
335
- alphabetMaxBits,
336
- highResScaleFactor,
337
- } = model
312
+ // an autorun tracks every model read here, so each kind reads only what it
313
+ // draws and a vertical zoom leaves the bar and arc canvases alone
314
+ const { blockSize, colWidth, highResScaleFactor } = model
338
315
  const {
339
316
  id,
340
317
  kind,
@@ -381,9 +358,9 @@ export function drawTrackBlock({
381
358
  case 'logo': {
382
359
  drawSequenceLogo({
383
360
  ctx,
384
- colStats,
385
- colorScheme: modelColorScheme,
386
- maxBits: alphabetMaxBits,
361
+ colStats: model.colStats,
362
+ colorScheme: model.colorScheme,
363
+ maxBits: model.alphabetMaxBits,
387
364
  textColor,
388
365
  colWidth,
389
366
  trackHeight,
@@ -397,8 +374,7 @@ export function drawTrackBlock({
397
374
  drawColumnRuler({
398
375
  ctx,
399
376
  numColumns,
400
- // numbered by the reference row's own residues when the alignment is
401
- // drawn relative to one, which is what the rest of the view counts in
377
+ // with relativeTo set, number by the reference row's residues
402
378
  label: relativeTo
403
379
  ? col => {
404
380
  const pos = model.visibleColToSeqPosOneBased(relativeTo, col)
@@ -414,17 +390,16 @@ export function drawTrackBlock({
414
390
  break
415
391
  }
416
392
  case 'text': {
393
+ const { bgColor, fontSize, rowHeight, showMsaLetters } = model
417
394
  ctx.textAlign = 'center'
418
395
  ctx.textBaseline = 'middle'
419
396
  setFontSize(ctx, fontSize)
420
397
  drawTextTrackContent({
421
398
  ctx,
422
399
  data,
423
- colorScheme: customColorScheme ?? modelColorScheme,
400
+ colorScheme: customColorScheme ?? model.colorScheme,
424
401
  contrastText: contrastTextFn(theme),
425
402
  bgColor,
426
- // a text track is one alignment row tall, so it shows letters exactly
427
- // when the alignment does
428
403
  drawLetters: showMsaLetters,
429
404
  colWidth,
430
405
  rowHeight,
@@ -440,7 +415,7 @@ export function drawTrackBlock({
440
415
 
441
416
  /**
442
417
  * Every turned-on track stacked top to bottom, for the SVG export. The live
443
- * view draws the same content one canvas block at a time -- see TrackBlocks.
418
+ * view draws the same content one canvas block at a time in TrackBlocks.
444
419
  */
445
420
  export function renderAllTracks({
446
421
  model,
@@ -0,0 +1,42 @@
1
+ import { useState } from 'react'
2
+
3
+ import type { MsaViewModel } from '../../model.ts'
4
+ import type React from 'react'
5
+
6
+ /**
7
+ * Translates pointer events over a track's row into the hovered column and a
8
+ * tooltip anchor. The track shares the alignment's columns, so hovering it sets
9
+ * the model's mouse column too and the alignment's hover band follows along.
10
+ *
11
+ * `tooltip` off skips the anchor, so a track whose tooltip is turned off still
12
+ * drives the hover band without re-rendering on every mouse move.
13
+ */
14
+ export function useTrackHover({
15
+ model,
16
+ tooltip,
17
+ }: {
18
+ model: MsaViewModel
19
+ tooltip: boolean
20
+ }) {
21
+ const [hover, setHover] = useState<{ col: number; x: number; y: number }>()
22
+
23
+ // the blocks are laid out in column space and scrolled by a transform on
24
+ // their container, so the pointer's column comes off the untransformed row
25
+ function onMouseMove(event: React.MouseEvent, el: HTMLElement) {
26
+ const { colWidth, scrollX, numColumns } = model
27
+ const { left } = el.getBoundingClientRect()
28
+ const col = Math.floor((event.clientX - left - scrollX) / colWidth)
29
+ const hit = col >= 0 && col < numColumns
30
+ model.setMousePos(hit ? col : undefined, undefined)
31
+ if (tooltip) {
32
+ setHover(hit ? { col, x: event.clientX, y: event.clientY } : undefined)
33
+ }
34
+ }
35
+
36
+ function onMouseLeave() {
37
+ setHover(undefined)
38
+ model.setMousePos(undefined, undefined)
39
+ }
40
+
41
+ return { hover: tooltip ? hover : undefined, onMouseMove, onMouseLeave }
42
+ }