react-msaview 9.1.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 (272) hide show
  1. package/bundle/index.js +111 -115
  2. package/bundle/index.js.map +4 -4
  3. package/dist/blosum62.d.ts +3 -0
  4. package/dist/blosum62.js +43 -0
  5. package/dist/blosum62.js.map +1 -0
  6. package/dist/colorSchemes.js +1 -0
  7. package/dist/colorSchemes.js.map +1 -1
  8. package/dist/components/Loading.d.ts +3 -2
  9. package/dist/components/Loading.js +2 -2
  10. package/dist/components/Loading.js.map +1 -1
  11. package/dist/components/MSAView.d.ts +3 -2
  12. package/dist/components/MSAView.js +7 -4
  13. package/dist/components/MSAView.js.map +1 -1
  14. package/dist/components/MSAViewer.d.ts +62 -12
  15. package/dist/components/MetadataTable.d.ts +4 -1
  16. package/dist/components/MetadataTable.js +55 -16
  17. package/dist/components/MetadataTable.js.map +1 -1
  18. package/dist/components/Track.js +6 -0
  19. package/dist/components/Track.js.map +1 -1
  20. package/dist/components/controls.js +2 -1
  21. package/dist/components/controls.js.map +1 -1
  22. package/dist/components/dialogs/AnnotationFileDialog.js +7 -2
  23. package/dist/components/dialogs/AnnotationFileDialog.js.map +1 -1
  24. package/dist/components/dialogs/FeatureDialog.js +2 -2
  25. package/dist/components/dialogs/FeatureDialog.js.map +1 -1
  26. package/dist/components/{header/MSASettingsMenu.d.ts → dialogs/GapThresholdDialog.d.ts} +3 -2
  27. package/dist/components/dialogs/GapThresholdDialog.js +15 -0
  28. package/dist/components/dialogs/GapThresholdDialog.js.map +1 -0
  29. package/dist/components/{header → dialogs}/GappynessSlider.d.ts +1 -1
  30. package/dist/components/{header → dialogs}/GappynessSlider.js +3 -10
  31. package/dist/components/dialogs/GappynessSlider.js.map +1 -0
  32. package/dist/components/{header → dialogs}/GoToBox.d.ts +4 -2
  33. package/dist/components/dialogs/GoToBox.js +48 -0
  34. package/dist/components/dialogs/GoToBox.js.map +1 -0
  35. package/dist/components/dialogs/GoToDialog.d.ts +6 -0
  36. package/dist/components/dialogs/GoToDialog.js +10 -0
  37. package/dist/components/dialogs/GoToDialog.js.map +1 -0
  38. package/dist/components/dialogs/MetadataDialog.js +11 -3
  39. package/dist/components/dialogs/MetadataDialog.js.map +1 -1
  40. package/dist/components/dialogs/goTo.d.ts +5 -0
  41. package/dist/components/{header → dialogs}/goTo.js +1 -1
  42. package/dist/components/dialogs/goTo.js.map +1 -0
  43. package/dist/components/dialogs/metadataLabels.d.ts +4 -0
  44. package/dist/components/dialogs/metadataLabels.js +52 -0
  45. package/dist/components/dialogs/metadataLabels.js.map +1 -0
  46. package/dist/components/dialogs/metadataRecord.d.ts +1 -0
  47. package/dist/components/dialogs/metadataRecord.js +38 -0
  48. package/dist/components/dialogs/metadataRecord.js.map +1 -0
  49. package/dist/components/header/Header.d.ts +6 -2
  50. package/dist/components/header/Header.js +30 -32
  51. package/dist/components/header/Header.js.map +1 -1
  52. package/dist/components/header/{TreeSettingsMenu.d.ts → HiddenColumnsChip.d.ts} +2 -2
  53. package/dist/components/header/HiddenColumnsChip.js +15 -0
  54. package/dist/components/header/HiddenColumnsChip.js.map +1 -0
  55. package/dist/components/header/MenuBar.d.ts +6 -0
  56. package/dist/components/header/MenuBar.js +33 -0
  57. package/dist/components/header/MenuBar.js.map +1 -0
  58. package/dist/components/header/ScrollZoomCheckbox.d.ts +7 -0
  59. package/dist/components/header/ScrollZoomCheckbox.js +19 -0
  60. package/dist/components/header/ScrollZoomCheckbox.js.map +1 -0
  61. package/dist/components/header/ZoomControls.js +10 -30
  62. package/dist/components/header/ZoomControls.js.map +1 -1
  63. package/dist/components/header/colorSchemeMenuItems.d.ts +4 -0
  64. package/dist/components/header/colorSchemeMenuItems.js +106 -0
  65. package/dist/components/header/colorSchemeMenuItems.js.map +1 -0
  66. package/dist/components/header/getAnnotationsMenu.d.ts +3 -30
  67. package/dist/components/header/getAnnotationsMenu.js +48 -54
  68. package/dist/components/header/getAnnotationsMenu.js.map +1 -1
  69. package/dist/components/header/menuBarItems.d.ts +11 -0
  70. package/dist/components/header/menuBarItems.js +277 -0
  71. package/dist/components/header/menuBarItems.js.map +1 -0
  72. package/dist/components/import/ImportForm.js +12 -6
  73. package/dist/components/import/ImportForm.js.map +1 -1
  74. package/dist/components/minimap/Minimap.js +7 -3
  75. package/dist/components/minimap/Minimap.js.map +1 -1
  76. package/dist/components/minimap/MinimapSVG.js +2 -1
  77. package/dist/components/minimap/MinimapSVG.js.map +1 -1
  78. package/dist/components/minimap/minimapLayout.d.ts +12 -0
  79. package/dist/components/minimap/minimapLayout.js +20 -0
  80. package/dist/components/minimap/minimapLayout.js.map +1 -1
  81. package/dist/components/msa/MSACanvas.js +26 -9
  82. package/dist/components/msa/MSACanvas.js.map +1 -1
  83. package/dist/components/msa/MSACanvasBlock.js +15 -11
  84. package/dist/components/msa/MSACanvasBlock.js.map +1 -1
  85. package/dist/components/msa/MSAMouseoverCanvas.js +2 -2
  86. package/dist/components/msa/MSAMouseoverCanvas.js.map +1 -1
  87. package/dist/components/msa/WheelZoomHint.d.ts +7 -0
  88. package/dist/components/msa/WheelZoomHint.js +38 -0
  89. package/dist/components/msa/WheelZoomHint.js.map +1 -0
  90. package/dist/components/msa/msaKeys.js +3 -0
  91. package/dist/components/msa/msaKeys.js.map +1 -1
  92. package/dist/components/msa/msaRaster.d.ts +4 -5
  93. package/dist/components/msa/msaRaster.js +5 -5
  94. package/dist/components/msa/msaRaster.js.map +1 -1
  95. package/dist/components/msa/renderBoxFeatureCanvasBlock.js +22 -2
  96. package/dist/components/msa/renderBoxFeatureCanvasBlock.js.map +1 -1
  97. package/dist/components/msa/renderHighlights.d.ts +0 -3
  98. package/dist/components/msa/renderHighlights.js +3 -3
  99. package/dist/components/msa/renderHighlights.js.map +1 -1
  100. package/dist/components/msa/renderMSABlock.d.ts +6 -1
  101. package/dist/components/msa/renderMSABlock.js +44 -17
  102. package/dist/components/msa/renderMSABlock.js.map +1 -1
  103. package/dist/components/msa/tileColor.js +7 -0
  104. package/dist/components/msa/tileColor.js.map +1 -1
  105. package/dist/components/rowpanels/RowPanelBlock.js +2 -2
  106. package/dist/components/rowpanels/RowPanelBlock.js.map +1 -1
  107. package/dist/components/settings/MenuItemsForm.d.ts +10 -0
  108. package/dist/components/settings/MenuItemsForm.js +80 -0
  109. package/dist/components/settings/MenuItemsForm.js.map +1 -0
  110. package/dist/components/settings/SettingsPanel.d.ts +12 -0
  111. package/dist/components/settings/SettingsPanel.js +92 -0
  112. package/dist/components/settings/SettingsPanel.js.map +1 -0
  113. package/dist/components/{header/FileMenu.d.ts → settings/TrackList.d.ts} +2 -2
  114. package/dist/components/settings/TrackList.js +55 -0
  115. package/dist/components/settings/TrackList.js.map +1 -0
  116. package/dist/components/tracks/TrackBlocks.js +2 -2
  117. package/dist/components/tracks/TrackBlocks.js.map +1 -1
  118. package/dist/components/tree/TreeCanvasBlock.js +2 -2
  119. package/dist/components/tree/TreeCanvasBlock.js.map +1 -1
  120. package/dist/components/tree/TreeMenu.js +4 -6
  121. package/dist/components/tree/TreeMenu.js.map +1 -1
  122. package/dist/components/tree/TreeOverview.js +2 -2
  123. package/dist/components/tree/TreeOverview.js.map +1 -1
  124. package/dist/constants.d.ts +4 -1
  125. package/dist/constants.js +7 -1
  126. package/dist/constants.js.map +1 -1
  127. package/dist/darkReader.d.ts +7 -0
  128. package/dist/darkReader.js +42 -0
  129. package/dist/darkReader.js.map +1 -0
  130. package/dist/element.js +22 -47
  131. package/dist/element.js.map +1 -1
  132. package/dist/featurePanels.d.ts +37 -0
  133. package/dist/featurePanels.js +187 -0
  134. package/dist/featurePanels.js.map +1 -0
  135. package/dist/filehandleLoader.d.ts +41 -0
  136. package/dist/filehandleLoader.js +99 -0
  137. package/dist/filehandleLoader.js.map +1 -0
  138. package/dist/index.d.ts +3 -0
  139. package/dist/index.js +2 -0
  140. package/dist/index.js.map +1 -1
  141. package/dist/model/treeModel.d.ts +9 -0
  142. package/dist/model/treeModel.js +12 -1
  143. package/dist/model/treeModel.js.map +1 -1
  144. package/dist/model.d.ts +142 -32
  145. package/dist/model.js +268 -569
  146. package/dist/model.js.map +1 -1
  147. package/dist/neighborJoining.js +1 -40
  148. package/dist/neighborJoining.js.map +1 -1
  149. package/dist/renderToSvg.js +1 -13
  150. package/dist/renderToSvg.js.map +1 -1
  151. package/dist/rerootTree.d.ts +1 -0
  152. package/dist/rerootTree.js +7 -6
  153. package/dist/rerootTree.js.map +1 -1
  154. package/dist/schemeResidueColor.d.ts +12 -0
  155. package/dist/schemeResidueColor.js +33 -0
  156. package/dist/schemeResidueColor.js.map +1 -0
  157. package/dist/similarityColors.d.ts +11 -0
  158. package/dist/similarityColors.js +90 -0
  159. package/dist/similarityColors.js.map +1 -0
  160. package/dist/svgTestUtil.d.ts +62 -12
  161. package/dist/treeDisplay.d.ts +37 -0
  162. package/dist/treeDisplay.js +209 -0
  163. package/dist/treeDisplay.js.map +1 -0
  164. package/dist/types.d.ts +6 -2
  165. package/dist/useWheelScroll.d.ts +3 -1
  166. package/dist/useWheelScroll.js +36 -17
  167. package/dist/useWheelScroll.js.map +1 -1
  168. package/dist/util.d.ts +2 -0
  169. package/dist/util.js +2 -0
  170. package/dist/util.js.map +1 -1
  171. package/dist/version.d.ts +1 -1
  172. package/dist/version.js +1 -1
  173. package/dist/wheelZoom.d.ts +8 -3
  174. package/dist/wheelZoom.js +44 -32
  175. package/dist/wheelZoom.js.map +1 -1
  176. package/dist/zoomSnapshot.d.ts +29 -0
  177. package/dist/zoomSnapshot.js +47 -0
  178. package/dist/zoomSnapshot.js.map +1 -0
  179. package/package.json +11 -11
  180. package/src/blosum62.ts +45 -0
  181. package/src/colorSchemes.ts +1 -0
  182. package/src/components/Loading.tsx +6 -2
  183. package/src/components/MSAView.tsx +22 -5
  184. package/src/components/MetadataTable.tsx +83 -26
  185. package/src/components/Track.tsx +13 -0
  186. package/src/components/controls.ts +2 -1
  187. package/src/components/dialogs/AnnotationFileDialog.tsx +12 -1
  188. package/src/components/dialogs/FeatureDialog.tsx +2 -5
  189. package/src/components/dialogs/GapThresholdDialog.tsx +36 -0
  190. package/src/components/{header → dialogs}/GappynessSlider.tsx +3 -10
  191. package/src/components/{header → dialogs}/GoToBox.tsx +36 -10
  192. package/src/components/dialogs/GoToDialog.tsx +30 -0
  193. package/src/components/dialogs/MetadataDialog.tsx +32 -4
  194. package/src/components/{header → dialogs}/goTo.ts +1 -1
  195. package/src/components/dialogs/metadataLabels.ts +54 -0
  196. package/src/components/dialogs/metadataRecord.tsx +61 -0
  197. package/src/components/header/Header.tsx +75 -42
  198. package/src/components/header/HiddenColumnsChip.tsx +39 -0
  199. package/src/components/header/MenuBar.tsx +64 -0
  200. package/src/components/header/ScrollZoomCheckbox.tsx +46 -0
  201. package/src/components/header/ZoomControls.tsx +20 -35
  202. package/src/components/header/colorSchemeMenuItems.ts +121 -0
  203. package/src/components/header/getAnnotationsMenu.ts +52 -56
  204. package/src/components/header/menuBarItems.ts +357 -0
  205. package/src/components/import/ImportForm.tsx +19 -7
  206. package/src/components/minimap/Minimap.tsx +11 -3
  207. package/src/components/minimap/MinimapSVG.tsx +17 -1
  208. package/src/components/minimap/minimapLayout.ts +27 -0
  209. package/src/components/msa/MSACanvas.tsx +22 -3
  210. package/src/components/msa/MSACanvasBlock.tsx +18 -11
  211. package/src/components/msa/MSAMouseoverCanvas.tsx +2 -2
  212. package/src/components/msa/WheelZoomHint.tsx +56 -0
  213. package/src/components/msa/msaKeys.ts +3 -0
  214. package/src/components/msa/msaRaster.ts +6 -8
  215. package/src/components/msa/renderBoxFeatureCanvasBlock.ts +28 -5
  216. package/src/components/msa/renderHighlights.ts +3 -3
  217. package/src/components/msa/renderMSABlock.ts +51 -15
  218. package/src/components/msa/tileColor.ts +7 -0
  219. package/src/components/rowpanels/RowPanelBlock.tsx +2 -2
  220. package/src/components/settings/MenuItemsForm.tsx +225 -0
  221. package/src/components/settings/SettingsPanel.tsx +156 -0
  222. package/src/components/settings/TrackList.tsx +119 -0
  223. package/src/components/tracks/TrackBlocks.tsx +2 -2
  224. package/src/components/tree/TreeCanvasBlock.tsx +2 -2
  225. package/src/components/tree/TreeMenu.tsx +4 -6
  226. package/src/components/tree/TreeOverview.tsx +2 -2
  227. package/src/constants.ts +9 -1
  228. package/src/darkReader.ts +57 -0
  229. package/src/element.ts +34 -57
  230. package/src/featurePanels.ts +269 -0
  231. package/src/filehandleLoader.ts +136 -0
  232. package/src/index.ts +4 -0
  233. package/src/model/treeModel.ts +13 -0
  234. package/src/model.ts +345 -748
  235. package/src/neighborJoining.ts +1 -42
  236. package/src/renderToSvg.tsx +1 -13
  237. package/src/rerootTree.ts +7 -6
  238. package/src/schemeResidueColor.ts +37 -0
  239. package/src/similarityColors.ts +112 -0
  240. package/src/treeDisplay.ts +278 -0
  241. package/src/types.ts +6 -3
  242. package/src/useWheelScroll.ts +43 -22
  243. package/src/util.ts +3 -0
  244. package/src/version.ts +1 -1
  245. package/src/wheelZoom.ts +55 -33
  246. package/src/zoomSnapshot.ts +80 -0
  247. package/dist/components/header/ColorSchemeMenu.d.ts +0 -12
  248. package/dist/components/header/ColorSchemeMenu.js +0 -37
  249. package/dist/components/header/ColorSchemeMenu.js.map +0 -1
  250. package/dist/components/header/FileMenu.js +0 -92
  251. package/dist/components/header/FileMenu.js.map +0 -1
  252. package/dist/components/header/GappynessSlider.js.map +0 -1
  253. package/dist/components/header/GoToBox.js +0 -31
  254. package/dist/components/header/GoToBox.js.map +0 -1
  255. package/dist/components/header/MSASettingsMenu.js +0 -11
  256. package/dist/components/header/MSASettingsMenu.js.map +0 -1
  257. package/dist/components/header/ScrollZoomIcons.d.ts +0 -6
  258. package/dist/components/header/ScrollZoomIcons.js +0 -35
  259. package/dist/components/header/ScrollZoomIcons.js.map +0 -1
  260. package/dist/components/header/TreeSettingsMenu.js +0 -11
  261. package/dist/components/header/TreeSettingsMenu.js.map +0 -1
  262. package/dist/components/header/goTo.d.ts +0 -20
  263. package/dist/components/header/goTo.js.map +0 -1
  264. package/dist/components/header/settingsMenuItems.d.ts +0 -5
  265. package/dist/components/header/settingsMenuItems.js +0 -171
  266. package/dist/components/header/settingsMenuItems.js.map +0 -1
  267. package/src/components/header/ColorSchemeMenu.tsx +0 -56
  268. package/src/components/header/FileMenu.tsx +0 -107
  269. package/src/components/header/MSASettingsMenu.tsx +0 -26
  270. package/src/components/header/ScrollZoomIcons.tsx +0 -52
  271. package/src/components/header/TreeSettingsMenu.tsx +0 -30
  272. package/src/components/header/settingsMenuItems.ts +0 -216
@@ -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
 
@@ -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 },
@@ -30,7 +30,7 @@ const TreeMenu = observer(function ({
30
30
  const { name, id, leaf } = node
31
31
  const isCollapsed = collapsed.includes(id)
32
32
  const isRoot = id === model.tree.id
33
- const noun = leaf ? 'subtree' : 'this node'
33
+ const noun = leaf ? 'subtree' : 'clade'
34
34
  return (
35
35
  <Menu
36
36
  anchorReference="anchorPosition"
@@ -73,9 +73,7 @@ const TreeMenu = observer(function ({
73
73
  onClose()
74
74
  }}
75
75
  >
76
- {showOnly === id
77
- ? 'Disable show only this node'
78
- : 'Show only this node'}
76
+ {showOnly === id ? 'Show all rows' : 'Show only this clade'}
79
77
  </MenuItem>
80
78
  )}
81
79
  {leaf || isCollapsed ? null : (
@@ -86,7 +84,7 @@ const TreeMenu = observer(function ({
86
84
  onClose()
87
85
  }}
88
86
  >
89
- Rotate this node
87
+ Rotate clade
90
88
  </MenuItem>
91
89
  )}
92
90
  {isRoot ? null : (
@@ -108,7 +106,7 @@ const TreeMenu = observer(function ({
108
106
  onClose()
109
107
  }}
110
108
  >
111
- Indicate differences from this row
109
+ Use as reference row
112
110
  </MenuItem>
113
111
  ) : null}
114
112
  {leaf && relativeTo ? (
@@ -1,8 +1,8 @@
1
1
  import React, { useState } 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 {
8
8
  drawTreeOverview,
@@ -22,7 +22,7 @@ import type { MsaViewModel } from '../../model.ts'
22
22
  */
23
23
  const TreeOverview = observer(function ({ model }: { model: MsaViewModel }) {
24
24
  const { effectiveTreeAreaWidth, overviewHeight, highResScaleFactor } = model
25
- const theme = useTheme()
25
+ const theme = useCanvasTheme()
26
26
  const [hover, setHover] = useState<{ id: string; rows: [number, number] }>()
27
27
  const width = Math.round(effectiveTreeAreaWidth * highResScaleFactor)
28
28
  const height = Math.round(overviewHeight * highResScaleFactor)
package/src/constants.ts CHANGED
@@ -50,6 +50,10 @@ export const defaultScrollZoomAxis: ScrollZoomAxis = 'both'
50
50
  export const minLetterRowHeight = 8
51
51
  export const minLetterColWidth = 5
52
52
 
53
+ // A wheel-zoom frame draws a block's letters only when the block holds at most
54
+ // this many cells; the settle redraw after the gesture draws the rest
55
+ export const zoomFrameMaxLetters = 2000
56
+
53
57
  // A feature span draws its label at two pixels under its height, capped at 11,
54
58
  // so a span shorter than this holds no readable text: an underline bar, a
55
59
  // sub-row band or a features panel over small rows
@@ -68,9 +72,12 @@ export const labelFitFontSize = fontSizeFor(defaultRowHeight)
68
72
  // zoom-in buttons (which otherwise grow unbounded). minColWidth/minRowHeight are
69
73
  // the smooth-zoom floors.
70
74
  export const minColWidth = 0.2
71
- export const minRowHeight = 1
75
+ export const minRowHeight = 0.2
72
76
  export const maxCellSize = 80
73
77
 
78
+ // the settings panel's width, which it takes out of the view's
79
+ export const settingsPanelWidth = 280
80
+
74
81
  // MSA model defaults
75
82
  export const defaultBgColor = true
76
83
  export const defaultColorSchemeName = 'maeditor'
@@ -99,6 +106,7 @@ export const defaultDrawTree = true
99
106
  export const defaultDrawNodeBubbles = true
100
107
  export const defaultDrawNodeLabels = false
101
108
  export const defaultShowTreeOverview = false
109
+ export const defaultShowTreeScaleBar = false
102
110
  export const defaultOverviewHeight = 120
103
111
 
104
112
  // Neighbor joining does not run above this many rows. The distance matrix is
@@ -0,0 +1,57 @@
1
+ import { useSyncExternalStore } from 'react'
2
+
3
+ import { createJBrowseTheme } from '@jbrowse/core/ui/theme'
4
+ import { useTheme } from '@mui/material'
5
+
6
+ import type { Theme } from '@mui/material'
7
+
8
+ // Dark Reader's Dynamic mode rewrites the page's CSS and leaves canvas pixels
9
+ // alone, so a light theme's black letters land on the dark page it paints.
10
+ // Inlined from @jbrowse/core/util/systemColorScheme, which an older host core
11
+ // lacks (see CLAUDE.md on externals).
12
+ const DARK_READER_ATTRIBUTE = 'data-darkreader-scheme'
13
+
14
+ function darkReaderIsDark() {
15
+ return (
16
+ typeof document !== 'undefined' &&
17
+ document.documentElement.getAttribute(DARK_READER_ATTRIBUTE) === 'dark'
18
+ )
19
+ }
20
+
21
+ function onDarkReaderChange(listener: () => void) {
22
+ if (
23
+ typeof MutationObserver !== 'function' ||
24
+ typeof document === 'undefined'
25
+ ) {
26
+ return () => {}
27
+ }
28
+ const observer = new MutationObserver(listener)
29
+ observer.observe(document.documentElement, {
30
+ attributes: true,
31
+ attributeFilter: [DARK_READER_ATTRIBUTE],
32
+ })
33
+ return () => {
34
+ observer.disconnect()
35
+ }
36
+ }
37
+
38
+ let darkTheme: Theme | undefined
39
+
40
+ /**
41
+ * The theme a canvas draws in: the surrounding MUI theme, or a dark one while
42
+ * Dark Reader darkens a light page. The DOM keeps the surrounding theme, which
43
+ * Dark Reader restyles itself, and the SVG export keeps it too.
44
+ */
45
+ export function useCanvasTheme() {
46
+ const theme = useTheme()
47
+ const darkReader = useSyncExternalStore(
48
+ onDarkReaderChange,
49
+ darkReaderIsDark,
50
+ () => false,
51
+ )
52
+ if (darkReader && theme.palette.mode !== 'dark') {
53
+ darkTheme ??= createJBrowseTheme({ palette: { mode: 'dark' } })
54
+ return darkTheme
55
+ }
56
+ return theme
57
+ }
package/src/element.ts CHANGED
@@ -5,16 +5,7 @@ import { mount } from './mount.tsx'
5
5
  import type { MSAViewerProps } from './components/MSAViewer.tsx'
6
6
  import type { MsaViewModel } from './model.ts'
7
7
  import type { MountedViewer } from './mount.tsx'
8
- import type {
9
- Cell,
10
- ColorScheme,
11
- ColumnTrackSpec,
12
- Feature,
13
- Highlight,
14
- MsaSelection,
15
- ResidueMapping,
16
- Viewport,
17
- } from './types.ts'
8
+ import type { Cell, Highlight, MsaSelection, Viewport } from './types.ts'
18
9
 
19
10
  type ElementData = Pick<
20
11
  MSAViewerProps,
@@ -29,6 +20,17 @@ type ElementData = Pick<
29
20
  | 'selection'
30
21
  >
31
22
 
23
+ const dataProperties = [
24
+ 'msa',
25
+ 'tree',
26
+ 'gff',
27
+ 'features',
28
+ 'colorScheme',
29
+ 'highlights',
30
+ 'columnTracks',
31
+ 'residueMappings',
32
+ ] as const satisfies (keyof ElementData)[]
33
+
32
34
  interface NightingaleManager extends HTMLElement {
33
35
  register: (element: HTMLElement) => void
34
36
  unregister: (element: HTMLElement) => void
@@ -115,54 +117,29 @@ export function defineMsaElement(tagName = 'jbrowse-msa') {
115
117
  #reported?: { start: number; end: number }
116
118
  #data: ElementData = {}
117
119
 
118
- get msa() {
119
- return this.#data.msa
120
- }
121
- set msa(value: string | undefined) {
122
- this.#setData({ msa: value })
123
- }
124
- get tree() {
125
- return this.#data.tree
126
- }
127
- set tree(value: string | undefined) {
128
- this.#setData({ tree: value })
129
- }
130
- get gff() {
131
- return this.#data.gff
132
- }
133
- set gff(value: string | undefined) {
134
- this.#setData({ gff: value })
135
- }
136
- get features() {
137
- return this.#data.features
138
- }
139
- set features(value: Feature[] | undefined) {
140
- this.#setData({ features: value })
141
- }
142
- get colorScheme() {
143
- return this.#data.colorScheme
144
- }
145
- set colorScheme(value: ColorScheme | undefined) {
146
- this.#setData({ colorScheme: value })
147
- }
148
- get highlights() {
149
- return this.#data.highlights
150
- }
151
- set highlights(value: Highlight[] | undefined) {
152
- this.#setData({ highlights: value })
153
- }
154
- get columnTracks() {
155
- return this.#data.columnTracks
156
- }
157
- set columnTracks(value: ColumnTrackSpec[] | undefined) {
158
- this.#setData({ columnTracks: value })
159
- }
160
- get residueMappings() {
161
- return this.#data.residueMappings
162
- }
163
- set residueMappings(value: ResidueMapping[] | undefined) {
164
- this.#setData({ residueMappings: value })
120
+ declare msa: ElementData['msa']
121
+ declare tree: ElementData['tree']
122
+ declare gff: ElementData['gff']
123
+ declare features: ElementData['features']
124
+ declare colorScheme: ElementData['colorScheme']
125
+ declare highlights: ElementData['highlights']
126
+ declare columnTracks: ElementData['columnTracks']
127
+ declare residueMappings: ElementData['residueMappings']
128
+
129
+ static {
130
+ for (const key of dataProperties) {
131
+ Object.defineProperty(MsaElement.prototype, key, {
132
+ configurable: true,
133
+ get(this: MsaElement) {
134
+ return this.#data[key]
135
+ },
136
+ set(this: MsaElement, value: ElementData[typeof key]) {
137
+ this.#setData({ [key]: value })
138
+ },
139
+ })
140
+ }
165
141
  }
142
+
166
143
  // the viewer's selection, which the reader's drags change
167
144
  get selection() {
168
145
  return this.#model ? this.#model.selection : this.#data.selection