react-msaview 9.0.0 → 9.1.1

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 (352) hide show
  1. package/README.md +19 -10
  2. package/bundle/index.js +111 -115
  3. package/bundle/index.js.map +4 -4
  4. package/dist/blosum62.d.ts +3 -0
  5. package/dist/blosum62.js +43 -0
  6. package/dist/blosum62.js.map +1 -0
  7. package/dist/colorSchemes.js +2 -3
  8. package/dist/colorSchemes.js.map +1 -1
  9. package/dist/columnCounts.d.ts +0 -1
  10. package/dist/columnCounts.js +1 -1
  11. package/dist/columnCounts.js.map +1 -1
  12. package/dist/components/Loading.d.ts +3 -2
  13. package/dist/components/Loading.js +7 -7
  14. package/dist/components/Loading.js.map +1 -1
  15. package/dist/components/MSAView.d.ts +3 -2
  16. package/dist/components/MSAView.js +7 -4
  17. package/dist/components/MSAView.js.map +1 -1
  18. package/dist/components/MSAViewer.d.ts +1941 -4
  19. package/dist/components/MSAViewer.js +144 -201
  20. package/dist/components/MSAViewer.js.map +1 -1
  21. package/dist/components/MetadataTable.d.ts +4 -1
  22. package/dist/components/MetadataTable.js +55 -16
  23. package/dist/components/MetadataTable.js.map +1 -1
  24. package/dist/components/ResizeHandles.js +3 -2
  25. package/dist/components/ResizeHandles.js.map +1 -1
  26. package/dist/components/Track.js +8 -1
  27. package/dist/components/Track.js.map +1 -1
  28. package/dist/components/controls.d.ts +11 -0
  29. package/dist/components/controls.js +49 -0
  30. package/dist/components/controls.js.map +1 -0
  31. package/dist/components/dialogs/AnnotationFileDialog.js +12 -18
  32. package/dist/components/dialogs/AnnotationFileDialog.js.map +1 -1
  33. package/dist/components/dialogs/ExportSVGDialog.js +5 -7
  34. package/dist/components/dialogs/ExportSVGDialog.js.map +1 -1
  35. package/dist/components/dialogs/FeatureDialog.js +2 -2
  36. package/dist/components/dialogs/FeatureDialog.js.map +1 -1
  37. package/dist/components/{header/TreeSettingsMenu.d.ts → dialogs/GapThresholdDialog.d.ts} +3 -2
  38. package/dist/components/dialogs/GapThresholdDialog.js +15 -0
  39. package/dist/components/dialogs/GapThresholdDialog.js.map +1 -0
  40. package/dist/components/{header → dialogs}/GappynessSlider.d.ts +1 -1
  41. package/dist/components/{header → dialogs}/GappynessSlider.js +3 -10
  42. package/dist/components/dialogs/GappynessSlider.js.map +1 -0
  43. package/dist/components/{header → dialogs}/GoToBox.d.ts +4 -2
  44. package/dist/components/dialogs/GoToBox.js +48 -0
  45. package/dist/components/dialogs/GoToBox.js.map +1 -0
  46. package/dist/components/dialogs/GoToDialog.d.ts +6 -0
  47. package/dist/components/dialogs/GoToDialog.js +10 -0
  48. package/dist/components/dialogs/GoToDialog.js.map +1 -0
  49. package/dist/components/dialogs/HelpDialog.d.ts +6 -0
  50. package/dist/components/dialogs/HelpDialog.js +30 -0
  51. package/dist/components/dialogs/HelpDialog.js.map +1 -0
  52. package/dist/components/dialogs/MetadataDialog.js +11 -3
  53. package/dist/components/dialogs/MetadataDialog.js.map +1 -1
  54. package/dist/components/dialogs/goTo.d.ts +5 -0
  55. package/dist/components/{header → dialogs}/goTo.js +1 -1
  56. package/dist/components/dialogs/goTo.js.map +1 -0
  57. package/dist/components/dialogs/metadataLabels.d.ts +4 -0
  58. package/dist/components/dialogs/metadataLabels.js +52 -0
  59. package/dist/components/dialogs/metadataLabels.js.map +1 -0
  60. package/dist/components/dialogs/metadataRecord.d.ts +1 -0
  61. package/dist/components/dialogs/metadataRecord.js +38 -0
  62. package/dist/components/dialogs/metadataRecord.js.map +1 -0
  63. package/dist/components/header/Header.d.ts +6 -2
  64. package/dist/components/header/Header.js +30 -34
  65. package/dist/components/header/Header.js.map +1 -1
  66. package/dist/components/header/HiddenColumnsChip.d.ts +6 -0
  67. package/dist/components/header/HiddenColumnsChip.js +15 -0
  68. package/dist/components/header/HiddenColumnsChip.js.map +1 -0
  69. package/dist/components/header/{FileMenu.d.ts → MenuBar.d.ts} +3 -3
  70. package/dist/components/header/MenuBar.js +33 -0
  71. package/dist/components/header/MenuBar.js.map +1 -0
  72. package/dist/components/header/ScrollZoomCheckbox.d.ts +7 -0
  73. package/dist/components/header/ScrollZoomCheckbox.js +19 -0
  74. package/dist/components/header/ScrollZoomCheckbox.js.map +1 -0
  75. package/dist/components/header/ZoomControls.js +12 -48
  76. package/dist/components/header/ZoomControls.js.map +1 -1
  77. package/dist/components/header/colorSchemeMenuItems.d.ts +4 -0
  78. package/dist/components/header/colorSchemeMenuItems.js +106 -0
  79. package/dist/components/header/colorSchemeMenuItems.js.map +1 -0
  80. package/dist/components/header/getAnnotationsMenu.d.ts +3 -30
  81. package/dist/components/header/getAnnotationsMenu.js +48 -54
  82. package/dist/components/header/getAnnotationsMenu.js.map +1 -1
  83. package/dist/components/header/menuBarItems.d.ts +11 -0
  84. package/dist/components/header/menuBarItems.js +277 -0
  85. package/dist/components/header/menuBarItems.js.map +1 -0
  86. package/dist/components/import/ImportForm.js +12 -6
  87. package/dist/components/import/ImportForm.js.map +1 -1
  88. package/dist/components/import/data/seq2.d.ts +0 -1
  89. package/dist/components/import/data/seq2.js +0 -17
  90. package/dist/components/import/data/seq2.js.map +1 -1
  91. package/dist/components/minimap/Minimap.js +7 -3
  92. package/dist/components/minimap/Minimap.js.map +1 -1
  93. package/dist/components/minimap/MinimapSVG.js +2 -1
  94. package/dist/components/minimap/MinimapSVG.js.map +1 -1
  95. package/dist/components/minimap/minimapLayout.d.ts +12 -0
  96. package/dist/components/minimap/minimapLayout.js +20 -0
  97. package/dist/components/minimap/minimapLayout.js.map +1 -1
  98. package/dist/components/msa/MSACanvas.js +28 -10
  99. package/dist/components/msa/MSACanvas.js.map +1 -1
  100. package/dist/components/msa/MSACanvasBlock.js +17 -16
  101. package/dist/components/msa/MSACanvasBlock.js.map +1 -1
  102. package/dist/components/msa/MSAMouseoverCanvas.js +2 -2
  103. package/dist/components/msa/MSAMouseoverCanvas.js.map +1 -1
  104. package/dist/components/msa/WheelZoomHint.d.ts +7 -0
  105. package/dist/components/msa/WheelZoomHint.js +38 -0
  106. package/dist/components/msa/WheelZoomHint.js.map +1 -0
  107. package/dist/components/msa/drawFeatureSpans.d.ts +50 -11
  108. package/dist/components/msa/drawFeatureSpans.js +99 -56
  109. package/dist/components/msa/drawFeatureSpans.js.map +1 -1
  110. package/dist/components/msa/msaKeys.js +3 -0
  111. package/dist/components/msa/msaKeys.js.map +1 -1
  112. package/dist/components/msa/msaRaster.d.ts +14 -2
  113. package/dist/components/msa/msaRaster.js +20 -10
  114. package/dist/components/msa/msaRaster.js.map +1 -1
  115. package/dist/components/msa/renderBoxFeatureCanvasBlock.d.ts +9 -0
  116. package/dist/components/msa/renderBoxFeatureCanvasBlock.js +39 -8
  117. package/dist/components/msa/renderBoxFeatureCanvasBlock.js.map +1 -1
  118. package/dist/components/msa/renderHighlights.d.ts +2 -5
  119. package/dist/components/msa/renderHighlights.js +5 -6
  120. package/dist/components/msa/renderHighlights.js.map +1 -1
  121. package/dist/components/msa/renderMSABlock.d.ts +6 -1
  122. package/dist/components/msa/renderMSABlock.js +44 -17
  123. package/dist/components/msa/renderMSABlock.js.map +1 -1
  124. package/dist/components/msa/renderMSAMouseover.js +2 -1
  125. package/dist/components/msa/renderMSAMouseover.js.map +1 -1
  126. package/dist/components/msa/tileColor.js +7 -0
  127. package/dist/components/msa/tileColor.js.map +1 -1
  128. package/dist/components/renderCtx.d.ts +1 -1
  129. package/dist/components/rowpanels/RowPanelBlock.js +2 -2
  130. package/dist/components/rowpanels/RowPanelBlock.js.map +1 -1
  131. package/dist/components/rowpanels/renderFeaturePanel.d.ts +5 -1
  132. package/dist/components/rowpanels/renderFeaturePanel.js +12 -5
  133. package/dist/components/rowpanels/renderFeaturePanel.js.map +1 -1
  134. package/dist/components/settings/MenuItemsForm.d.ts +10 -0
  135. package/dist/components/settings/MenuItemsForm.js +80 -0
  136. package/dist/components/settings/MenuItemsForm.js.map +1 -0
  137. package/dist/components/settings/SettingsPanel.d.ts +12 -0
  138. package/dist/components/settings/SettingsPanel.js +92 -0
  139. package/dist/components/settings/SettingsPanel.js.map +1 -0
  140. package/dist/components/{header/ZoomMenu.d.ts → settings/TrackList.d.ts} +2 -2
  141. package/dist/components/settings/TrackList.js +55 -0
  142. package/dist/components/settings/TrackList.js.map +1 -0
  143. package/dist/components/tracks/TrackBlocks.js +2 -2
  144. package/dist/components/tracks/TrackBlocks.js.map +1 -1
  145. package/dist/components/tracks/drawTracks.d.ts +1 -53
  146. package/dist/components/tracks/drawTracks.js +5 -5
  147. package/dist/components/tracks/drawTracks.js.map +1 -1
  148. package/dist/components/tree/CladeLabels.d.ts +1 -1
  149. package/dist/components/tree/CladeLabels.js +22 -34
  150. package/dist/components/tree/CladeLabels.js.map +1 -1
  151. package/dist/components/tree/TreeCanvasBlock.js +2 -2
  152. package/dist/components/tree/TreeCanvasBlock.js.map +1 -1
  153. package/dist/components/tree/TreeMenu.js +4 -6
  154. package/dist/components/tree/TreeMenu.js.map +1 -1
  155. package/dist/components/tree/TreeOverview.js +2 -2
  156. package/dist/components/tree/TreeOverview.js.map +1 -1
  157. package/dist/components/tree/cladeBrackets.d.ts +17 -10
  158. package/dist/components/tree/cladeBrackets.js +33 -3
  159. package/dist/components/tree/cladeBrackets.js.map +1 -1
  160. package/dist/components/tree/renderTreeCanvas.js +2 -8
  161. package/dist/components/tree/renderTreeCanvas.js.map +1 -1
  162. package/dist/components/tree/renderTreeOverview.js +4 -11
  163. package/dist/components/tree/renderTreeOverview.js.map +1 -1
  164. package/dist/constants.d.ts +4 -1
  165. package/dist/constants.js +7 -1
  166. package/dist/constants.js.map +1 -1
  167. package/dist/darkReader.d.ts +7 -0
  168. package/dist/darkReader.js +42 -0
  169. package/dist/darkReader.js.map +1 -0
  170. package/dist/element.js +25 -53
  171. package/dist/element.js.map +1 -1
  172. package/dist/exportLayout.d.ts +39 -0
  173. package/dist/exportLayout.js +78 -0
  174. package/dist/exportLayout.js.map +1 -0
  175. package/dist/featurePanels.d.ts +37 -0
  176. package/dist/featurePanels.js +187 -0
  177. package/dist/featurePanels.js.map +1 -0
  178. package/dist/filehandleLoader.d.ts +41 -0
  179. package/dist/filehandleLoader.js +99 -0
  180. package/dist/filehandleLoader.js.map +1 -0
  181. package/dist/headlessRenderEnv.d.ts +12 -39
  182. package/dist/headlessRenderEnv.js +18 -12
  183. package/dist/headlessRenderEnv.js.map +1 -1
  184. package/dist/hierarchy.d.ts +5 -8
  185. package/dist/hierarchy.js +5 -79
  186. package/dist/hierarchy.js.map +1 -1
  187. package/dist/index.d.ts +6 -1
  188. package/dist/index.js +6 -1
  189. package/dist/index.js.map +1 -1
  190. package/dist/measureTextCanvas.d.ts +7 -0
  191. package/dist/measureTextCanvas.js +8 -6
  192. package/dist/measureTextCanvas.js.map +1 -1
  193. package/dist/model/treeModel.d.ts +6 -2
  194. package/dist/model/treeModel.js +8 -4
  195. package/dist/model/treeModel.js.map +1 -1
  196. package/dist/model.d.ts +180 -76
  197. package/dist/model.js +358 -618
  198. package/dist/model.js.map +1 -1
  199. package/dist/neighborJoining.js +1 -40
  200. package/dist/neighborJoining.js.map +1 -1
  201. package/dist/renderToSvg.d.ts +2 -7
  202. package/dist/renderToSvg.js +33 -133
  203. package/dist/renderToSvg.js.map +1 -1
  204. package/dist/rerootTree.d.ts +1 -0
  205. package/dist/rerootTree.js +7 -6
  206. package/dist/rerootTree.js.map +1 -1
  207. package/dist/schemeResidueColor.d.ts +12 -0
  208. package/dist/schemeResidueColor.js +33 -0
  209. package/dist/schemeResidueColor.js.map +1 -0
  210. package/dist/similarityColors.d.ts +11 -0
  211. package/dist/similarityColors.js +90 -0
  212. package/dist/similarityColors.js.map +1 -0
  213. package/dist/svgTestUtil.d.ts +74 -32
  214. package/dist/treeDisplay.d.ts +37 -0
  215. package/dist/treeDisplay.js +209 -0
  216. package/dist/treeDisplay.js.map +1 -0
  217. package/dist/types.d.ts +6 -3
  218. package/dist/useWheelScroll.d.ts +3 -1
  219. package/dist/useWheelScroll.js +36 -17
  220. package/dist/useWheelScroll.js.map +1 -1
  221. package/dist/util.d.ts +2 -0
  222. package/dist/util.js +2 -0
  223. package/dist/util.js.map +1 -1
  224. package/dist/version.d.ts +1 -1
  225. package/dist/version.js +1 -1
  226. package/dist/viewerSnapshot.d.ts +169 -0
  227. package/dist/viewerSnapshot.js +100 -0
  228. package/dist/viewerSnapshot.js.map +1 -0
  229. package/dist/wheelZoom.d.ts +8 -3
  230. package/dist/wheelZoom.js +44 -32
  231. package/dist/wheelZoom.js.map +1 -1
  232. package/dist/zoomSnapshot.d.ts +29 -0
  233. package/dist/zoomSnapshot.js +47 -0
  234. package/dist/zoomSnapshot.js.map +1 -0
  235. package/package.json +11 -11
  236. package/src/blosum62.ts +45 -0
  237. package/src/colorSchemes.ts +2 -4
  238. package/src/columnCounts.ts +1 -1
  239. package/src/components/Loading.tsx +25 -14
  240. package/src/components/MSAView.tsx +22 -5
  241. package/src/components/MSAViewer.tsx +275 -251
  242. package/src/components/MetadataTable.tsx +83 -26
  243. package/src/components/ResizeHandles.tsx +9 -2
  244. package/src/components/Track.tsx +15 -1
  245. package/src/components/controls.ts +62 -0
  246. package/src/components/dialogs/AnnotationFileDialog.tsx +16 -21
  247. package/src/components/dialogs/ExportSVGDialog.tsx +8 -19
  248. package/src/components/dialogs/FeatureDialog.tsx +2 -5
  249. package/src/components/dialogs/GapThresholdDialog.tsx +36 -0
  250. package/src/components/{header → dialogs}/GappynessSlider.tsx +3 -10
  251. package/src/components/{header → dialogs}/GoToBox.tsx +36 -10
  252. package/src/components/dialogs/GoToDialog.tsx +30 -0
  253. package/src/components/dialogs/HelpDialog.tsx +79 -0
  254. package/src/components/dialogs/MetadataDialog.tsx +32 -4
  255. package/src/components/{header → dialogs}/goTo.ts +1 -1
  256. package/src/components/dialogs/metadataLabels.ts +54 -0
  257. package/src/components/dialogs/metadataRecord.tsx +61 -0
  258. package/src/components/header/Header.tsx +75 -44
  259. package/src/components/header/HiddenColumnsChip.tsx +39 -0
  260. package/src/components/header/MenuBar.tsx +64 -0
  261. package/src/components/header/ScrollZoomCheckbox.tsx +46 -0
  262. package/src/components/header/ZoomControls.tsx +23 -74
  263. package/src/components/header/colorSchemeMenuItems.ts +121 -0
  264. package/src/components/header/getAnnotationsMenu.ts +52 -56
  265. package/src/components/header/menuBarItems.ts +357 -0
  266. package/src/components/import/ImportForm.tsx +19 -7
  267. package/src/components/import/data/seq2.ts +0 -18
  268. package/src/components/minimap/Minimap.tsx +11 -3
  269. package/src/components/minimap/MinimapSVG.tsx +17 -1
  270. package/src/components/minimap/minimapLayout.ts +27 -0
  271. package/src/components/msa/MSACanvas.tsx +24 -4
  272. package/src/components/msa/MSACanvasBlock.tsx +20 -17
  273. package/src/components/msa/MSAMouseoverCanvas.tsx +2 -2
  274. package/src/components/msa/WheelZoomHint.tsx +56 -0
  275. package/src/components/msa/drawFeatureSpans.ts +160 -79
  276. package/src/components/msa/msaKeys.ts +3 -0
  277. package/src/components/msa/msaRaster.ts +26 -10
  278. package/src/components/msa/renderBoxFeatureCanvasBlock.ts +62 -24
  279. package/src/components/msa/renderHighlights.ts +5 -6
  280. package/src/components/msa/renderMSABlock.ts +51 -15
  281. package/src/components/msa/renderMSAMouseover.ts +5 -1
  282. package/src/components/msa/tileColor.ts +7 -0
  283. package/src/components/renderCtx.ts +0 -1
  284. package/src/components/rowpanels/RowPanelBlock.tsx +2 -2
  285. package/src/components/rowpanels/renderFeaturePanel.ts +26 -7
  286. package/src/components/settings/MenuItemsForm.tsx +225 -0
  287. package/src/components/settings/SettingsPanel.tsx +156 -0
  288. package/src/components/settings/TrackList.tsx +119 -0
  289. package/src/components/tracks/TrackBlocks.tsx +2 -2
  290. package/src/components/tracks/drawTracks.ts +5 -5
  291. package/src/components/tree/CladeLabels.tsx +30 -54
  292. package/src/components/tree/TreeCanvasBlock.tsx +2 -2
  293. package/src/components/tree/TreeMenu.tsx +4 -6
  294. package/src/components/tree/TreeOverview.tsx +2 -2
  295. package/src/components/tree/cladeBrackets.ts +50 -3
  296. package/src/components/tree/renderTreeCanvas.ts +2 -13
  297. package/src/components/tree/renderTreeOverview.ts +7 -14
  298. package/src/constants.ts +9 -1
  299. package/src/darkReader.ts +57 -0
  300. package/src/element.ts +37 -64
  301. package/src/exportLayout.ts +134 -0
  302. package/src/featurePanels.ts +269 -0
  303. package/src/filehandleLoader.ts +136 -0
  304. package/src/headlessRenderEnv.ts +24 -11
  305. package/src/hierarchy.ts +5 -88
  306. package/src/index.ts +9 -5
  307. package/src/measureTextCanvas.ts +8 -6
  308. package/src/model/treeModel.ts +9 -3
  309. package/src/model.ts +452 -807
  310. package/src/neighborJoining.ts +1 -42
  311. package/src/renderToSvg.tsx +56 -207
  312. package/src/rerootTree.ts +7 -6
  313. package/src/schemeResidueColor.ts +37 -0
  314. package/src/similarityColors.ts +112 -0
  315. package/src/treeDisplay.ts +278 -0
  316. package/src/types.ts +6 -4
  317. package/src/useWheelScroll.ts +43 -22
  318. package/src/util.ts +3 -0
  319. package/src/version.ts +1 -1
  320. package/src/viewerSnapshot.ts +147 -0
  321. package/src/wheelZoom.ts +55 -33
  322. package/src/zoomSnapshot.ts +80 -0
  323. package/dist/components/header/ColorSchemeMenu.d.ts +0 -12
  324. package/dist/components/header/ColorSchemeMenu.js +0 -37
  325. package/dist/components/header/ColorSchemeMenu.js.map +0 -1
  326. package/dist/components/header/FileMenu.js +0 -66
  327. package/dist/components/header/FileMenu.js.map +0 -1
  328. package/dist/components/header/GappynessSlider.js.map +0 -1
  329. package/dist/components/header/GoToBox.js +0 -31
  330. package/dist/components/header/GoToBox.js.map +0 -1
  331. package/dist/components/header/MSASettingsMenu.d.ts +0 -6
  332. package/dist/components/header/MSASettingsMenu.js +0 -11
  333. package/dist/components/header/MSASettingsMenu.js.map +0 -1
  334. package/dist/components/header/ScrollZoomIcons.d.ts +0 -6
  335. package/dist/components/header/ScrollZoomIcons.js +0 -35
  336. package/dist/components/header/ScrollZoomIcons.js.map +0 -1
  337. package/dist/components/header/TreeSettingsMenu.js +0 -42
  338. package/dist/components/header/TreeSettingsMenu.js.map +0 -1
  339. package/dist/components/header/ZoomMenu.js +0 -37
  340. package/dist/components/header/ZoomMenu.js.map +0 -1
  341. package/dist/components/header/goTo.d.ts +0 -20
  342. package/dist/components/header/goTo.js.map +0 -1
  343. package/dist/components/header/settingsMenuItems.d.ts +0 -4
  344. package/dist/components/header/settingsMenuItems.js +0 -127
  345. package/dist/components/header/settingsMenuItems.js.map +0 -1
  346. package/src/components/header/ColorSchemeMenu.tsx +0 -56
  347. package/src/components/header/FileMenu.tsx +0 -81
  348. package/src/components/header/MSASettingsMenu.tsx +0 -26
  349. package/src/components/header/ScrollZoomIcons.tsx +0 -52
  350. package/src/components/header/TreeSettingsMenu.tsx +0 -60
  351. package/src/components/header/ZoomMenu.tsx +0 -48
  352. package/src/components/header/settingsMenuItems.ts +0 -157
@@ -0,0 +1,225 @@
1
+ import React, { useState } from 'react'
2
+
3
+ import ArrowDropDown from '@mui/icons-material/ArrowDropDown'
4
+ import ArrowRight from '@mui/icons-material/ArrowRight'
5
+ import {
6
+ Box,
7
+ Button,
8
+ Checkbox,
9
+ FormControlLabel,
10
+ MenuItem as MuiMenuItem,
11
+ Radio,
12
+ TextField,
13
+ ToggleButton,
14
+ ToggleButtonGroup,
15
+ Typography,
16
+ } from '@mui/material'
17
+
18
+ import type { MenuItem } from '@jbrowse/core/ui/Menu'
19
+
20
+ type RadioMenuItem = Extract<MenuItem, { type: 'radio' }>
21
+
22
+ /**
23
+ * A submenu of radios may name a compact control to render as: `segmented`
24
+ * for a few short choices, `select` for a longer list
25
+ */
26
+ export type ChoiceDisplay = 'segmented' | 'select'
27
+
28
+ function radios(items: MenuItem[]) {
29
+ return items.filter(
30
+ (i): i is RadioMenuItem => 'type' in i && i.type === 'radio',
31
+ )
32
+ }
33
+
34
+ function Caption({ children }: { children: React.ReactNode }) {
35
+ return (
36
+ <Typography
37
+ variant="caption"
38
+ color="text.secondary"
39
+ sx={{ mt: 1, display: 'block' }}
40
+ >
41
+ {children}
42
+ </Typography>
43
+ )
44
+ }
45
+
46
+ function Segmented({
47
+ label,
48
+ options,
49
+ }: {
50
+ label: string
51
+ options: RadioMenuItem[]
52
+ }) {
53
+ const value = options.findIndex(o => o.checked)
54
+ return (
55
+ <>
56
+ <Caption>{label}</Caption>
57
+ <ToggleButtonGroup
58
+ exclusive
59
+ fullWidth
60
+ size="small"
61
+ value={value}
62
+ aria-label={label}
63
+ onChange={(_event, i: number | null) => {
64
+ if (i !== null) {
65
+ options[i]?.onClick()
66
+ }
67
+ }}
68
+ >
69
+ {options.map((o, i) => (
70
+ <ToggleButton
71
+ key={String(o.label)}
72
+ value={i}
73
+ disabled={o.disabled}
74
+ title={o.helpText}
75
+ sx={{ textTransform: 'none', py: 0.25 }}
76
+ >
77
+ {o.label}
78
+ </ToggleButton>
79
+ ))}
80
+ </ToggleButtonGroup>
81
+ </>
82
+ )
83
+ }
84
+
85
+ // each option fires its own onClick, so picking the checked one again still
86
+ // runs, which reopens a dialog like the custom gap threshold
87
+ function Select({
88
+ label,
89
+ options,
90
+ }: {
91
+ label: string
92
+ options: RadioMenuItem[]
93
+ }) {
94
+ const value = options.findIndex(o => o.checked)
95
+ return (
96
+ <TextField
97
+ select
98
+ size="small"
99
+ fullWidth
100
+ label={label}
101
+ value={value === -1 ? '' : value}
102
+ sx={{ mt: 1.5, '& .MuiInputBase-root': { typography: 'body2' } }}
103
+ >
104
+ {options.map((o, i) => (
105
+ <MuiMenuItem
106
+ key={String(o.label)}
107
+ value={i}
108
+ disabled={o.disabled}
109
+ dense
110
+ onClick={() => {
111
+ o.onClick()
112
+ }}
113
+ >
114
+ {o.label}
115
+ </MuiMenuItem>
116
+ ))}
117
+ </TextField>
118
+ )
119
+ }
120
+
121
+ // a short submenu opens in place under a caption, where every choice shows at
122
+ // once; a long one, like the extra color schemes, folds behind a toggle
123
+ const inlineUpTo = 8
124
+
125
+ function SubSection({ item }: { item: MenuItem }) {
126
+ const [open, setOpen] = useState(false)
127
+ if (!('subMenu' in item) || !Array.isArray(item.subMenu)) {
128
+ return null
129
+ }
130
+ const label = String(item.label)
131
+ const display =
132
+ 'display' in item ? (item.display as ChoiceDisplay) : undefined
133
+ if (display) {
134
+ const options = radios(item.subMenu)
135
+ const rest = item.subMenu.filter(i => !options.includes(i as RadioMenuItem))
136
+ const Control = display === 'segmented' ? Segmented : Select
137
+ return (
138
+ <>
139
+ <Control label={label} options={options} />
140
+ <MenuItemsForm items={rest} />
141
+ </>
142
+ )
143
+ }
144
+ if (item.subMenu.length <= inlineUpTo) {
145
+ return (
146
+ <>
147
+ <Caption>{label}</Caption>
148
+ <MenuItemsForm items={item.subMenu} />
149
+ </>
150
+ )
151
+ }
152
+ return (
153
+ <>
154
+ <Button
155
+ size="small"
156
+ color="inherit"
157
+ startIcon={open ? <ArrowDropDown /> : <ArrowRight />}
158
+ onClick={() => {
159
+ setOpen(!open)
160
+ }}
161
+ sx={{ textTransform: 'none', justifyContent: 'flex-start', px: 0.5 }}
162
+ >
163
+ {label}
164
+ </Button>
165
+ {open ? (
166
+ <Box sx={{ pl: 2 }}>
167
+ <MenuItemsForm items={item.subMenu} />
168
+ </Box>
169
+ ) : null}
170
+ </>
171
+ )
172
+ }
173
+
174
+ export default function MenuItemsForm({ items }: { items: MenuItem[] }) {
175
+ return (
176
+ <Box sx={{ display: 'flex', flexDirection: 'column' }}>
177
+ {items.map((item, i) => {
178
+ const key = 'label' in item ? String(item.label) : String(i)
179
+ if (!('label' in item)) {
180
+ return null
181
+ }
182
+ if ('type' in item && item.type === 'subHeader') {
183
+ return <Caption key={key}>{item.label}</Caption>
184
+ }
185
+ if ('subMenu' in item) {
186
+ return <SubSection key={key} item={item} />
187
+ }
188
+ const onClick = 'onClick' in item ? item.onClick : undefined
189
+ if (
190
+ 'type' in item &&
191
+ (item.type === 'checkbox' || item.type === 'radio')
192
+ ) {
193
+ const Control = item.type === 'checkbox' ? Checkbox : Radio
194
+ return (
195
+ <FormControlLabel
196
+ key={key}
197
+ disabled={item.disabled}
198
+ control={
199
+ <Control
200
+ size="small"
201
+ checked={item.checked}
202
+ onChange={() => onClick?.()}
203
+ sx={{ py: 0.25 }}
204
+ />
205
+ }
206
+ label={item.label}
207
+ slotProps={{ typography: { variant: 'body2' } }}
208
+ />
209
+ )
210
+ }
211
+ return (
212
+ <Button
213
+ key={key}
214
+ size="small"
215
+ disabled={item.disabled}
216
+ onClick={() => onClick?.()}
217
+ sx={{ alignSelf: 'flex-start', textTransform: 'none' }}
218
+ >
219
+ {item.label}
220
+ </Button>
221
+ )
222
+ })}
223
+ </Box>
224
+ )
225
+ }
@@ -0,0 +1,156 @@
1
+ import React, { useState } from 'react'
2
+
3
+ import ArrowDropDown from '@mui/icons-material/ArrowDropDown'
4
+ import ArrowRight from '@mui/icons-material/ArrowRight'
5
+ import Close from '@mui/icons-material/Close'
6
+ import { Box, ButtonBase, IconButton, Typography } from '@mui/material'
7
+ import { observer } from 'mobx-react'
8
+
9
+ import { settingsPanelWidth } from '../../constants.ts'
10
+ import { annotationsSubMenu } from '../header/getAnnotationsMenu.ts'
11
+ import {
12
+ alignmentSettingsItems,
13
+ colorSettingsItems,
14
+ gapsSubMenu,
15
+ trackSettingsItems,
16
+ treeSubMenu,
17
+ } from '../header/menuBarItems.ts'
18
+ import MenuItemsForm from './MenuItemsForm.tsx'
19
+ import TrackList from './TrackList.tsx'
20
+
21
+ import type { MsaViewModel } from '../../model.ts'
22
+
23
+ const collapsedKey = 'msaview-settings-collapsed'
24
+
25
+ function readCollapsed() {
26
+ try {
27
+ const parsed: unknown = JSON.parse(
28
+ localStorage.getItem(collapsedKey) ?? '[]',
29
+ )
30
+ return new Set(Array.isArray(parsed) ? parsed.map(String) : [])
31
+ } catch {
32
+ return new Set<string>()
33
+ }
34
+ }
35
+
36
+ function writeCollapsed(collapsed: Set<string>) {
37
+ try {
38
+ localStorage.setItem(collapsedKey, JSON.stringify([...collapsed]))
39
+ } catch {}
40
+ }
41
+
42
+ function Section({
43
+ title,
44
+ collapsed,
45
+ onToggle,
46
+ children,
47
+ }: {
48
+ title: string
49
+ collapsed: boolean
50
+ onToggle: () => void
51
+ children: React.ReactNode
52
+ }) {
53
+ const Arrow = collapsed ? ArrowRight : ArrowDropDown
54
+ return (
55
+ <Box sx={{ px: 1.5, py: 1, borderBottom: 1, borderColor: 'divider' }}>
56
+ <ButtonBase
57
+ aria-expanded={!collapsed}
58
+ onClick={onToggle}
59
+ sx={{ width: '100%', justifyContent: 'flex-start', ml: -0.75 }}
60
+ >
61
+ <Arrow fontSize="small" color="action" />
62
+ <Typography variant="overline" color="text.secondary">
63
+ {title}
64
+ </Typography>
65
+ </ButtonBase>
66
+ {collapsed ? null : children}
67
+ </Box>
68
+ )
69
+ }
70
+
71
+ /**
72
+ * The settings whose effect the user watches as they change them, docked to
73
+ * the right of the alignment so nothing it opens covers the tree or the rows.
74
+ * Each section folds, and the browser remembers which ones the user folded.
75
+ */
76
+ const SettingsPanel = observer(function ({
77
+ model,
78
+ height,
79
+ }: {
80
+ model: MsaViewModel
81
+ height: number
82
+ }) {
83
+ const { noTree, noDomains } = model
84
+ const [collapsed, setCollapsed] = useState(readCollapsed)
85
+ const section = (title: string) => ({
86
+ title,
87
+ collapsed: collapsed.has(title),
88
+ onToggle: () => {
89
+ const next = new Set(collapsed)
90
+ if (!next.delete(title)) {
91
+ next.add(title)
92
+ }
93
+ writeCollapsed(next)
94
+ setCollapsed(next)
95
+ },
96
+ })
97
+ return (
98
+ <Box
99
+ data-testid="settings_panel"
100
+ sx={{
101
+ width: settingsPanelWidth,
102
+ height,
103
+ flexShrink: 0,
104
+ overflowY: 'auto',
105
+ borderLeft: 1,
106
+ borderColor: 'divider',
107
+ bgcolor: 'background.paper',
108
+ }}
109
+ >
110
+ <Box
111
+ sx={{
112
+ display: 'flex',
113
+ alignItems: 'center',
114
+ justifyContent: 'space-between',
115
+ px: 1.5,
116
+ pt: 0.5,
117
+ }}
118
+ >
119
+ <Typography variant="subtitle1">Settings</Typography>
120
+ <IconButton
121
+ aria-label="Close settings"
122
+ onClick={() => {
123
+ model.setSettingsOpen(false)
124
+ }}
125
+ >
126
+ <Close fontSize="small" />
127
+ </IconButton>
128
+ </Box>
129
+ <Section {...section('Color')}>
130
+ <MenuItemsForm items={colorSettingsItems(model)} />
131
+ </Section>
132
+ {noTree ? null : (
133
+ <Section {...section('Tree')}>
134
+ <MenuItemsForm items={treeSubMenu(model)} />
135
+ </Section>
136
+ )}
137
+ <Section {...section('Alignment')}>
138
+ <MenuItemsForm items={alignmentSettingsItems(model)} />
139
+ <MenuItemsForm items={gapsSubMenu(model)} />
140
+ </Section>
141
+ {noDomains ? null : (
142
+ <Section {...section('Annotations')}>
143
+ <MenuItemsForm items={annotationsSubMenu(model)} />
144
+ </Section>
145
+ )}
146
+ <Section {...section('Tracks')}>
147
+ <TrackList model={model} />
148
+ <Box sx={{ mt: 1, pt: 0.5, borderTop: 1, borderColor: 'divider' }}>
149
+ <MenuItemsForm items={trackSettingsItems(model)} />
150
+ </Box>
151
+ </Section>
152
+ </Box>
153
+ )
154
+ })
155
+
156
+ export default SettingsPanel
@@ -0,0 +1,119 @@
1
+ import React, { useState } from 'react'
2
+
3
+ import {
4
+ Box,
5
+ Button,
6
+ Checkbox,
7
+ FormControlLabel,
8
+ TextField,
9
+ Typography,
10
+ } from '@mui/material'
11
+ import { observer } from 'mobx-react'
12
+
13
+ import type { MsaViewModel } from '../../model.ts'
14
+
15
+ interface Entry {
16
+ id: string
17
+ label: string
18
+ }
19
+
20
+ function trackGroups(model: MsaViewModel) {
21
+ const computed = new Set(model.computedTrackModels.map(t => t.model.id))
22
+ const computedEntries: Entry[] = []
23
+ const alignmentEntries: Entry[] = []
24
+ for (const { model: t } of model.tracks) {
25
+ ;(computed.has(t.id) ? computedEntries : alignmentEntries).push({
26
+ id: t.id,
27
+ label: t.name,
28
+ })
29
+ }
30
+ return [
31
+ { title: 'Computed from the alignment', entries: computedEntries },
32
+ { title: 'Alignment annotations', entries: alignmentEntries },
33
+ ].filter(g => g.entries.length > 0)
34
+ }
35
+
36
+ // a filter box shows once the list is long enough to need one
37
+ const filterFrom = 10
38
+
39
+ const TrackList = observer(function ({ model }: { model: MsaViewModel }) {
40
+ const [filter, setFilter] = useState('')
41
+ const shown = new Set(model.turnedOnTracks.map(t => t.model.id))
42
+ const all = trackGroups(model)
43
+ const needle = filter.trim().toLowerCase()
44
+ const groups = all
45
+ .map(g => ({
46
+ ...g,
47
+ entries: g.entries.filter(e => e.label.toLowerCase().includes(needle)),
48
+ }))
49
+ .filter(g => g.entries.length > 0)
50
+ const matching = groups.flatMap(g => g.entries.map(e => e.id))
51
+ const long = all.reduce((n, g) => n + g.entries.length, 0) >= filterFrom
52
+ return (
53
+ <>
54
+ {long ? (
55
+ <>
56
+ <TextField
57
+ size="small"
58
+ placeholder="Filter tracks"
59
+ value={filter}
60
+ onChange={event => {
61
+ setFilter(event.target.value)
62
+ }}
63
+ fullWidth
64
+ />
65
+ <Box sx={{ display: 'flex' }}>
66
+ <Button
67
+ size="small"
68
+ onClick={() => {
69
+ model.setTracksShown(matching, true)
70
+ }}
71
+ >
72
+ Show all
73
+ </Button>
74
+ <Button
75
+ size="small"
76
+ onClick={() => {
77
+ model.setTracksShown(matching, false)
78
+ }}
79
+ >
80
+ Hide all
81
+ </Button>
82
+ </Box>
83
+ </>
84
+ ) : null}
85
+ {groups.map(({ title, entries }) => (
86
+ <Box key={title}>
87
+ {groups.length > 1 ? (
88
+ <Typography variant="caption" color="text.secondary">
89
+ {title}
90
+ </Typography>
91
+ ) : null}
92
+ {entries.map(({ id, label }) => (
93
+ <FormControlLabel
94
+ key={id}
95
+ sx={{ display: 'flex' }}
96
+ control={
97
+ <Checkbox
98
+ size="small"
99
+ checked={shown.has(id)}
100
+ onChange={() => {
101
+ model.toggleTrack(id)
102
+ }}
103
+ sx={{ py: 0.25 }}
104
+ />
105
+ }
106
+ label={label}
107
+ slotProps={{ typography: { variant: 'body2' } }}
108
+ />
109
+ ))}
110
+ </Box>
111
+ ))}
112
+ {groups.length === 0 ? (
113
+ <Typography variant="body2">No track matches</Typography>
114
+ ) : null}
115
+ </>
116
+ )
117
+ })
118
+
119
+ export default TrackList
@@ -1,8 +1,8 @@
1
1
  import React from 'react'
2
2
 
3
- import { useTheme } from '@mui/material'
4
3
  import { observer } from 'mobx-react'
5
4
 
5
+ import { useCanvasTheme } from '../../darkReader.ts'
6
6
  import { useCanvasAutorun } from '../../useCanvasAutorun.ts'
7
7
  import { TrackResizeHandle } from '../ResizeHandles.tsx'
8
8
  import { drawTrackBlock } from './drawTracks.ts'
@@ -21,7 +21,7 @@ const TrackBlock = observer(function ({
21
21
  }) {
22
22
  const { blockSize, highResScaleFactor } = model
23
23
  const { height } = track.model
24
- const theme = useTheme()
24
+ const theme = useCanvasTheme()
25
25
  const canvasWidth = blockSize * highResScaleFactor
26
26
  const canvasHeight = height * highResScaleFactor
27
27
 
@@ -9,7 +9,7 @@ import type { Arc, BasicTrack } from '../../types.ts'
9
9
  import type { RenderCtx } from '../renderCtx.ts'
10
10
  import type { Theme } from '@mui/material'
11
11
 
12
- export function drawConservationBars({
12
+ function drawConservationBars({
13
13
  ctx,
14
14
  values,
15
15
  color,
@@ -55,7 +55,7 @@ export function barTrackValues(model: MsaViewModel, trackId: string) {
55
55
 
56
56
  // An arc's apex scales with the square root of its span. Contact-map spans skew
57
57
  // short, and a linear scale flattens them along the baseline.
58
- export function drawArcs({
58
+ function drawArcs({
59
59
  ctx,
60
60
  arcs,
61
61
  color,
@@ -103,7 +103,7 @@ export function drawArcs({
103
103
  }
104
104
  }
105
105
 
106
- export function drawTextTrackContent({
106
+ function drawTextTrackContent({
107
107
  ctx,
108
108
  data,
109
109
  colorScheme,
@@ -176,7 +176,7 @@ export function rulerCols(xStart: number, end: number, colWidth: number) {
176
176
  const RULER_FONT_SIZE = 10
177
177
  const TICK_HEIGHT = 4
178
178
 
179
- export function drawColumnRuler({
179
+ function drawColumnRuler({
180
180
  ctx,
181
181
  numColumns,
182
182
  label,
@@ -229,7 +229,7 @@ export function drawColumnRuler({
229
229
  const CAP_HEIGHT_RATIO = 0.72
230
230
  const MIN_LETTER_PX = 3
231
231
 
232
- export function drawSequenceLogo({
232
+ function drawSequenceLogo({
233
233
  ctx,
234
234
  colStats,
235
235
  colorScheme,
@@ -3,13 +3,7 @@ import React from 'react'
3
3
  import { useTheme } from '@mui/material'
4
4
  import { observer } from 'mobx-react'
5
5
 
6
- import {
7
- bracketGap,
8
- cladeHeight,
9
- cladeInGutter,
10
- cladeLabelHorizontal,
11
- cladeLabelOffset,
12
- } from './cladeBrackets.ts'
6
+ import { cladeLabelBoxes } from './cladeBrackets.ts'
13
7
 
14
8
  import type { MsaViewModel } from '../../model.ts'
15
9
 
@@ -20,56 +14,38 @@ import type { MsaViewModel } from '../../model.ts'
20
14
  * the same labels with a `rotate(-90)`.
21
15
  */
22
16
  const CladeLabels = observer(function ({ model }: { model: MsaViewModel }) {
23
- const {
24
- resolvedClades,
25
- cladeGutterWidth,
26
- rowHeight,
27
- effectiveTreeAreaWidth,
28
- fontSize,
29
- } = model
30
17
  const theme = useTheme()
31
- if (cladeGutterWidth === 0) {
32
- return null
33
- }
34
- const gutterLeft = effectiveTreeAreaWidth - cladeGutterWidth
18
+ const { fontSize } = model
35
19
  return (
36
20
  <>
37
- {resolvedClades.map((clade, index) => {
38
- const { label, markColor, rows } = clade
39
- if (!label || !cladeInGutter(clade)) {
40
- return null
41
- }
42
- const left = gutterLeft + cladeLabelOffset(clade)
43
- const horizontal = cladeLabelHorizontal(clade, rowHeight, fontSize)
44
- return (
45
- <div
46
- key={`${rows[0]}-${rows[1]}-${index}`}
47
- title={label}
48
- style={{
49
- position: 'absolute',
50
- left,
51
- top: rows[0] * rowHeight,
52
- width: effectiveTreeAreaWidth - left - bracketGap,
53
- height: cladeHeight(clade, rowHeight),
54
- display: 'flex',
55
- alignItems: 'center',
56
- color: markColor ?? theme.palette.text.primary,
57
- fontSize,
58
- overflow: 'hidden',
59
- textOverflow: 'ellipsis',
60
- whiteSpace: 'nowrap',
61
- pointerEvents: 'none',
62
- // vertical-rl reads top to bottom, and the half turn makes it
63
- // read upwards, which is what rotate(-90) exports
64
- ...(horizontal
65
- ? {}
66
- : { writingMode: 'vertical-rl', transform: 'rotate(180deg)' }),
67
- }}
68
- >
69
- {label}
70
- </div>
71
- )
72
- })}
21
+ {cladeLabelBoxes(model).map(box => (
22
+ <div
23
+ key={box.key}
24
+ title={box.label}
25
+ style={{
26
+ position: 'absolute',
27
+ left: box.left,
28
+ top: box.top,
29
+ width: box.width,
30
+ height: box.height,
31
+ display: 'flex',
32
+ alignItems: 'center',
33
+ color: box.color ?? theme.palette.text.primary,
34
+ fontSize,
35
+ overflow: 'hidden',
36
+ textOverflow: 'ellipsis',
37
+ whiteSpace: 'nowrap',
38
+ pointerEvents: 'none',
39
+ // vertical-rl reads top to bottom, and the half turn makes it
40
+ // read upwards, which is what rotate(-90) exports
41
+ ...(box.horizontal
42
+ ? {}
43
+ : { writingMode: 'vertical-rl', transform: 'rotate(180deg)' }),
44
+ }}
45
+ >
46
+ {box.label}
47
+ </div>
48
+ ))}
73
49
  </>
74
50
  )
75
51
  })
@@ -2,9 +2,9 @@ import React, { useState } from 'react'
2
2
 
3
3
  import BaseTooltip from '@jbrowse/core/ui/BaseTooltip'
4
4
  import { makeStyles } from '@jbrowse/core/util/tss-react'
5
- import { useTheme } from '@mui/material'
6
5
  import { observer } from 'mobx-react'
7
6
 
7
+ import { useCanvasTheme } from '../../darkReader.ts'
8
8
  import { useCanvasAutorun } from '../../useCanvasAutorun.ts'
9
9
  import TreeMenu from './TreeMenu.tsx'
10
10
  import { renderTreeCanvas } from './renderTreeCanvas.ts'
@@ -30,7 +30,7 @@ const TreeCanvasBlock = observer(function ({
30
30
  offsetY: number
31
31
  }) {
32
32
  const { classes } = useStyles()
33
- const theme = useTheme()
33
+ const theme = useCanvasTheme()
34
34
  const [menu, setMenu] = useState<TreeMenuTarget>()
35
35
  const { clickMap, anchor, hitTest, onMouseMove, onMouseLeave } = useTreeHover(
36
36
  { model, offsetY },