react-msaview 7.0.0 → 8.0.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 (310) 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/colorSchemes.js +1 -2
  5. package/dist/colorSchemes.js.map +1 -1
  6. package/dist/columnStats.d.ts +28 -0
  7. package/dist/columnStats.js +34 -0
  8. package/dist/columnStats.js.map +1 -0
  9. package/dist/components/DragHandle.d.ts +3 -2
  10. package/dist/components/DragHandle.js +11 -5
  11. package/dist/components/DragHandle.js.map +1 -1
  12. package/dist/components/Loading.js.map +1 -1
  13. package/dist/components/MSAView.js +17 -6
  14. package/dist/components/MSAView.js.map +1 -1
  15. package/dist/components/MSAViewer.d.ts +64 -7
  16. package/dist/components/MSAViewer.js +128 -23
  17. package/dist/components/MSAViewer.js.map +1 -1
  18. package/dist/components/PortalTooltip.d.ts +11 -0
  19. package/dist/components/PortalTooltip.js +29 -0
  20. package/dist/components/PortalTooltip.js.map +1 -0
  21. package/dist/components/ResizeHandles.js +11 -29
  22. package/dist/components/ResizeHandles.js.map +1 -1
  23. package/dist/components/SequenceTextArea.js +3 -3
  24. package/dist/components/SequenceTextArea.js.map +1 -1
  25. package/dist/components/Track.js +41 -22
  26. package/dist/components/Track.js.map +1 -1
  27. package/dist/components/dialogs/ExportSVGDialog.js +5 -3
  28. package/dist/components/dialogs/ExportSVGDialog.js.map +1 -1
  29. package/dist/components/dialogs/TrackInfoDialog.js +1 -1
  30. package/dist/components/dialogs/TrackInfoDialog.js.map +1 -1
  31. package/dist/components/header/GappynessSlider.js +6 -11
  32. package/dist/components/header/GappynessSlider.js.map +1 -1
  33. package/dist/components/header/Header.js +3 -0
  34. package/dist/components/header/Header.js.map +1 -1
  35. package/dist/components/header/LoadWarnings.js +3 -4
  36. package/dist/components/header/LoadWarnings.js.map +1 -1
  37. package/dist/components/header/ScrollZoomIcons.d.ts +6 -0
  38. package/dist/components/header/ScrollZoomIcons.js +35 -0
  39. package/dist/components/header/ScrollZoomIcons.js.map +1 -0
  40. package/dist/components/header/TreeSettingsMenu.js +1 -3
  41. package/dist/components/header/TreeSettingsMenu.js.map +1 -1
  42. package/dist/components/header/UnshareableDataWarning.js +2 -4
  43. package/dist/components/header/UnshareableDataWarning.js.map +1 -1
  44. package/dist/components/header/ZoomControls.js +49 -6
  45. package/dist/components/header/ZoomControls.js.map +1 -1
  46. package/dist/components/header/getAnnotationsMenu.js +3 -6
  47. package/dist/components/header/getAnnotationsMenu.js.map +1 -1
  48. package/dist/components/header/settingsMenuItems.js +10 -7
  49. package/dist/components/header/settingsMenuItems.js.map +1 -1
  50. package/dist/components/import/util.js +1 -2
  51. package/dist/components/import/util.js.map +1 -1
  52. package/dist/components/minimap/Minimap.js +1 -2
  53. package/dist/components/minimap/Minimap.js.map +1 -1
  54. package/dist/components/msa/AnnotationLegend.js +27 -19
  55. package/dist/components/msa/AnnotationLegend.js.map +1 -1
  56. package/dist/components/msa/MSACanvas.js +1 -1
  57. package/dist/components/msa/MSACanvas.js.map +1 -1
  58. package/dist/components/msa/MSACanvasBlock.js +13 -15
  59. package/dist/components/msa/MSACanvasBlock.js.map +1 -1
  60. package/dist/components/msa/MSAMouseoverCanvas.js +2 -1
  61. package/dist/components/msa/MSAMouseoverCanvas.js.map +1 -1
  62. package/dist/components/msa/domainBandCursor.d.ts +7 -11
  63. package/dist/components/msa/domainBandCursor.js +7 -11
  64. package/dist/components/msa/domainBandCursor.js.map +1 -1
  65. package/dist/components/msa/drawFeatureSpans.d.ts +53 -0
  66. package/dist/components/msa/drawFeatureSpans.js +78 -0
  67. package/dist/components/msa/drawFeatureSpans.js.map +1 -0
  68. package/dist/components/msa/legendRows.d.ts +8 -0
  69. package/dist/components/msa/legendRows.js +16 -0
  70. package/dist/components/msa/legendRows.js.map +1 -0
  71. package/dist/components/msa/msaRaster.d.ts +24 -31
  72. package/dist/components/msa/msaRaster.js +30 -39
  73. package/dist/components/msa/msaRaster.js.map +1 -1
  74. package/dist/components/msa/packDomainLanes.d.ts +20 -0
  75. package/dist/components/msa/packDomainLanes.js +33 -0
  76. package/dist/components/msa/packDomainLanes.js.map +1 -0
  77. package/dist/components/msa/renderBoxFeatureCanvasBlock.d.ts +3 -1
  78. package/dist/components/msa/renderBoxFeatureCanvasBlock.js +51 -87
  79. package/dist/components/msa/renderBoxFeatureCanvasBlock.js.map +1 -1
  80. package/dist/components/msa/renderHighlights.d.ts +15 -13
  81. package/dist/components/msa/renderHighlights.js +43 -20
  82. package/dist/components/msa/renderHighlights.js.map +1 -1
  83. package/dist/components/msa/renderMSABlock.js +5 -4
  84. package/dist/components/msa/renderMSABlock.js.map +1 -1
  85. package/dist/components/msa/tileColor.js +4 -7
  86. package/dist/components/msa/tileColor.js.map +1 -1
  87. package/dist/components/msa/useMsaBlockMouse.d.ts +5 -3
  88. package/dist/components/msa/useMsaBlockMouse.js +6 -6
  89. package/dist/components/msa/useMsaBlockMouse.js.map +1 -1
  90. package/dist/components/rowpanels/RowPanelBlock.d.ts +9 -0
  91. package/dist/components/rowpanels/RowPanelBlock.js +86 -0
  92. package/dist/components/rowpanels/RowPanelBlock.js.map +1 -0
  93. package/dist/components/rowpanels/RowPanelHeaders.d.ts +12 -0
  94. package/dist/components/rowpanels/RowPanelHeaders.js +38 -0
  95. package/dist/components/rowpanels/RowPanelHeaders.js.map +1 -0
  96. package/dist/components/{msa/ColumnStats.d.ts → rowpanels/RowPanels.d.ts} +2 -2
  97. package/dist/components/rowpanels/RowPanels.js +32 -0
  98. package/dist/components/rowpanels/RowPanels.js.map +1 -0
  99. package/dist/components/rowpanels/headerLayout.d.ts +1 -0
  100. package/dist/components/rowpanels/headerLayout.js +5 -0
  101. package/dist/components/rowpanels/headerLayout.js.map +1 -0
  102. package/dist/components/rowpanels/renderFeaturePanel.d.ts +21 -0
  103. package/dist/components/rowpanels/renderFeaturePanel.js +37 -0
  104. package/dist/components/rowpanels/renderFeaturePanel.js.map +1 -0
  105. package/dist/components/rowpanels/renderRowPanel.d.ts +23 -0
  106. package/dist/components/rowpanels/renderRowPanel.js +22 -0
  107. package/dist/components/rowpanels/renderRowPanel.js.map +1 -0
  108. package/dist/components/rowpanels/renderStrip.d.ts +20 -0
  109. package/dist/components/rowpanels/renderStrip.js +26 -0
  110. package/dist/components/rowpanels/renderStrip.js.map +1 -0
  111. package/dist/components/tracks/TrackBlocks.js +3 -5
  112. package/dist/components/tracks/TrackBlocks.js.map +1 -1
  113. package/dist/components/tracks/TrackTooltipContent.d.ts +15 -0
  114. package/dist/components/tracks/TrackTooltipContent.js +109 -0
  115. package/dist/components/tracks/TrackTooltipContent.js.map +1 -0
  116. package/dist/components/tracks/drawTracks.d.ts +4 -4
  117. package/dist/components/tracks/drawTracks.js +17 -34
  118. package/dist/components/tracks/drawTracks.js.map +1 -1
  119. package/dist/components/tracks/useTrackHover.d.ts +22 -0
  120. package/dist/components/tracks/useTrackHover.js +30 -0
  121. package/dist/components/tracks/useTrackHover.js.map +1 -0
  122. package/dist/components/tree/CladeLabels.d.ts +12 -0
  123. package/dist/components/tree/CladeLabels.js +48 -0
  124. package/dist/components/tree/CladeLabels.js.map +1 -0
  125. package/dist/components/tree/TreeCanvas.js +4 -1
  126. package/dist/components/tree/TreeCanvas.js.map +1 -1
  127. package/dist/components/tree/TreeCanvasBlock.js +4 -21
  128. package/dist/components/tree/TreeCanvasBlock.js.map +1 -1
  129. package/dist/components/tree/TreeOverview.d.ts +14 -0
  130. package/dist/components/tree/TreeOverview.js +75 -0
  131. package/dist/components/tree/TreeOverview.js.map +1 -0
  132. package/dist/components/tree/TreeRuler.js +9 -13
  133. package/dist/components/tree/TreeRuler.js.map +1 -1
  134. package/dist/components/tree/cladeBrackets.d.ts +25 -0
  135. package/dist/components/tree/cladeBrackets.js +51 -0
  136. package/dist/components/tree/cladeBrackets.js.map +1 -0
  137. package/dist/components/tree/dialogs/TreeNodeInfoDialog.js +2 -2
  138. package/dist/components/tree/dialogs/TreeNodeInfoDialog.js.map +1 -1
  139. package/dist/components/tree/renderTreeCanvas.js +158 -24
  140. package/dist/components/tree/renderTreeCanvas.js.map +1 -1
  141. package/dist/components/tree/renderTreeOverview.d.ts +52 -0
  142. package/dist/components/tree/renderTreeOverview.js +138 -0
  143. package/dist/components/tree/renderTreeOverview.js.map +1 -0
  144. package/dist/components/tree/useTreeHover.js +2 -4
  145. package/dist/components/tree/useTreeHover.js.map +1 -1
  146. package/dist/constants.d.ts +15 -0
  147. package/dist/constants.js +48 -22
  148. package/dist/constants.js.map +1 -1
  149. package/dist/createPaletteMap.d.ts +7 -6
  150. package/dist/createPaletteMap.js +10 -9
  151. package/dist/createPaletteMap.js.map +1 -1
  152. package/dist/element.d.ts +13 -0
  153. package/dist/element.js +269 -0
  154. package/dist/element.js.map +1 -0
  155. package/dist/featureFields.d.ts +11 -0
  156. package/dist/featureFields.js +17 -0
  157. package/dist/featureFields.js.map +1 -0
  158. package/dist/flatToTree.js +1 -2
  159. package/dist/flatToTree.js.map +1 -1
  160. package/dist/ggplotPalettes.d.ts +10 -0
  161. package/dist/ggplotPalettes.js +58 -0
  162. package/dist/ggplotPalettes.js.map +1 -1
  163. package/dist/hierarchy.d.ts +27 -0
  164. package/dist/hierarchy.js +66 -9
  165. package/dist/hierarchy.js.map +1 -1
  166. package/dist/index.d.ts +9 -1
  167. package/dist/index.js +5 -3
  168. package/dist/index.js.map +1 -1
  169. package/dist/model/DataModel.js +2 -5
  170. package/dist/model/DataModel.js.map +1 -1
  171. package/dist/model/msaModel.d.ts +3 -2
  172. package/dist/model/msaModel.js +3 -2
  173. package/dist/model/msaModel.js.map +1 -1
  174. package/dist/model/treeModel.d.ts +30 -0
  175. package/dist/model/treeModel.js +37 -1
  176. package/dist/model/treeModel.js.map +1 -1
  177. package/dist/model.d.ts +531 -275
  178. package/dist/model.js +1295 -547
  179. package/dist/model.js.map +1 -1
  180. package/dist/mount.d.ts +11 -0
  181. package/dist/mount.js +22 -0
  182. package/dist/mount.js.map +1 -0
  183. package/dist/neighborJoining.js +8 -32
  184. package/dist/neighborJoining.js.map +1 -1
  185. package/dist/renderToStaticMarkup.d.ts +10 -19
  186. package/dist/renderToStaticMarkup.js +13 -24
  187. package/dist/renderToStaticMarkup.js.map +1 -1
  188. package/dist/renderToSvg.js +180 -56
  189. package/dist/renderToSvg.js.map +1 -1
  190. package/dist/rowCoordinateCalculations.d.ts +4 -6
  191. package/dist/rowCoordinateCalculations.js +4 -6
  192. package/dist/rowCoordinateCalculations.js.map +1 -1
  193. package/dist/scales.d.ts +22 -0
  194. package/dist/scales.js +30 -0
  195. package/dist/scales.js.map +1 -0
  196. package/dist/seqPosToGlobalCol.d.ts +2 -3
  197. package/dist/seqPosToGlobalCol.js +2 -3
  198. package/dist/seqPosToGlobalCol.js.map +1 -1
  199. package/dist/sequenceLogo.d.ts +6 -11
  200. package/dist/sequenceLogo.js +6 -11
  201. package/dist/sequenceLogo.js.map +1 -1
  202. package/dist/stripDefault.d.ts +7 -14
  203. package/dist/stripDefault.js +7 -14
  204. package/dist/stripDefault.js.map +1 -1
  205. package/dist/svgTestUtil.d.ts +172 -52
  206. package/dist/types.d.ts +219 -21
  207. package/dist/umd.d.ts +2 -0
  208. package/dist/umd.js +4 -2
  209. package/dist/umd.js.map +1 -1
  210. package/dist/useCanvasAutorun.d.ts +5 -10
  211. package/dist/useCanvasAutorun.js +5 -10
  212. package/dist/useCanvasAutorun.js.map +1 -1
  213. package/dist/useMsaSvgFigure.d.ts +20 -0
  214. package/dist/useMsaSvgFigure.js +46 -0
  215. package/dist/useMsaSvgFigure.js.map +1 -0
  216. package/dist/util.d.ts +5 -6
  217. package/dist/util.js +13 -6
  218. package/dist/util.js.map +1 -1
  219. package/dist/version.d.ts +1 -1
  220. package/dist/version.js +1 -1
  221. package/package.json +19 -12
  222. package/src/colorSchemes.ts +1 -2
  223. package/src/columnStats.ts +58 -0
  224. package/src/components/DragHandle.tsx +18 -7
  225. package/src/components/Loading.tsx +2 -6
  226. package/src/components/MSAView.tsx +19 -5
  227. package/src/components/MSAViewer.tsx +240 -28
  228. package/src/components/PortalTooltip.tsx +47 -0
  229. package/src/components/ResizeHandles.tsx +17 -32
  230. package/src/components/SequenceTextArea.tsx +3 -3
  231. package/src/components/Track.tsx +55 -18
  232. package/src/components/dialogs/ExportSVGDialog.tsx +7 -2
  233. package/src/components/dialogs/TrackInfoDialog.tsx +1 -1
  234. package/src/components/header/GappynessSlider.tsx +6 -11
  235. package/src/components/header/Header.tsx +6 -0
  236. package/src/components/header/LoadWarnings.tsx +3 -4
  237. package/src/components/header/ScrollZoomIcons.tsx +52 -0
  238. package/src/components/header/TreeSettingsMenu.tsx +1 -3
  239. package/src/components/header/UnshareableDataWarning.tsx +2 -4
  240. package/src/components/header/ZoomControls.tsx +74 -14
  241. package/src/components/header/getAnnotationsMenu.ts +3 -6
  242. package/src/components/header/settingsMenuItems.ts +11 -6
  243. package/src/components/import/ImportFormExamples.tsx +2 -2
  244. package/src/components/import/util.ts +1 -2
  245. package/src/components/minimap/Minimap.tsx +1 -2
  246. package/src/components/msa/AnnotationLegend.tsx +48 -38
  247. package/src/components/msa/MSACanvas.tsx +3 -5
  248. package/src/components/msa/MSACanvasBlock.tsx +13 -16
  249. package/src/components/msa/MSAMouseoverCanvas.tsx +2 -1
  250. package/src/components/msa/domainBandCursor.ts +7 -11
  251. package/src/components/msa/drawFeatureSpans.ts +150 -0
  252. package/src/components/msa/legendRows.ts +24 -0
  253. package/src/components/msa/msaRaster.ts +30 -39
  254. package/src/components/msa/packDomainLanes.ts +36 -0
  255. package/src/components/msa/renderBoxFeatureCanvasBlock.ts +69 -123
  256. package/src/components/msa/renderHighlights.ts +53 -20
  257. package/src/components/msa/renderMSABlock.ts +5 -3
  258. package/src/components/msa/tileColor.ts +4 -7
  259. package/src/components/msa/useMsaBlockMouse.ts +6 -6
  260. package/src/components/rowpanels/RowPanelBlock.tsx +139 -0
  261. package/src/components/rowpanels/RowPanelHeaders.tsx +57 -0
  262. package/src/components/rowpanels/RowPanels.tsx +77 -0
  263. package/src/components/rowpanels/headerLayout.ts +5 -0
  264. package/src/components/rowpanels/renderFeaturePanel.ts +66 -0
  265. package/src/components/rowpanels/renderRowPanel.ts +41 -0
  266. package/src/components/rowpanels/renderStrip.ts +46 -0
  267. package/src/components/tracks/TrackBlocks.tsx +3 -5
  268. package/src/components/tracks/TrackTooltipContent.tsx +156 -0
  269. package/src/components/tracks/drawTracks.ts +17 -34
  270. package/src/components/tracks/useTrackHover.ts +42 -0
  271. package/src/components/tree/CladeLabels.tsx +77 -0
  272. package/src/components/tree/TreeCanvas.tsx +2 -0
  273. package/src/components/tree/TreeCanvasBlock.tsx +8 -32
  274. package/src/components/tree/TreeOverview.tsx +106 -0
  275. package/src/components/tree/TreeRuler.tsx +11 -22
  276. package/src/components/tree/cladeBrackets.ts +73 -0
  277. package/src/components/tree/dialogs/TreeNodeInfoDialog.tsx +2 -2
  278. package/src/components/tree/renderTreeCanvas.ts +240 -20
  279. package/src/components/tree/renderTreeOverview.ts +212 -0
  280. package/src/components/tree/useTreeHover.ts +2 -4
  281. package/src/constants.ts +56 -22
  282. package/src/createPaletteMap.ts +10 -9
  283. package/src/element.ts +321 -0
  284. package/src/featureFields.ts +19 -0
  285. package/src/flatToTree.ts +1 -2
  286. package/src/ggplotPalettes.ts +60 -0
  287. package/src/hierarchy.ts +77 -9
  288. package/src/index.ts +31 -7
  289. package/src/model/DataModel.ts +2 -5
  290. package/src/model/msaModel.ts +3 -2
  291. package/src/model/treeModel.ts +42 -0
  292. package/src/model.ts +1523 -560
  293. package/src/mount.tsx +32 -0
  294. package/src/neighborJoining.ts +8 -32
  295. package/src/renderToStaticMarkup.ts +13 -24
  296. package/src/renderToSvg.tsx +335 -68
  297. package/src/rowCoordinateCalculations.ts +4 -6
  298. package/src/scales.ts +47 -0
  299. package/src/seqPosToGlobalCol.ts +2 -3
  300. package/src/sequenceLogo.ts +6 -11
  301. package/src/stripDefault.ts +7 -14
  302. package/src/types.ts +258 -45
  303. package/src/umd.ts +4 -2
  304. package/src/useCanvasAutorun.ts +5 -10
  305. package/src/useMsaSvgFigure.ts +72 -0
  306. package/src/util.ts +15 -6
  307. package/src/version.ts +1 -1
  308. package/dist/components/msa/ColumnStats.js +0 -42
  309. package/dist/components/msa/ColumnStats.js.map +0 -1
  310. package/src/components/msa/ColumnStats.tsx +0 -54
package/src/scales.ts ADDED
@@ -0,0 +1,47 @@
1
+ import { createPaletteMap } from './createPaletteMap.ts'
2
+ import { paletteNamed } from './ggplotPalettes.ts'
3
+
4
+ import type { LegendEntry } from './types.ts'
5
+
6
+ /** how a channel reads a field: a palette by name, or a color per value */
7
+ export interface ScaleSpec {
8
+ palette?: string
9
+ map?: Record<string, string>
10
+ }
11
+
12
+ export interface ResolvedScale {
13
+ kind: 'categorical'
14
+ colorOf: (value: string) => string | undefined
15
+ legend: LegendEntry[]
16
+ }
17
+
18
+ /**
19
+ * The scale a channel reads its field through. `values` are the field's values,
20
+ * in any order and with repeats; the domain is their distinct values sorted, so
21
+ * a value keeps its color when rows are re-ordered, collapsed or filtered.
22
+ *
23
+ * `{map}` colors the values the producer named and leaves the rest uncolored.
24
+ * `{palette}` takes the colors of a named palette, and a spec with neither
25
+ * takes the ggplot palette of the domain's size. Every scale is categorical
26
+ * today; a continuous one waits for a caller.
27
+ */
28
+ export function resolveScale(
29
+ spec: ScaleSpec | undefined,
30
+ values: Iterable<string>,
31
+ ): ResolvedScale {
32
+ const domain = [...new Set(values)].sort((a, b) => a.localeCompare(b))
33
+ const { map, palette } = spec ?? {}
34
+ const colors = map ?? createPaletteMap(domain, paletteNamed(palette))
35
+ const present = new Set(domain)
36
+ const labels = map ? Object.keys(map).filter(v => present.has(v)) : domain
37
+ return {
38
+ kind: 'categorical',
39
+ colorOf: (value: string) => colors[value],
40
+ // a categorical value is its own label, and the id a legend keys on
41
+ legend: labels.map(value => ({
42
+ id: value,
43
+ label: value,
44
+ color: colors[value]!,
45
+ })),
46
+ }
47
+ }
@@ -2,9 +2,8 @@
2
2
  * The global column of every ungapped sequence position in a row, i.e.
3
3
  * index[seqPos] is the column holding the seqPos-th non-gap character.
4
4
  *
5
- * A row is indexed once and then answers seqPos -> column by lookup, which is
6
- * what makes the domain overlay affordable: it resolves one position per feature
7
- * per row on every redraw, and scanning the row for each would be quadratic.
5
+ * The domain overlay resolves one position per feature per row on every redraw,
6
+ * and scanning the row for each would be quadratic.
8
7
  *
9
8
  * @example
10
9
  * // Row: "A-TG-C" (A at 0, T at 2, G at 3, C at 5)
@@ -19,18 +19,13 @@ export function maxBitsFor(sequenceType: 'dna' | 'rna' | 'amino') {
19
19
  * stack bottom-up in array order.
20
20
  *
21
21
  * Heights follow Schneider & Stephens: the column's information content is
22
- * `log2(alphabet) - H` bits, and each residue takes the share of that its own
23
- * frequency earns. Frequencies come from the non-gap residues only, so a column
24
- * of two residues and eight gaps is read as a two-way choice rather than a
25
- * ten-way one -- otherwise every gappy column would look uninformative for the
26
- * wrong reason.
22
+ * `log2(alphabet) - H` bits, split among residues by frequency. Frequencies
23
+ * count non-gap residues only, so two residues and eight gaps form a two-way
24
+ * choice.
27
25
  *
28
- * The whole stack is then scaled by the non-gap fraction, which plain WebLogo
29
- * does not do. Here it earns its place: the track sits directly under an
30
- * alignment where the gaps are visible, and a column that is 90% gap drawing a
31
- * full-height stack off its one remaining pair of residues reads as a conserved
32
- * site. Scaling makes stack height mean "how much this column tells you about
33
- * the family", matching what the conservation track above it already plots.
26
+ * Unlike plain WebLogo, the stack is then scaled by the non-gap fraction, so a
27
+ * 90%-gap column with one conserved pair does not draw a full-height stack, and
28
+ * stack height matches the conservation track.
34
29
  */
35
30
  export function columnLogoStack(
36
31
  colStats: ColumnCounts,
@@ -9,21 +9,14 @@ import type {
9
9
  /**
10
10
  * `types.stripDefault`, degrading to `types.optional` on hosts that lack it.
11
11
  *
12
- * stripDefault keeps defaulted properties out of snapshots, but it exists only
13
- * in the mobx-state-tree that ships with unreleased @jbrowse/core. This package
14
- * declares `@jbrowse/mobx-state-tree: ^5.0.0`, and inside jbrowse-web the host
15
- * supplies its own copy at runtime -- the plugin bundle externalizes it -- so we
16
- * genuinely do not know which version we are calling until we are running.
12
+ * stripDefault keeps defaulted properties out of snapshots, and only the
13
+ * mobx-state-tree in unreleased @jbrowse/core has it. The plugin bundle
14
+ * externalizes mobx-state-tree, so the host's copy is known only at runtime, and
15
+ * calling a missing stripDefault during model construction makes PluginLoader
16
+ * reject and the whole app show its error page.
17
17
  *
18
- * Calling it where it is absent throws while the model is being constructed,
19
- * which in a plugin bundle means PluginLoader rejects and the entire app renders
20
- * its error page. Losing minimal snapshots is a far better failure than losing
21
- * the session, so this degrades instead.
22
- *
23
- * The two signatures are identical, so behaviour differs only in that defaults
24
- * reappear in snapshots on older hosts (bigger session snapshots and shared
25
- * URLs, nothing incorrect). stripDefaultSnapshot.test.ts still pins the stripped
26
- * behaviour, since dev runs against a version that has it.
18
+ * The signatures are identical; on older hosts, defaults reappear in snapshots
19
+ * and shared URLs. stripDefaultSnapshot.test.ts pins the stripped behaviour.
27
20
  */
28
21
  export function stripDefault<IT extends IAnyType>(
29
22
  type: IT,
package/src/types.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import type { ResolvedScale, ScaleSpec } from './scales.ts'
1
2
  import type { Annotation } from 'msa-parsers'
2
3
 
3
4
  export interface Accession {
@@ -5,17 +6,26 @@ export interface Accession {
5
6
  name: string
6
7
  description: string
7
8
  }
8
- // which renderer draws a track's content. Every track kind draws into the same
9
- // per-column coordinate space, so the kind picks the draw function rather than
10
- // the geometry -- see drawTracks.ts, which dispatches on it.
9
+ // every kind draws into the same per-column space; drawTracks.ts dispatches on
10
+ // it to pick the draw function
11
11
  export type TrackKind = 'text' | 'bar' | 'logo' | 'arc' | 'ruler'
12
12
 
13
+ /**
14
+ * Which channel the color scheme paints. `fill` colors the cell a residue sits
15
+ * in, `color` colors the letter itself, the same distinction a grammar of
16
+ * graphics draws between a shape's interior and its own ink.
17
+ */
18
+ export type ResidueEncoding = 'fill' | 'color'
19
+
13
20
  export interface BasicTrackModel {
14
21
  id: string
15
22
  name: string
16
23
  associatedRowName?: string
17
24
  height: number
18
25
  kind: TrackKind
26
+ // the model height this track's divider writes, shared with every track
27
+ // carrying the same key. Absent on a track no divider resizes.
28
+ heightKey?: string
19
29
  // hidden until the user asks for it, unless they have already chosen
20
30
  defaultOff?: boolean
21
31
  }
@@ -31,8 +41,8 @@ export interface BarTrackModel extends BasicTrackModel {
31
41
  barColor?: string
32
42
  }
33
43
 
34
- // a pair of columns joined by an arc, already resolved to 0-based visible
35
- // columns -- what the renderer consumes
44
+ // a pair of columns joined by an arc, resolved to 0-based visible columns for
45
+ // the renderer
36
46
  export interface Arc {
37
47
  start: number
38
48
  end: number
@@ -77,11 +87,106 @@ export interface ColumnTrackSpec {
77
87
  height?: number
78
88
  }
79
89
 
80
- // One contiguous run where a row's residues and a structure's line up 1:1.
81
- // Segment-shaped because SIFTS is: a dozen numbers cover what a per-residue
82
- // array would spend kilobytes on, and it makes the refusal rule structural
83
- // rather than a vocabulary -- a position no segment covers is unmapped, and
84
- // there is no status field to disagree with.
90
+ /**
91
+ * A strip panel: one cell per row, colored from a `rowData` field through
92
+ * `scale`, which is ggtree's `gheatmap`. `width` is in pixels and defaults to
93
+ * the row height, and `header` labels the column and defaults to the field.
94
+ * `legend` titles the strip's legend and defaults to the field, and strips
95
+ * naming one title share one legend.
96
+ */
97
+ export interface RowStripSpec {
98
+ kind: 'strip'
99
+ field: string
100
+ scale?: ScaleSpec
101
+ width?: number
102
+ header?: string
103
+ legend?: string
104
+ }
105
+
106
+ /** the channels a `features` panel reads off the feature table */
107
+ export interface FeatureEncodingSpec {
108
+ color?: { field: string; scale?: ScaleSpec }
109
+ label?: string
110
+ }
111
+
112
+ /**
113
+ * Shifts each row so that the first feature named `on` starts at zero, which
114
+ * is gggenes' `make_alignment_dummies`. Only `x: "position"` reads it.
115
+ */
116
+ export interface AlignTransform {
117
+ type: 'align'
118
+ on: string
119
+ }
120
+
121
+ /**
122
+ * A features panel: the GFF's spans per row, as arrows where they carry a
123
+ * strand. `x: "column"` draws them in the alignment's columns, and
124
+ * `x: "position"` in each row's own residue positions on one linear scale, so
125
+ * a genome with no alignment has an x.
126
+ */
127
+ export interface RowFeaturesSpec {
128
+ kind: 'features'
129
+ x: 'column' | 'position'
130
+ width?: number
131
+ header?: string
132
+ encoding?: FeatureEncodingSpec
133
+ transform?: AlignTransform[]
134
+ }
135
+
136
+ /**
137
+ * A panel left of the alignment on the row scale, the counterpart of
138
+ * ColumnTrackSpec on the column scale. See docs/layers.md
139
+ */
140
+ export type RowPanelSpec = RowStripSpec | RowFeaturesSpec
141
+
142
+ interface ResolvedPanelBase {
143
+ id: string
144
+ header: string
145
+ width: number
146
+ offsetX: number
147
+ legend: LegendEntry[]
148
+ /** the legend this panel's entries go under; absent means the domain key */
149
+ legendTitle?: string
150
+ }
151
+
152
+ /**
153
+ * A strip with its scale resolved: the color each row takes, keyed by row
154
+ * name, and the pixel column the panel draws in.
155
+ */
156
+ export interface ResolvedStripPanel extends ResolvedPanelBase {
157
+ kind: 'strip'
158
+ field: string
159
+ colors: Map<string, string>
160
+ }
161
+
162
+ /** one feature of a features panel, at its pixel span inside the panel */
163
+ export interface RowPanelSpan {
164
+ annotation: Annotation
165
+ xStart: number
166
+ xEnd: number
167
+ lane: number
168
+ laneCount: number
169
+ }
170
+
171
+ /**
172
+ * A features panel with its x mapping and its scale resolved: the spans per
173
+ * row name in panel pixels, the fill and outline of each feature, and the
174
+ * label its `label` channel draws. `field` is the field its color scale reads,
175
+ * which its legend is titled by.
176
+ */
177
+ export interface ResolvedFeaturePanel extends ResolvedPanelBase {
178
+ kind: 'features'
179
+ x: 'column' | 'position'
180
+ field?: string
181
+ spans: Map<string, RowPanelSpan[]>
182
+ colors: Map<Annotation, { fill: string; stroke: string }>
183
+ labels?: Map<Annotation, string>
184
+ }
185
+
186
+ export type ResolvedRowPanel = ResolvedStripPanel | ResolvedFeaturePanel
187
+
188
+ // One contiguous run where a row's residues and a structure's line up 1:1, as
189
+ // SIFTS reports them. A position no segment covers is unmapped.
85
190
  export interface ResidueSegment {
86
191
  rowStart: number
87
192
  rowEnd: number
@@ -90,8 +195,8 @@ export interface ResidueSegment {
90
195
  }
91
196
 
92
197
  // the structure half of a mapping. `id` is whatever the producer calls the
93
- // entry (a PDB id, an AlphaFold accession); `asymId` names the chain, which is
94
- // what distinguishes two mappings onto the same entry
198
+ // entry (a PDB id, an AlphaFold accession); `asymId` names the chain and
199
+ // distinguishes two mappings onto the same entry
95
200
  export interface MappedStructure {
96
201
  id: string
97
202
  kind?: 'experimental' | 'predicted'
@@ -100,21 +205,16 @@ export interface MappedStructure {
100
205
  }
101
206
 
102
207
  /**
103
- * Which residue of which structure a row's residue is, as data. Computed by
104
- * whatever knows how -- SIFTS, an AlphaFold model, a curator -- and carried in
105
- * the snapshot, because the viewer cannot work it out: matching a row to a
106
- * structure by sequence equality fails for a construct with an expression tag,
107
- * a truncation, an engineered residue, or a row that is a subsequence of the
108
- * entry, and it fails in the direction that looks like it worked.
208
+ * The structure residue for each residue of a row, computed outside the viewer
209
+ * (SIFTS, an AlphaFold model, a curator). Sequence equality misplaces tagged
210
+ * constructs, truncations and subsequence rows; see docs/layers.md.
109
211
  *
110
212
  * Positions are 1-based and inclusive on both sides, as GFF and `highlights`
111
213
  * are. Structure positions are `label_seq_id`, the index into the entity's
112
- * SEQRES; author numbering carries insertion codes and stays out.
214
+ * SEQRES; author numbering carries insertion codes and is not used.
113
215
  *
114
216
  * `unobserved` is in structure positions: present in SEQRES with no
115
- * coordinates. Worth distinguishing from unmapped, because "the
116
- * crystallographer could not see it" and "this protein does not have that
117
- * residue" mean different things to a reader.
217
+ * coordinates, as distinct from a residue the structure lacks.
118
218
  */
119
219
  export interface ResidueMapping {
120
220
  row: string
@@ -123,11 +223,9 @@ export interface ResidueMapping {
123
223
  segments: ResidueSegment[]
124
224
  unobserved?: [number, number][]
125
225
  /**
126
- * ungapped length of the row this was computed against. Optional, and worth
127
- * setting: a mapping outlives the alignment it was made for, and pointing at
128
- * residue 58 of a sequence that has since been re-aligned, revised or swapped
129
- * is the failure this whole layer exists to avoid. Declared here, the viewer
130
- * can notice and refuse instead of answering from stale arithmetic.
226
+ * ungapped length of the row this was computed against. When set, the viewer
227
+ * ignores the mapping if the loaded row's length differs, as it does after a
228
+ * re-alignment, revision or swapped sequence.
131
229
  */
132
230
  rowLength?: number
133
231
  generated?: {
@@ -137,25 +235,21 @@ export interface ResidueMapping {
137
235
  }
138
236
  }
139
237
 
140
- // why a mapping, or one segment of it, is not being used. Refusing silently
141
- // leaves a host unable to tell "no structure for this row" from "the mapping
142
- // no longer matches what is loaded", which are different problems with
143
- // different fixes.
238
+ // why a mapping, or one segment of it, is ignored, so a host can tell "no
239
+ // structure for this row" from "the mapping no longer matches what is loaded"
144
240
  export interface ResidueMappingProblem {
145
241
  row: string
146
242
  structureId: string
147
243
  /**
148
- * `mapping` means the whole mapping is out: the evidence is about the row it
149
- * claims, so nothing it says can be trusted. `segment` takes only that
150
- * segment, since the rest of the mapping still describes residues that exist.
244
+ * `mapping` ignores the whole mapping, because the problem concerns its row.
245
+ * `segment` ignores only that segment and keeps the rest.
151
246
  */
152
247
  scope: 'mapping' | 'segment'
153
248
  reason: string
154
249
  }
155
250
 
156
- // what a lookup gives back: the structure residue a row residue is, or the row
157
- // residue a structure residue is. `observed` is false for a residue the
158
- // structure declares but did not resolve.
251
+ // a lookup result in either direction. `observed` is false for a residue in
252
+ // SEQRES with no coordinates.
159
253
  export interface StructureResidue {
160
254
  structure: MappedStructure
161
255
  position: number
@@ -168,29 +262,50 @@ export interface RowResidue {
168
262
  }
169
263
 
170
264
  /**
171
- * One loaded document that the snapshot leaves behind, and how big it is.
172
- * `what` names it the way the import form does, since that is where the reader
173
- * would go to load it differently.
265
+ * One loaded document that the snapshot omits, and its size. `what` uses the
266
+ * import form's names for the documents.
174
267
  */
175
268
  export interface UnshareableData {
176
269
  what: 'alignment' | 'tree' | 'annotations' | 'row metadata' | 'data tracks'
177
270
  bytes: number
178
271
  }
179
272
 
180
- // the overlay annotation itself lives in msa-parsers, alongside the adapters
181
- // that build it. TidyDomainAnnotation is its former name, kept because
182
- // downstream plugins name it in their emitted declarations.
273
+ // Annotation is defined in msa-parsers. Downstream plugins' emitted
274
+ // declarations still name TidyDomainAnnotation.
183
275
  export type { Annotation }
184
276
  export type TidyDomainAnnotation = Annotation
185
277
 
186
278
  // an annotation resolved to the visible column span it is drawn across.
187
- // stackIndex is its position among the bands its row actually draws, which the
188
- // sub-row layout uses to stack boxes.
279
+ // stackIndex is its position in its row's paint order; lane is the sub-row it
280
+ // occupies, out of the laneCount the row's overlaps need.
189
281
  export interface DomainBand {
190
282
  annotation: Annotation
191
283
  startCol: number
192
284
  endCol: number
193
285
  stackIndex: number
286
+ lane: number
287
+ laneCount: number
288
+ }
289
+
290
+ /**
291
+ * One swatch of a categorical color key. `id` is the value the scale read, and
292
+ * `label` is the text drawn beside the swatch.
293
+ */
294
+ export interface LegendEntry {
295
+ id: string
296
+ label: string
297
+ color: string
298
+ }
299
+
300
+ /**
301
+ * A categorical color key, drawn by the on-screen overlay and reserved as a
302
+ * column in the SVG export. `title` names the legend where the viewer draws
303
+ * more than one.
304
+ */
305
+ export interface Legend {
306
+ id: string
307
+ title: string
308
+ entries: LegendEntry[]
194
309
  }
195
310
 
196
311
  /**
@@ -207,6 +322,104 @@ export interface Highlight {
207
322
  color?: string
208
323
  }
209
324
 
325
+ /**
326
+ * What the viewer draws over a clade. `highlight` fills the rows behind it,
327
+ * across the tree and the alignment. `bracket` draws a bar and the clade's
328
+ * `label` in the gutter right of the tip labels. `collapse` and `focus` seed
329
+ * the collapsed list and the subtree in focus at load.
330
+ */
331
+ export type CladeMark = 'highlight' | 'bracket' | 'collapse' | 'focus'
332
+
333
+ /**
334
+ * A clade of the tree and the mark drawn over it. `mrca` names tips whose most
335
+ * recent common ancestor is the clade; `range` names the first and last tip of
336
+ * a run in display order, in either order. `tips` is the leaf count the
337
+ * producer measured, and a clade that resolves to a different count is dropped.
338
+ * `label` names the clade beside a bracket, and a highlight carrying one draws
339
+ * the label without the bar. See docs/layers.md
340
+ */
341
+ export interface Clade {
342
+ mrca?: string[]
343
+ range?: [string, string]
344
+ tips: number
345
+ mark: CladeMark
346
+ color?: string
347
+ label?: string
348
+ }
349
+
350
+ /**
351
+ * A Clade resolved to the rows it covers, with its fill color settled. `color`
352
+ * is the highlight fill; `markColor` is the producer's own color for a bracket
353
+ * bar and its label, unset where the theme's text color stands. `nodeId` is the
354
+ * node an `mrca` resolved to, which `collapse` and `focus` seed themselves
355
+ * with, and a `range` record has none.
356
+ */
357
+ export interface ResolvedClade {
358
+ rows: [number, number]
359
+ mark: CladeMark
360
+ color: string
361
+ markColor?: string
362
+ label?: string
363
+ nodeId?: string
364
+ }
365
+
366
+ /**
367
+ * A span in Highlight coordinates: 1-based inclusive residues of `row`, or
368
+ * columns of the file without it
369
+ */
370
+ export interface Region {
371
+ row?: string
372
+ start: number
373
+ end: number
374
+ }
375
+
376
+ /**
377
+ * A cell of the alignment in the host's coordinates, 1-based like Highlight:
378
+ * `column` counts every column of the file, hidden gappy ones included, and
379
+ * `residue` counts the row's own letters, absent on a gap. A pointer over a
380
+ * track has a column and no row.
381
+ */
382
+ export interface Cell {
383
+ column: number
384
+ row?: string
385
+ residue?: number
386
+ letter?: string
387
+ }
388
+
389
+ /** the alignment columns on screen, 1-based and inclusive */
390
+ export interface Viewport {
391
+ startColumn: number
392
+ endColumn: number
393
+ }
394
+
395
+ /**
396
+ * A channel of a mark the viewer always draws. `tipLabel` colors each tip label
397
+ * in the tree, `rowTint` washes the row across the tree gutter and the
398
+ * alignment, and `branch` colors a tree edge whose tips all share one value;
399
+ * all three read a field of `rowData`. `featureFill` colors each span of the
400
+ * annotation overlay and `featureLabel` names the field drawn inside a span,
401
+ * both from a field of the feature table.
402
+ */
403
+ export type EncodingChannel =
404
+ | 'tipLabel'
405
+ | 'rowTint'
406
+ | 'branch'
407
+ | 'featureFill'
408
+ | 'featureLabel'
409
+
410
+ /**
411
+ * A channel, the field feeding it, and the scale it reads that field through.
412
+ * See docs/layers.md
413
+ */
414
+ export interface Encoding {
415
+ channel: EncodingChannel
416
+ field: string
417
+ scale?: ScaleSpec
418
+ }
419
+
420
+ /** an Encoding with its scale resolved against the values in the table */
421
+ export type ResolvedEncoding = Encoding & ResolvedScale
422
+
210
423
  /** a Highlight resolved to visible column indices and row indices */
211
424
  export interface ResolvedHighlight {
212
425
  startCol?: number
package/src/umd.ts CHANGED
@@ -1,9 +1,11 @@
1
1
  import * as React from 'react'
2
2
 
3
- // a host that swaps models (the R htmlwidget on re-render) has to destroy the
4
- // old one, or the disposers its autoruns registered never run
3
+ // a page that builds models with MSAModelF destroys each one it replaces, so
4
+ // the disposers its autoruns registered run
5
5
  export { destroy } from '@jbrowse/mobx-state-tree'
6
6
  export { default as MSAView } from './components/Loading.tsx'
7
+ export { mount } from './mount.tsx'
8
+ export { defineMsaElement } from './element.ts'
7
9
  export { type MsaViewModel, default as MSAModelF } from './model.ts'
8
10
 
9
11
  export * from 'react-dom/client'
@@ -6,17 +6,12 @@ import type React from 'react'
6
6
 
7
7
  /**
8
8
  * Wires a canvas ref to a mobx autorun: grabs the 2d context once the canvas
9
- * mounts and re-runs `draw` whenever any observable it reads changes. Collapses
10
- * the getContext/null-guard/autorun-dispose boilerplate repeated by every canvas
11
- * block host.
9
+ * mounts and re-runs `draw` whenever any observable it reads changes.
12
10
  *
13
- * `width`/`height` are the canvas *backing store* size the caller renders onto
14
- * the element (logical size times the high-res scale factor), and they are load
15
- * bearing rather than informational. Assigning either resets the canvas bitmap,
16
- * and React assigns them on every commit where they changed -- which lands
17
- * *after* the mobx reaction that already redrew at the old size. Without a redraw
18
- * on the far side of that commit the canvas is left blank: it is what blanked the
19
- * tree on a tree-area resize and the consensus tracks on a vertical zoom.
11
+ * `width`/`height` are the canvas backing store size (logical size times the
12
+ * high-res scale factor). Assigning either clears the bitmap, and React assigns
13
+ * them after the mobx reaction has drawn at the old size, so the effect redraws
14
+ * when they change.
20
15
  *
21
16
  * `deps` covers everything else the effect should re-subscribe on, matching a
22
17
  * normal useEffect dep array.
@@ -0,0 +1,72 @@
1
+ import { useEffect, useState } from 'react'
2
+
3
+ import { createJBrowseTheme } from '@jbrowse/core/ui/theme'
4
+ import { getSnapshot } from '@jbrowse/mobx-state-tree'
5
+ import { compareStructural, reaction } from 'mobx'
6
+
7
+ import { renderToSvg } from './renderToSvg.tsx'
8
+
9
+ import type { MsaViewModel } from './model.ts'
10
+ import type { Theme } from '@mui/material'
11
+
12
+ export interface MsaSvgFigure {
13
+ /** the SVG document for what is on screen, once the first draw finishes */
14
+ svg?: string
15
+ error?: unknown
16
+ }
17
+
18
+ /**
19
+ * The viewer's SVG export of its viewport, as markup for the page to insert.
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.
22
+ *
23
+ * The figure's row names and letters are SVG text, so the browser's find
24
+ * locates them, a reader can select them, and print stylesheets apply.
25
+ */
26
+ export function useMsaSvgFigure(
27
+ model: MsaViewModel,
28
+ {
29
+ theme,
30
+ includeTracks = false,
31
+ delay = 300,
32
+ }: { theme?: Theme; includeTracks?: boolean; delay?: number } = {},
33
+ ) {
34
+ const [figure, setFigure] = useState<MsaSvgFigure>({})
35
+ useEffect(() => {
36
+ let latest = 0
37
+ const dispose = reaction(
38
+ () =>
39
+ model.viewInitialized && model.dataInitialized
40
+ ? [getSnapshot(model), model.width, model.height]
41
+ : undefined,
42
+ key => {
43
+ if (!key) {
44
+ return
45
+ }
46
+ const draw = ++latest
47
+ renderToSvg(model, {
48
+ theme: theme ?? createJBrowseTheme(),
49
+ exportType: 'viewport',
50
+ includeTracks,
51
+ }).then(
52
+ svg => {
53
+ if (draw === latest) {
54
+ setFigure({ svg })
55
+ }
56
+ },
57
+ (error: unknown) => {
58
+ if (draw === latest) {
59
+ setFigure({ error })
60
+ }
61
+ },
62
+ )
63
+ },
64
+ { delay, fireImmediately: true, equals: compareStructural },
65
+ )
66
+ return () => {
67
+ latest = -1
68
+ dispose()
69
+ }
70
+ }, [model, theme, includeTracks, delay])
71
+ return figure
72
+ }
package/src/util.ts CHANGED
@@ -29,6 +29,18 @@ export function adjustColorForContrast(color: string, bg: string): string {
29
29
  return result
30
30
  }
31
31
 
32
+ // a wash over the alignment or the tree: a color carrying its own alpha draws
33
+ // at that alpha, and one with none takes `alpha` so the marks under it stay
34
+ // readable
35
+ export function withAlpha(color: string, alpha: number) {
36
+ const parsed = colord(color)
37
+ return parsed.alpha() === 1 ? parsed.alpha(alpha).toRgbString() : color
38
+ }
39
+
40
+ export function outlineColor(fill: string) {
41
+ return colord(fill).darken(0.1).toHex()
42
+ }
43
+
32
44
  export function transform<T>(
33
45
  obj: Record<string, T>,
34
46
  cb: (arg0: [string, T]) => [string, T],
@@ -41,12 +53,9 @@ const contrastTextCaches = new WeakMap<Theme, Map<string, string>>()
41
53
  /**
42
54
  * Readable letter color over a given cell color, memoized per theme.
43
55
  *
44
- * A dynamic scheme colors a cell from its column, so there is no letter->color
45
- * table to precompute a contrast table from; asking per cell color covers the
46
- * dynamic schemes, the static ones and a track's own `colors` alike. An
47
- * uncolored cell sits on the theme's background, where the theme's own text
48
- * color is what stays readable -- `black` turned every gap and every uncolored
49
- * cell invisible in the dark theme.
56
+ * Keyed by cell color, since a dynamic scheme has no letter->color table; this
57
+ * covers static schemes and a track's own `colors` too. An uncolored cell gets
58
+ * the theme's text color, which stays readable in the dark theme.
50
59
  */
51
60
  export function contrastTextFn(theme: Theme) {
52
61
  let cache = contrastTextCaches.get(theme)