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
@@ -5,6 +5,9 @@ import ExpandMore from '@mui/icons-material/ExpandMore'
5
5
  import { IconButton, Paper, Typography } from '@mui/material'
6
6
  import { observer } from 'mobx-react'
7
7
 
8
+ import { legendZIndex } from '../../constants.ts'
9
+ import { legendRows } from './legendRows.ts'
10
+
8
11
  import type { MsaViewModel } from '../../model.ts'
9
12
 
10
13
  const AnnotationLegend = observer(function ({
@@ -12,65 +15,72 @@ const AnnotationLegend = observer(function ({
12
15
  }: {
13
16
  model: MsaViewModel
14
17
  }) {
15
- const {
16
- actuallyShowDomains,
17
- visibleDomainTypes: visible,
18
- fillPalette,
19
- showDomainLegend: expanded,
20
- } = model
18
+ const { legends, showDomainLegend: expanded } = model
19
+ const rows = legendRows(legends)
21
20
  const ExpandIcon = expanded ? ExpandLess : ExpandMore
22
21
 
23
- return actuallyShowDomains && visible.length > 0 ? (
22
+ return rows.length > 0 ? (
24
23
  <Paper
25
24
  elevation={3}
26
25
  style={{
27
26
  position: 'absolute',
28
27
  top: 4,
29
28
  right: 4,
30
- zIndex: 100,
31
- maxWidth: 260,
29
+ zIndex: legendZIndex,
30
+ maxWidth: 220,
32
31
  maxHeight: '60%',
33
32
  display: 'flex',
34
33
  flexDirection: 'column',
35
34
  opacity: 0.95,
36
35
  }}
37
36
  >
38
- <div
39
- style={{
40
- display: 'flex',
41
- alignItems: 'center',
42
- justifyContent: 'flex-end',
43
- padding: '2px 4px 2px 8px',
37
+ <IconButton
38
+ size="small"
39
+ title={expanded ? 'Collapse key' : 'Expand key'}
40
+ style={
41
+ expanded
42
+ ? { position: 'absolute', top: 0, right: 0, padding: 1 }
43
+ : { padding: 1 }
44
+ }
45
+ onClick={() => {
46
+ model.setShowDomainLegend(!expanded)
44
47
  }}
45
48
  >
46
- <IconButton
47
- size="small"
48
- title={expanded ? 'Collapse key' : 'Expand key'}
49
- onClick={() => {
50
- model.setShowDomainLegend(!expanded)
51
- }}
52
- >
53
- <ExpandIcon fontSize="small" />
54
- </IconButton>
55
- </div>
49
+ <ExpandIcon style={{ fontSize: 14 }} />
50
+ </IconButton>
56
51
  {expanded ? (
57
- <div style={{ overflow: 'auto', padding: '0 8px 6px' }}>
58
- {visible.map(({ accession, name }) => (
52
+ <div style={{ overflow: 'auto', padding: '2px 6px 4px' }}>
53
+ {rows.map((row, i) => (
59
54
  <div
60
- key={accession}
61
- style={{ display: 'flex', alignItems: 'center', gap: 6 }}
62
- title={accession}
55
+ key={row.key}
56
+ style={{
57
+ display: 'flex',
58
+ alignItems: 'center',
59
+ gap: 4,
60
+ marginRight: i === 0 ? 18 : 0,
61
+ }}
62
+ title={row.hint}
63
63
  >
64
- <div
64
+ {row.color ? (
65
+ <div
66
+ style={{
67
+ width: 9,
68
+ height: 9,
69
+ flexShrink: 0,
70
+ background: row.color,
71
+ }}
72
+ />
73
+ ) : null}
74
+ <Typography
75
+ variant="caption"
76
+ noWrap
65
77
  style={{
66
- width: 12,
67
- height: 12,
68
- flexShrink: 0,
69
- background: fillPalette[accession],
78
+ fontSize: 10,
79
+ lineHeight: 1.4,
80
+ fontWeight: row.color ? undefined : 'bold',
70
81
  }}
71
- />
72
- <Typography variant="caption" noWrap>
73
- {name}
82
+ >
83
+ {row.label}
74
84
  </Typography>
75
85
  </div>
76
86
  ))}
@@ -34,7 +34,7 @@ const MSACanvas = observer(function ({ model }: { model: MsaViewModel }) {
34
34
  )
35
35
  const onZoom = useCallback(
36
36
  (scaleFactor: number, offsetX: number, offsetY: number) => {
37
- model.zoomToPos(scaleFactor, offsetX, offsetY)
37
+ model.zoomToPos(scaleFactor, offsetX, offsetY, model.wheelZoomAxis)
38
38
  },
39
39
  [model],
40
40
  )
@@ -49,16 +49,11 @@ const MSACanvas = observer(function ({ model }: { model: MsaViewModel }) {
49
49
  return (
50
50
  <div
51
51
  ref={ref}
52
- // the MSA viewport: its rect is the origin every column/row offset is
53
- // measured from (col*colWidth + scrollX, row*rowHeight + scrollY), which
54
- // is what lets a screenshot callout anchor to an alignment column instead
55
- // of a hand-measured pixel
52
+ // screenshot callouts anchor to columns from this rect's origin:
53
+ // col*colWidth + scrollX, row*rowHeight + scrollY
56
54
  data-testid="msa_canvas"
57
55
  onMouseDown={onMouseDown}
58
56
  onMouseUp={onMouseUp}
59
- onMouseLeave={event => {
60
- event.preventDefault()
61
- }}
62
57
  style={{
63
58
  position: 'relative',
64
59
  height,
@@ -5,7 +5,6 @@ import { useTheme } from '@mui/material'
5
5
  import { observer } from 'mobx-react'
6
6
 
7
7
  import { useCanvasAutorun } from '../../useCanvasAutorun.ts'
8
- import ColumnStats from './ColumnStats.tsx'
9
8
  import { drawMsaRaster, rasterSupported } from './msaRaster.ts'
10
9
  import { renderBoxFeatureCanvasBlock } from './renderBoxFeatureCanvasBlock.ts'
11
10
  import { renderMSABlock } from './renderMSABlock.ts'
@@ -39,6 +38,7 @@ const MSACanvasBlock = observer(function ({
39
38
  ctx.clearRect(0, 0, canvasSize, canvasSize)
40
39
  renderBoxFeatureCanvasBlock({
41
40
  ctx,
41
+ theme,
42
42
  offsetX,
43
43
  offsetY,
44
44
  model,
@@ -65,7 +65,7 @@ const MSACanvasBlock = observer(function ({
65
65
  deps: [model, offsetX, offsetY, theme],
66
66
  })
67
67
 
68
- const { hoveredInsertion, mouseOverDomains, showColumnStats } = model
68
+ const { hoveredInsertion, mouseOverDomains } = model
69
69
 
70
70
  return (
71
71
  <>
@@ -107,20 +107,17 @@ const MSACanvasBlock = observer(function ({
107
107
  : hoveredInsertion.letters}
108
108
  </>
109
109
  ) : (
110
- <>
111
- {mouseOverDomains.map(d => (
112
- <div key={`${d.accession}-${d.start}-${d.end}`}>
113
- <b>{d.name}</b> ({d.accession}) {d.start}-{d.end}
114
- {d.description ? (
115
- <>
116
- <br />
117
- {d.description}
118
- </>
119
- ) : null}
120
- </div>
121
- ))}
122
- {showColumnStats ? <ColumnStats model={model} /> : null}
123
- </>
110
+ mouseOverDomains.map(d => (
111
+ <div key={`${d.accession}-${d.start}-${d.end}`}>
112
+ <b>{d.name}</b> ({d.accession}) {d.start}-{d.end}
113
+ {d.description ? (
114
+ <>
115
+ <br />
116
+ {d.description}
117
+ </>
118
+ ) : null}
119
+ </div>
120
+ ))
124
121
  )}
125
122
  </BaseTooltip>
126
123
  ) : null}
@@ -4,6 +4,7 @@ import { isAlive } from '@jbrowse/mobx-state-tree'
4
4
  import { useTheme } from '@mui/material'
5
5
  import { observer } from 'mobx-react'
6
6
 
7
+ import { msaOverlayZIndex } from '../../constants.ts'
7
8
  import { useCanvasAutorun } from '../../useCanvasAutorun.ts'
8
9
  import { renderMouseover } from './renderMSAMouseover.ts'
9
10
 
@@ -41,7 +42,7 @@ const MSAMouseoverCanvas = observer(function ({
41
42
  left: 0,
42
43
  width,
43
44
  height,
44
- zIndex: 1000,
45
+ zIndex: msaOverlayZIndex,
45
46
  pointerEvents: 'none',
46
47
  }}
47
48
  />
@@ -1,19 +1,15 @@
1
1
  import type { DomainBand } from '../../types.ts'
2
2
 
3
3
  /**
4
- * Left-to-right cursor over one row's domain bands, answering "which band is
5
- * drawn on top at this column" — the fill a letter there has to contrast
6
- * against.
4
+ * Left-to-right cursor over one row's domain bands that returns the band drawn
5
+ * on top at a column, for a letter's contrast color.
7
6
  *
8
- * Not simply the first band covering the column. The overlay paints in
9
- * `domainBands` order, largest first, so where signatures nest — a family
10
- * enclosing a domain, which InterProScan emits routinely — the box a reader
11
- * actually sees is the last one painted over that column. `stackIndex` is that
12
- * paint order.
7
+ * The overlay paints in `domainBands` order, largest first, so where signatures
8
+ * nest (InterProScan often emits a family enclosing a domain) the visible band
9
+ * is the last one painted. `stackIndex` is that paint order.
13
10
  *
14
- * `bands` must be sorted by start column (`domainBandsByStart`), and columns
15
- * must be visited in increasing order, which is what the block renderer does.
16
- * Both keep this to one pass over the bands per row rather than a scan per cell.
11
+ * `bands` must be sorted by start column (`domainBandsByStart`) and columns
12
+ * visited in increasing order, which keeps this to one pass per row.
17
13
  */
18
14
  export function domainBandCursor(bands: DomainBand[] | undefined) {
19
15
  let next = 0
@@ -0,0 +1,205 @@
1
+ import { minFeatureLabelHeight } from '../../constants.ts'
2
+
3
+ import type { HierarchyNode } from '../../hierarchy.ts'
4
+ import type { Annotation, NodeWithIdsAndLength } from '../../types.ts'
5
+ import type { RenderCtx } from '../renderCtx.ts'
6
+
7
+ /** one drawn span: a feature, the lane it sits in, and its row's depth */
8
+ export interface SpanBand {
9
+ annotation: Annotation
10
+ lane: number
11
+ laneCount: number
12
+ }
13
+
14
+ /** one row's spans, at the row baseline `y` the tree layout gives it */
15
+ export interface SpanRow<T extends SpanBand> {
16
+ y: number
17
+ bands: T[]
18
+ }
19
+
20
+ /**
21
+ * Where a band lands inside its row. The alignment overlay fills the row, bars
22
+ * it along the bottom, or stacks thin sub-rows; a `features` row panel divides
23
+ * the row between its lanes.
24
+ */
25
+ export interface SpanLayout {
26
+ /** the band height in a row needing `laneCount` lanes */
27
+ height: (laneCount: number) => number
28
+ /** the band's top edge, from its row baseline, its lane and its height */
29
+ top: (y: number, lane: number, height: number) => number
30
+ /** how far back from the feature's end the head starts */
31
+ headLength: (height: number) => number
32
+ /**
33
+ * how far the head rises above the band. A bar too thin to taper reads its
34
+ * direction off a head taller than itself, and the bar sits on the row's
35
+ * bottom edge, so the room is above.
36
+ */
37
+ headRise?: (height: number) => number
38
+ }
39
+
40
+ /** the rows of `bandsByRow` that `leaves` covers, in display order */
41
+ export function spanRows<T extends SpanBand>(
42
+ leaves: HierarchyNode<NodeWithIdsAndLength>[],
43
+ bandsByRow: Map<string, T[]>,
44
+ ): SpanRow<T>[] {
45
+ const rows: SpanRow<T>[] = []
46
+ for (const leaf of leaves) {
47
+ const bands = bandsByRow.get(leaf.data.name)
48
+ if (bands && bands.length > 0) {
49
+ rows.push({ y: leaf.x!, bands })
50
+ }
51
+ }
52
+ return rows
53
+ }
54
+
55
+ /**
56
+ * The span mark, in whatever x space the caller maps its bands into: alignment
57
+ * columns for the domain overlay, panel pixels for a `features` row panel. A
58
+ * feature with a strand draws as an arrow, one without as a box, and a label
59
+ * draws inside the span wherever the text fits.
60
+ *
61
+ * `xOf` returns the span's `[xStart, xEnd]` in the context's coordinates, and
62
+ * the context is translated so that `y - rowHeight` is the row's top edge.
63
+ */
64
+ export function drawFeatureSpans<T extends SpanBand>({
65
+ ctx,
66
+ rows,
67
+ layout,
68
+ xOf,
69
+ colors,
70
+ labelOf,
71
+ labelDrawn,
72
+ contrastText,
73
+ xMin,
74
+ xMax,
75
+ }: {
76
+ ctx: RenderCtx
77
+ rows: SpanRow<T>[]
78
+ layout: SpanLayout
79
+ xOf: (band: T) => [number, number]
80
+ colors: Map<Annotation, { fill: string; stroke: string }>
81
+ labelOf?: (band: T) => string | undefined
82
+ labelDrawn?: (band: T) => void
83
+ contrastText: (color: string) => string
84
+ xMin: number
85
+ xMax: number
86
+ }) {
87
+ // one row's spans are drawn before any of its labels. Bands are ordered
88
+ // longest-first so a nested one lands on top, and drawing each label with its
89
+ // own span put the child's box over the parent's text: `S` covered `RBD`, and
90
+ // `RBD` read as `RB`. Reused across rows rather than allocated per row.
91
+ const placed: {
92
+ band: T
93
+ xStart: number
94
+ w: number
95
+ t: number
96
+ head: number
97
+ fill: string
98
+ }[] = []
99
+
100
+ for (const { y, bands } of rows) {
101
+ const h = layout.height(bands[0]!.laneCount)
102
+ const headLen = layout.headLength(h)
103
+ const rise = layout.headRise?.(h) ?? 0
104
+ const labelled = labelOf !== undefined && h >= minFeatureLabelHeight
105
+ placed.length = 0
106
+
107
+ for (const band of bands) {
108
+ const [xStart, xEnd] = xOf(band)
109
+ const w = xEnd - xStart
110
+ if (xEnd < xMin || xStart > xMax) {
111
+ continue
112
+ }
113
+ const t = layout.top(y, band.lane, h)
114
+ const { fill, stroke } = colors.get(band.annotation)!
115
+ ctx.fillStyle = fill
116
+ ctx.strokeStyle = stroke
117
+ const { strand } = band.annotation
118
+ const head = strand === undefined ? 0 : Math.min(headLen, w)
119
+ if (strand === undefined) {
120
+ ctx.fillRect(xStart, t, w, h)
121
+ ctx.strokeRect(xStart, t, w, h)
122
+ } else {
123
+ drawGeneArrow({ ctx, x: xStart, t, w, h, head, rise, strand })
124
+ }
125
+ if (labelled) {
126
+ placed.push({ band, xStart, w, t, head, fill })
127
+ }
128
+ }
129
+
130
+ if (placed.length > 0) {
131
+ ctx.font = `${Math.min(h - 2, 11)}px sans-serif`
132
+ ctx.textAlign = 'center'
133
+ ctx.textBaseline = 'middle'
134
+ for (const { band, xStart, w, t, head, fill } of placed) {
135
+ // asking for the label here rather than in the pass above keeps the
136
+ // once-per-block segment numbering in drawing order
137
+ const label = labelOf!(band)
138
+ // the head narrows to a point, so only about half of it holds text
139
+ if (
140
+ label !== undefined &&
141
+ ctx.measureText(label).width + 2 <= w - head / 2
142
+ ) {
143
+ labelDrawn?.(band)
144
+ ctx.fillStyle = contrastText(fill)
145
+ ctx.fillText(label, xStart + w / 2, t + h / 2)
146
+ }
147
+ }
148
+ }
149
+ }
150
+ }
151
+
152
+ /**
153
+ * A gene arrow, drawn the way gggenes and gggenomes draw one: the head is the
154
+ * last `head` pixels *of* the feature, tapering to a point at its end, and a
155
+ * feature shorter than the head is all head. The glyph therefore covers its
156
+ * start..end span and nothing else, so adjacent genes butt together instead of
157
+ * biting triangles out of each other, a neighbour never draws over a label, and
158
+ * a gene 20 columns wide reads as 20 columns wide.
159
+ *
160
+ * `rise` lifts the head above the band, for a bar too thin to taper.
161
+ */
162
+ function drawGeneArrow({
163
+ ctx,
164
+ x,
165
+ t,
166
+ w,
167
+ h,
168
+ head,
169
+ rise,
170
+ strand,
171
+ }: {
172
+ ctx: RenderCtx
173
+ x: number
174
+ t: number
175
+ w: number
176
+ h: number
177
+ head: number
178
+ rise: number
179
+ strand: number
180
+ }) {
181
+ const tip = strand > 0 ? x + w : x
182
+ const tail = strand > 0 ? x : x + w
183
+ const flange = strand > 0 ? tip - head : tip + head
184
+ const bottom = t + h
185
+ const crown = t - rise
186
+ const point = (crown + bottom) / 2
187
+ ctx.beginPath()
188
+ if (head >= w) {
189
+ ctx.moveTo(tail, crown)
190
+ ctx.lineTo(tip, point)
191
+ ctx.lineTo(tail, bottom)
192
+ } else {
193
+ ctx.moveTo(tail, t)
194
+ ctx.lineTo(flange, t)
195
+ if (rise > 0) {
196
+ ctx.lineTo(flange, crown)
197
+ }
198
+ ctx.lineTo(tip, point)
199
+ ctx.lineTo(flange, bottom)
200
+ ctx.lineTo(tail, bottom)
201
+ }
202
+ ctx.closePath()
203
+ ctx.fill()
204
+ ctx.stroke()
205
+ }
@@ -0,0 +1,24 @@
1
+ import type { Legend } from '../../types.ts'
2
+
3
+ export interface LegendRow {
4
+ key: string
5
+ label: string
6
+ color?: string
7
+ hint?: string
8
+ }
9
+
10
+ // the legends flattened into the rows both the overlay and the export stack
11
+ // top to bottom. A title row separates one legend from the next, and a lone
12
+ // legend needs no title.
13
+ export function legendRows(legends: Legend[]): LegendRow[] {
14
+ const titled = legends.length > 1
15
+ return legends.flatMap(legend => [
16
+ ...(titled ? [{ key: legend.id, label: legend.title }] : []),
17
+ ...legend.entries.map(entry => ({
18
+ key: `${legend.id}:${entry.id}`,
19
+ label: entry.label,
20
+ color: entry.color,
21
+ hint: entry.id,
22
+ })),
23
+ ])
24
+ }
@@ -36,9 +36,8 @@ const colorCache = new Map<string, number>()
36
36
 
37
37
  /**
38
38
  * CSS color string -> the packed pixel an ImageData holds for it. Painting a 1x1
39
- * canvas and reading it back handles every form a color scheme can produce --
40
- * hex, rgb(), hsl(), named -- without a parser of our own, and the cache means
41
- * each distinct color is resolved once per session rather than once per cell.
39
+ * canvas and reading it back parses hex, rgb(), hsl() and named colors; the
40
+ * cache resolves each distinct color once per session.
42
41
  */
43
42
  export function cssColorToPixel(css: string) {
44
43
  const hit = colorCache.get(css)
@@ -80,13 +79,12 @@ function cellPixelFn(spec: RasterSpec, toPixel: (css: string) => number) {
80
79
  /**
81
80
  * Packs a rectangle of the alignment into one pixel per cell, in `rowNames`
82
81
  * order, ready to hand to putImageData. Cells past the end of a row, and rows
83
- * with no sequence at all, stay transparent -- what the per-cell tile painter
84
- * also leaves behind.
82
+ * with no sequence, stay transparent, as in the per-cell painter.
85
83
  *
86
- * `colStep`/`rowStep` sample instead of covering, which is how the minimap fits
87
- * a whole alignment into a 12px bar. `colSpan`/`rowSpan` average each block of
88
- * that many cells into one pixel instead, so a zoom below a device pixel per
89
- * column still shows every column's color without blending neighbouring rows.
84
+ * `colStep`/`rowStep` sample every nth cell, which fits a whole alignment into
85
+ * the minimap's 12px bar. `colSpan`/`rowSpan` average each block of that many
86
+ * cells into one pixel, so a zoom below a device pixel per column still shows
87
+ * every column's color without blending neighbouring rows.
90
88
  */
91
89
  export function rasterPixels({
92
90
  spec,
@@ -292,8 +290,8 @@ interface RasterCache {
292
290
 
293
291
  const caches = new WeakMap<object, RasterCache>()
294
292
 
295
- // Everything a cell's color depends on. A change to any of them makes every
296
- // cached tile stale, which a fresh cache expresses without tracking which.
293
+ // Everything a cell's color depends on; a change to any of them starts a fresh
294
+ // cache.
297
295
  function rasterKeys(model: MsaViewModel, theme: Theme) {
298
296
  return [
299
297
  model.columns,
@@ -402,13 +400,12 @@ function getTile({
402
400
  /**
403
401
  * Paints the alignment background of one MSA block from cached
404
402
  * one-pixel-per-cell tiles. The tiles do not depend on the zoom level, so a zoom
405
- * frame costs a handful of drawImage calls instead of a fillRect per visible
406
- * cell -- which ran to 885ms per block at the minimum column width.
403
+ * frame costs a handful of drawImage calls. A fillRect per visible cell measured
404
+ * 885ms per block at the minimum column width.
407
405
  *
408
- * Below a device pixel per cell the tiles average cells together instead, and
409
- * only along the axis that needs it. Leaving the downsampling to the browser's
410
- * image smoothing blurred both axes at once: at fit-to-width, where only the
411
- * columns are narrow, every row boundary turned into a gradient.
406
+ * Below a device pixel per cell the tiles average cells together, per axis.
407
+ * The browser's image smoothing blurs both axes, which turns every row
408
+ * boundary into a gradient at fit-to-width, where only the columns are narrow.
412
409
  */
413
410
  export function drawMsaRaster({
414
411
  ctx,
@@ -467,12 +464,11 @@ export function drawMsaRaster({
467
464
  /**
468
465
  * A raster canvas as a PNG data URI, for embedding in the SVG export.
469
466
  *
470
- * toDataURL is the synchronous read-back. The DOM canvas has it, and so does a
471
- * node canvas standing in for OffscreenCanvas headlessly; a browser
472
- * OffscreenCanvas -- which is what the tile and thumbnail caches hold wherever
473
- * it exists -- gets copied onto a DOM canvas first. Returns undefined wherever
474
- * the read-back is unavailable (jsdom) rather than throwing, which is the
475
- * signal to fall back to drawing the thing in vector.
467
+ * toDataURL is the synchronous read-back. The DOM canvas and a headless node
468
+ * canvas have it; a browser OffscreenCanvas, which the tile and thumbnail
469
+ * caches hold where it exists, is copied onto a DOM canvas first. Returns
470
+ * undefined where read-back is unavailable (jsdom), and the caller then draws
471
+ * in vector.
476
472
  */
477
473
  export function canvasHref(canvas: RasterCanvas | undefined) {
478
474
  if (!canvas) {
@@ -499,12 +495,10 @@ export function canvasHref(canvas: RasterCanvas | undefined) {
499
495
  }
500
496
  }
501
497
 
502
- // A canvas much past this many pixels fails to allocate, and the browser's own
503
- // per-side limit is lower still, so a raster bigger than either averages down,
504
- // per axis: an alignment wide enough to hit the limit on columns alone keeps
505
- // every row.
506
- // The result is still drawn across the same rectangle -- it loses cell-exact
507
- // detail at a size where no figure could show it anyway.
498
+ // A canvas much past this many pixels fails to allocate, and the browser's
499
+ // per-side limit is lower still, so a larger raster averages down per axis and
500
+ // is drawn across the same rectangle. An alignment that hits the limit on
501
+ // columns alone keeps every row.
508
502
  const maxImagePixels = 64e6
509
503
  const maxImageSide = 16384
510
504
 
@@ -512,14 +506,12 @@ const maxImageSide = 16384
512
506
  * The alignment rectangle [col0, col0+numCols) x [row0, row0+numRows) as a PNG
513
507
  * data URI, for the SVG export.
514
508
  *
515
- * SVG has no blit, so the export cannot reuse the tile cache the live canvas
516
- * draws from: one <image> is the whole background instead. That is the
517
- * difference between an export that scales and one that does not -- the vector
518
- * path emits a <rect> per cell, and a 200x500 alignment exhausts the heap
519
- * building them.
509
+ * SVG has no blit, so the export draws the whole background as one <image>. The
510
+ * vector path emits a <rect> per cell, and a 200x500 alignment exhausts the
511
+ * heap building them.
520
512
  *
521
- * Returns undefined wherever a canvas cannot be read back (jsdom, notably),
522
- * which is the signal to keep the per-cell path.
513
+ * Returns undefined where a canvas cannot be read back (jsdom), and the caller
514
+ * then keeps the per-cell path.
523
515
  */
524
516
  export function rasterImageHref({
525
517
  model,
@@ -578,9 +570,8 @@ export function rasterImageHref({
578
570
  }
579
571
 
580
572
  /**
581
- * The whole alignment sampled down to a strip small enough to sit behind the
582
- * minimap thumb, cached alongside the block tiles because it goes stale under
583
- * exactly the same conditions.
573
+ * The whole alignment sampled down to a strip behind the minimap thumb, cached
574
+ * with the block tiles since the same keys invalidate both.
584
575
  */
585
576
  export function msaThumbnail({
586
577
  model,
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Assigns each of one row's bands a lane: the first lane whose last band ends
3
+ * before this one starts, so only bands that actually overlap stack. A row of
4
+ * adjacent exons packs into a single lane; a family with a domain nested inside
5
+ * it takes two.
6
+ *
7
+ * Packing runs left to right, but the result keeps the input's largest-first
8
+ * paint order. `laneCount` is the row's depth, which the renderer divides the
9
+ * row height by so a deep stack stays inside its own row.
10
+ *
11
+ * The coordinates are whatever the caller packs in: alignment columns for the
12
+ * overlay, residue positions for a `features` row panel.
13
+ */
14
+ export function packDomainLanes<T extends { startCol: number; endCol: number }>(
15
+ bands: T[],
16
+ ): (T & { lane: number; laneCount: number })[] {
17
+ const laneEnds: number[] = []
18
+ const lanes = new Array<number>(bands.length)
19
+ const byStart = bands
20
+ .map((_, i) => i)
21
+ .sort((a, b) => bands[a]!.startCol - bands[b]!.startCol)
22
+
23
+ for (const i of byStart) {
24
+ const { startCol, endCol } = bands[i]!
25
+ const found = laneEnds.findIndex(end => end <= startCol)
26
+ const lane = found === -1 ? laneEnds.length : found
27
+ laneEnds[lane] = endCol
28
+ lanes[i] = lane
29
+ }
30
+
31
+ return bands.map((band, i) => ({
32
+ ...band,
33
+ lane: lanes[i]!,
34
+ laneCount: laneEnds.length,
35
+ }))
36
+ }