react-msaview 5.0.7 → 5.0.16

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 (174) hide show
  1. package/bundle/index.js +135 -35
  2. package/bundle/index.js.LICENSE.txt +1 -1
  3. package/bundle/index.js.map +1 -1
  4. package/dist/components/Checkbox2.js +3 -6
  5. package/dist/components/Checkbox2.js.map +1 -1
  6. package/dist/components/MSAViewer.d.ts +14 -0
  7. package/dist/components/MSAViewer.js +34 -0
  8. package/dist/components/MSAViewer.js.map +1 -0
  9. package/dist/components/SequenceTextArea.js +4 -4
  10. package/dist/components/SequenceTextArea.js.map +1 -1
  11. package/dist/components/Track.js +5 -24
  12. package/dist/components/Track.js.map +1 -1
  13. package/dist/components/dialogs/DomainDialog.js +2 -5
  14. package/dist/components/dialogs/DomainDialog.js.map +1 -1
  15. package/dist/components/dialogs/InterProScanDialog.js +7 -7
  16. package/dist/components/dialogs/InterProScanDialog.js.map +1 -1
  17. package/dist/components/dialogs/SettingsDialog.js +3 -19
  18. package/dist/components/dialogs/SettingsDialog.js.map +1 -1
  19. package/dist/components/header/ColorSchemeMenu.d.ts +6 -0
  20. package/dist/components/header/ColorSchemeMenu.js +19 -0
  21. package/dist/components/header/ColorSchemeMenu.js.map +1 -0
  22. package/dist/components/header/{ZoomStar.d.ts → FileMenu.d.ts} +2 -2
  23. package/dist/components/header/FileMenu.js +71 -0
  24. package/dist/components/header/FileMenu.js.map +1 -0
  25. package/dist/components/header/Header.js +8 -6
  26. package/dist/components/header/Header.js.map +1 -1
  27. package/dist/components/header/HeaderMenu.js +3 -145
  28. package/dist/components/header/HeaderMenu.js.map +1 -1
  29. package/dist/components/header/MSASettingsMenu.d.ts +6 -0
  30. package/dist/components/header/MSASettingsMenu.js +36 -0
  31. package/dist/components/header/MSASettingsMenu.js.map +1 -0
  32. package/dist/components/header/SettingsMenu.js +1 -21
  33. package/dist/components/header/SettingsMenu.js.map +1 -1
  34. package/dist/components/header/TreeSettingsMenu.d.ts +6 -0
  35. package/dist/components/header/TreeSettingsMenu.js +74 -0
  36. package/dist/components/header/TreeSettingsMenu.js.map +1 -0
  37. package/dist/components/header/ZoomMenu.js +0 -8
  38. package/dist/components/header/ZoomMenu.js.map +1 -1
  39. package/dist/components/header/getDomainsMenu.d.ts +31 -0
  40. package/dist/components/header/getDomainsMenu.js +75 -0
  41. package/dist/components/header/getDomainsMenu.js.map +1 -0
  42. package/dist/components/import/ImportFormExamples.js +22 -20
  43. package/dist/components/import/ImportFormExamples.js.map +1 -1
  44. package/dist/components/msa/MSACanvas.js +13 -84
  45. package/dist/components/msa/MSACanvas.js.map +1 -1
  46. package/dist/components/msa/MSACanvasBlock.js +1 -3
  47. package/dist/components/msa/MSACanvasBlock.js.map +1 -1
  48. package/dist/components/msa/renderMSABlock.js +2 -4
  49. package/dist/components/msa/renderMSABlock.js.map +1 -1
  50. package/dist/components/msa/renderMSAMouseover.js +1 -7
  51. package/dist/components/msa/renderMSAMouseover.js.map +1 -1
  52. package/dist/components/tree/TreeCanvas.js +14 -91
  53. package/dist/components/tree/TreeCanvas.js.map +1 -1
  54. package/dist/components/tree/TreeNodeMenu.js +5 -16
  55. package/dist/components/tree/TreeNodeMenu.js.map +1 -1
  56. package/dist/components/tree/renderTreeCanvas.js +55 -22
  57. package/dist/components/tree/renderTreeCanvas.js.map +1 -1
  58. package/dist/constants.d.ts +0 -2
  59. package/dist/constants.js +0 -2
  60. package/dist/constants.js.map +1 -1
  61. package/dist/fetchUtils.d.ts +0 -1
  62. package/dist/fetchUtils.js +0 -4
  63. package/dist/fetchUtils.js.map +1 -1
  64. package/dist/flatToTree.d.ts +0 -5
  65. package/dist/flatToTree.js +13 -30
  66. package/dist/flatToTree.js.map +1 -1
  67. package/dist/hierarchy.d.ts +29 -0
  68. package/dist/hierarchy.js +164 -0
  69. package/dist/hierarchy.js.map +1 -0
  70. package/dist/index.d.ts +2 -0
  71. package/dist/index.js +1 -0
  72. package/dist/index.js.map +1 -1
  73. package/dist/launchInterProScan.d.ts +0 -5
  74. package/dist/launchInterProScan.js +5 -3
  75. package/dist/launchInterProScan.js.map +1 -1
  76. package/dist/model/DataModel.d.ts +9 -0
  77. package/dist/model/DataModel.js +12 -1
  78. package/dist/model/DataModel.js.map +1 -1
  79. package/dist/model/msaModel.d.ts +3 -0
  80. package/dist/model/msaModel.js +0 -1
  81. package/dist/model/msaModel.js.map +1 -1
  82. package/dist/model/treeModel.d.ts +3 -6
  83. package/dist/model/treeModel.js +3 -15
  84. package/dist/model/treeModel.js.map +1 -1
  85. package/dist/model.d.ts +24 -77
  86. package/dist/model.js +118 -239
  87. package/dist/model.js.map +1 -1
  88. package/dist/neighborJoining.js +38 -629
  89. package/dist/neighborJoining.js.map +1 -1
  90. package/dist/parseAsn1.d.ts +0 -12
  91. package/dist/parseAsn1.js +125 -332
  92. package/dist/parseAsn1.js.map +1 -1
  93. package/dist/useWheelScroll.d.ts +8 -0
  94. package/dist/useWheelScroll.js +93 -0
  95. package/dist/useWheelScroll.js.map +1 -0
  96. package/dist/util.d.ts +1 -6
  97. package/dist/util.js +5 -34
  98. package/dist/util.js.map +1 -1
  99. package/dist/vendor/copyToClipboard.d.ts +1 -10
  100. package/dist/vendor/copyToClipboard.js +14 -109
  101. package/dist/vendor/copyToClipboard.js.map +1 -1
  102. package/dist/vendor/fileSaver.d.ts +1 -11
  103. package/dist/vendor/fileSaver.js +7 -76
  104. package/dist/vendor/fileSaver.js.map +1 -1
  105. package/dist/version.d.ts +1 -1
  106. package/dist/version.js +1 -1
  107. package/dist/version.js.map +1 -1
  108. package/package.json +10 -13
  109. package/src/collapseLogic.test.ts +115 -0
  110. package/src/components/Checkbox2.tsx +9 -18
  111. package/src/components/MSAViewer.tsx +67 -0
  112. package/src/components/SequenceTextArea.tsx +4 -4
  113. package/src/components/Track.tsx +10 -26
  114. package/src/components/dialogs/DomainDialog.tsx +4 -5
  115. package/src/components/dialogs/InterProScanDialog.tsx +7 -7
  116. package/src/components/dialogs/SettingsDialog.tsx +0 -37
  117. package/src/components/header/ColorSchemeMenu.tsx +35 -0
  118. package/src/components/header/FileMenu.tsx +84 -0
  119. package/src/components/header/Header.tsx +8 -6
  120. package/src/components/header/HeaderMenu.tsx +4 -155
  121. package/src/components/header/MSASettingsMenu.tsx +48 -0
  122. package/src/components/header/SettingsMenu.tsx +0 -23
  123. package/src/components/header/TreeSettingsMenu.tsx +96 -0
  124. package/src/components/header/ZoomMenu.tsx +0 -8
  125. package/src/components/header/getDomainsMenu.ts +83 -0
  126. package/src/components/import/ImportFormExamples.tsx +38 -35
  127. package/src/components/msa/MSACanvas.tsx +21 -91
  128. package/src/components/msa/MSACanvasBlock.tsx +1 -3
  129. package/src/components/msa/renderBoxFeatureCanvasBlock.ts +1 -1
  130. package/src/components/msa/renderMSABlock.ts +2 -5
  131. package/src/components/msa/renderMSAMouseover.ts +0 -6
  132. package/src/components/tree/TreeCanvas.tsx +35 -100
  133. package/src/components/tree/TreeNodeMenu.tsx +5 -14
  134. package/src/components/tree/renderTreeCanvas.test.ts +205 -0
  135. package/src/components/tree/renderTreeCanvas.ts +64 -27
  136. package/src/constants.ts +0 -2
  137. package/src/fetchUtils.ts +0 -5
  138. package/src/flatToTree.ts +20 -38
  139. package/src/hierarchy.test.ts +120 -0
  140. package/src/hierarchy.ts +221 -0
  141. package/src/index.ts +2 -0
  142. package/src/launchInterProScan.ts +4 -3
  143. package/src/model/DataModel.ts +12 -1
  144. package/src/model/msaModel.ts +0 -2
  145. package/src/model/treeModel.ts +2 -18
  146. package/src/model.ts +180 -278
  147. package/src/neighborJoining.ts +38 -628
  148. package/src/parseAsn1.test.ts +4 -1
  149. package/src/parseAsn1.ts +135 -405
  150. package/src/useWheelScroll.ts +109 -0
  151. package/src/util.ts +5 -50
  152. package/src/vendor/copyToClipboard.ts +14 -122
  153. package/src/vendor/fileSaver.ts +8 -105
  154. package/src/version.ts +1 -1
  155. package/dist/components/dialogs/AddTrackDialog.d.ts +0 -8
  156. package/dist/components/dialogs/AddTrackDialog.js +0 -30
  157. package/dist/components/dialogs/AddTrackDialog.js.map +0 -1
  158. package/dist/components/dialogs/TabPanel.d.ts +0 -6
  159. package/dist/components/dialogs/TabPanel.js +0 -6
  160. package/dist/components/dialogs/TabPanel.js.map +0 -1
  161. package/dist/components/header/ZoomStar.js +0 -40
  162. package/dist/components/header/ZoomStar.js.map +0 -1
  163. package/dist/layout.d.ts +0 -26
  164. package/dist/layout.js +0 -74
  165. package/dist/layout.js.map +0 -1
  166. package/dist/reparseTree.d.ts +0 -2
  167. package/dist/reparseTree.js +0 -15
  168. package/dist/reparseTree.js.map +0 -1
  169. package/src/components/dialogs/AddTrackDialog.tsx +0 -85
  170. package/src/components/dialogs/TabPanel.tsx +0 -19
  171. package/src/components/header/ZoomStar.tsx +0 -74
  172. package/src/createPaletteMap.test.ts +0 -57
  173. package/src/layout.ts +0 -118
  174. package/src/reparseTree.ts +0 -18
@@ -0,0 +1,115 @@
1
+ import { describe, expect, test } from 'vitest'
2
+
3
+ import { collapse, find, hierarchy, leaves, sort, sum } from './hierarchy.ts'
4
+
5
+ import type { NodeWithIds } from './types.ts'
6
+
7
+ function buildRoot(tree: NodeWithIds, collapsed: string[], showOnly?: string) {
8
+ let hier = hierarchy(tree, d => d.children)
9
+ sum(hier, d => (d.children.length > 0 ? 0 : 1))
10
+ sort(hier, (a, b) => (a.data.length ?? 1) - (b.data.length ?? 1))
11
+
12
+ if (showOnly) {
13
+ const res = find(hier, n => n.data.id === showOnly)
14
+ if (res) {
15
+ hier = res
16
+ }
17
+ }
18
+
19
+ for (const collapsedId of collapsed) {
20
+ const node = find(hier, n => n.data.id === collapsedId)
21
+ if (!node) {
22
+ continue
23
+ }
24
+ if (node.children) {
25
+ collapse(node)
26
+ } else if (node.parent?.children) {
27
+ node.parent.children = node.parent.children.filter(
28
+ c => c.data.id !== collapsedId,
29
+ )
30
+ }
31
+ }
32
+
33
+ return hier
34
+ }
35
+
36
+ function simpleTree(): NodeWithIds {
37
+ return {
38
+ id: 'root',
39
+ name: 'root',
40
+ children: [
41
+ {
42
+ id: 'clade1',
43
+ name: 'clade1',
44
+ children: [
45
+ { id: 'seq1', name: 'seq1', children: [] },
46
+ { id: 'seq2', name: 'seq2', children: [] },
47
+ ],
48
+ },
49
+ {
50
+ id: 'clade2',
51
+ name: 'clade2',
52
+ children: [
53
+ { id: 'seq3', name: 'seq3', children: [] },
54
+ { id: 'seq4', name: 'seq4', children: [] },
55
+ ],
56
+ },
57
+ ],
58
+ }
59
+ }
60
+
61
+ describe('collapse logic (mirrors model.root getter)', () => {
62
+ test('no collapsed nodes returns all leaves', () => {
63
+ const root = buildRoot(simpleTree(), [])
64
+ expect(leaves(root).map(n => n.data.name)).toEqual([
65
+ 'seq1',
66
+ 'seq2',
67
+ 'seq3',
68
+ 'seq4',
69
+ ])
70
+ })
71
+
72
+ test('collapsing a branch hides its children', () => {
73
+ const root = buildRoot(simpleTree(), ['clade1'])
74
+ const l = leaves(root).map(n => n.data.name)
75
+ expect(l).toEqual(['clade1', 'seq3', 'seq4'])
76
+ })
77
+
78
+ test('collapsing a leaf node removes it', () => {
79
+ const root = buildRoot(simpleTree(), ['seq2'])
80
+ const l = leaves(root).map(n => n.data.name)
81
+ expect(l).toEqual(['seq1', 'seq3', 'seq4'])
82
+ })
83
+
84
+ test('collapsing multiple leaves', () => {
85
+ const root = buildRoot(simpleTree(), ['seq1', 'seq4'])
86
+ const l = leaves(root).map(n => n.data.name)
87
+ expect(l).toEqual(['seq2', 'seq3'])
88
+ })
89
+
90
+ test('collapsing a branch and a leaf in another branch', () => {
91
+ const root = buildRoot(simpleTree(), ['clade1', 'seq3'])
92
+ const l = leaves(root).map(n => n.data.name)
93
+ expect(l).toEqual(['clade1', 'seq4'])
94
+ })
95
+
96
+ test('collapsing nonexistent id is a no-op', () => {
97
+ const root = buildRoot(simpleTree(), ['doesnotexist'])
98
+ expect(leaves(root).map(n => n.data.name)).toEqual([
99
+ 'seq1',
100
+ 'seq2',
101
+ 'seq3',
102
+ 'seq4',
103
+ ])
104
+ })
105
+
106
+ test('showOnly focuses on a subtree', () => {
107
+ const root = buildRoot(simpleTree(), [], 'clade2')
108
+ expect(leaves(root).map(n => n.data.name)).toEqual(['seq3', 'seq4'])
109
+ })
110
+
111
+ test('showOnly + collapse within subtree', () => {
112
+ const root = buildRoot(simpleTree(), ['seq3'], 'clade2')
113
+ expect(leaves(root).map(n => n.data.name)).toEqual(['seq4'])
114
+ })
115
+ })
@@ -1,18 +1,7 @@
1
1
  import React from 'react'
2
2
 
3
- import {
4
- Checkbox,
5
- FormControlLabel,
6
- type FormControlLabelProps,
7
- } from '@mui/material'
3
+ import { Checkbox, FormControlLabel } from '@mui/material'
8
4
 
9
- function FormControlLabel2(rest: FormControlLabelProps) {
10
- return (
11
- <div>
12
- <FormControlLabel {...rest} />
13
- </div>
14
- )
15
- }
16
5
  export default function Checkbox2({
17
6
  checked,
18
7
  label,
@@ -25,11 +14,13 @@ export default function Checkbox2({
25
14
  onChange: () => void
26
15
  }) {
27
16
  return (
28
- <FormControlLabel2
29
- control={
30
- <Checkbox disabled={disabled} checked={checked} onChange={onChange} />
31
- }
32
- label={label}
33
- />
17
+ <div>
18
+ <FormControlLabel
19
+ control={
20
+ <Checkbox disabled={disabled} checked={checked} onChange={onChange} />
21
+ }
22
+ label={label}
23
+ />
24
+ </div>
34
25
  )
35
26
  }
@@ -0,0 +1,67 @@
1
+ import React, { useEffect, useMemo } from 'react'
2
+
3
+ import { createJBrowseTheme } from '@jbrowse/core/ui/theme'
4
+ import useMeasure from '@jbrowse/core/util/useMeasure'
5
+ import { ThemeProvider } from '@mui/material/styles'
6
+
7
+ import MSAModelF from '../model.ts'
8
+ import Loading from './Loading.tsx'
9
+
10
+ import type { FileLocation as FileLocationType } from '@jbrowse/core/util/types'
11
+
12
+ interface MSAViewerProps {
13
+ msa?: string
14
+ tree?: string
15
+ gff?: string
16
+ msaFilehandle?: FileLocationType
17
+ treeFilehandle?: FileLocationType
18
+ gffFilehandle?: FileLocationType
19
+ colorScheme?: string
20
+ height?: number
21
+ }
22
+
23
+ export default function MSAViewer({
24
+ msa,
25
+ tree,
26
+ gff,
27
+ msaFilehandle,
28
+ treeFilehandle,
29
+ gffFilehandle,
30
+ colorScheme,
31
+ height,
32
+ }: MSAViewerProps) {
33
+ const theme = useMemo(() => createJBrowseTheme(), [])
34
+ const model = useMemo(
35
+ () =>
36
+ MSAModelF().create({
37
+ type: 'MsaView',
38
+ ...(msa || tree || gff
39
+ ? { data: { msa: msa ?? '', tree: tree ?? '', gff } }
40
+ : {}),
41
+ ...(msaFilehandle ? { msaFilehandle } : {}),
42
+ ...(treeFilehandle ? { treeFilehandle } : {}),
43
+ ...(gffFilehandle ? { gffFilehandle } : {}),
44
+ ...(colorScheme ? { colorSchemeName: colorScheme } : {}),
45
+ ...(height ? { height } : {}),
46
+ }),
47
+ // eslint-disable-next-line react-hooks/exhaustive-deps
48
+ [],
49
+ )
50
+
51
+ const [ref, { width }] = useMeasure()
52
+ useEffect(() => {
53
+ if (width) {
54
+ requestAnimationFrame(() => {
55
+ model.setWidth(width)
56
+ })
57
+ }
58
+ }, [model, width])
59
+
60
+ return (
61
+ <ThemeProvider theme={theme}>
62
+ <div ref={ref}>
63
+ <Loading model={model} />
64
+ </div>
65
+ </ThemeProvider>
66
+ )
67
+ }
@@ -67,10 +67,10 @@ export default function SequenceTextArea({ str }: { str: [string, string][] }) {
67
67
  maxRows={10}
68
68
  fullWidth
69
69
  value={disp}
70
- InputProps={{
71
- readOnly: true,
72
- classes: {
73
- input: classes.textAreaFont,
70
+ slotProps={{
71
+ input: {
72
+ readOnly: true,
73
+ className: classes.textAreaFont,
74
74
  },
75
75
  }}
76
76
  />
@@ -1,10 +1,12 @@
1
- import React, { lazy, useEffect, useRef, useState } from 'react'
1
+ import React, { lazy, useCallback, useRef, useState } from 'react'
2
2
 
3
3
  import ArrowDropDownIcon from '@mui/icons-material/ArrowDropDown'
4
4
  import { IconButton, Menu, MenuItem } from '@mui/material'
5
5
  import { observer } from 'mobx-react'
6
6
  import { makeStyles } from 'tss-react/mui'
7
7
 
8
+ import { useWheelScroll } from '../useWheelScroll.ts'
9
+
8
10
  import type { MsaViewModel } from '../model.ts'
9
11
 
10
12
  const TrackInfoDialog = lazy(() => import('./dialogs/TrackInfoDialog.tsx'))
@@ -101,31 +103,13 @@ const Track = observer(function ({
101
103
  model: { height, error },
102
104
  } = track
103
105
  const ref = useRef<HTMLDivElement>(null)
104
- const scheduled = useRef(false)
105
- const deltaX = useRef(0)
106
- useEffect(() => {
107
- const curr = ref.current
108
- if (!curr) {
109
- return
110
- }
111
- function onWheel(event: WheelEvent) {
112
- deltaX.current += event.deltaX
113
-
114
- if (!scheduled.current) {
115
- scheduled.current = true
116
- requestAnimationFrame(() => {
117
- model.doScrollX(-deltaX.current)
118
- deltaX.current = 0
119
- scheduled.current = false
120
- })
121
- }
122
- event.preventDefault()
123
- }
124
- curr.addEventListener('wheel', onWheel, { passive: false })
125
- return () => {
126
- curr.removeEventListener('wheel', onWheel)
127
- }
128
- }, [model])
106
+ const onScrollX = useCallback(
107
+ (d: number) => {
108
+ model.doScrollX(d)
109
+ },
110
+ [model],
111
+ )
112
+ useWheelScroll({ ref, onScrollX })
129
113
 
130
114
  return (
131
115
  <div key={track.id} style={{ display: 'flex', height }}>
@@ -4,7 +4,6 @@ import { Dialog } from '@jbrowse/core/ui'
4
4
  import { Tab, Tabs } from '@mui/material'
5
5
 
6
6
  import InterProScanPanel from './InterProScanDialog.tsx'
7
- import TabPanel from './TabPanel.tsx'
8
7
  import UserProvidedResultPanel from './UserProvidedDomainsDialog.tsx'
9
8
 
10
9
  import type { MsaViewModel } from '../../model.ts'
@@ -35,12 +34,12 @@ export default function LaunchDomainViewDialog({
35
34
  <Tab value={0} label="Automatic lookup" />
36
35
  <Tab value={1} label="Manual" />
37
36
  </Tabs>
38
- <TabPanel value={choice} index={0}>
37
+ {choice === 0 ? (
39
38
  <InterProScanPanel model={model} handleClose={handleClose} />
40
- </TabPanel>
41
- <TabPanel value={choice} index={1}>
39
+ ) : null}
40
+ {choice === 1 ? (
42
41
  <UserProvidedResultPanel model={model} handleClose={handleClose} />
43
- </TabPanel>
42
+ ) : null}
44
43
  </Dialog>
45
44
  )
46
45
  }
@@ -57,32 +57,32 @@ const InterProScanDialog = observer(function ({
57
57
  category: 'Other category',
58
58
  },
59
59
  {
60
- name: 'SUPERFAMILY',
60
+ name: 'SuperFamily',
61
61
  category: 'Structural domains',
62
62
  checked: true,
63
63
  },
64
64
  {
65
- name: 'PANTHER',
65
+ name: 'Panther',
66
66
  category: 'Families, domains, sites & repeats',
67
67
  checked: true,
68
68
  },
69
69
  {
70
- name: 'Gene3D',
70
+ name: 'Gene3d',
71
71
  category: 'Structural domains',
72
72
  checked: true,
73
73
  },
74
74
  {
75
- name: 'Hamap',
75
+ name: 'HAMAP',
76
76
  category: 'Families, domains, sites & repeats',
77
77
  checked: true,
78
78
  },
79
79
  {
80
- name: 'ProSiteProfiles',
80
+ name: 'PrositeProfiles',
81
81
  category: 'Families, domains, sites & repeats',
82
82
  checked: true,
83
83
  },
84
84
  {
85
- name: 'ProSitePatterns',
85
+ name: 'PrositePatterns',
86
86
  category: 'Families, domains, sites & repeats',
87
87
  checked: true,
88
88
  },
@@ -108,7 +108,7 @@ const InterProScanDialog = observer(function ({
108
108
  checked: true,
109
109
  },
110
110
  {
111
- name: 'Pfam',
111
+ name: 'PfamA',
112
112
  category: 'Families, domains, sites & repeats',
113
113
  checked: true,
114
114
  },
@@ -46,16 +46,12 @@ const TreeSettings = observer(function TreeSettings({
46
46
  }: {
47
47
  model: MsaViewModel
48
48
  }) {
49
- const { classes } = useStyles()
50
49
  const {
51
50
  drawTree,
52
51
  drawLabels,
53
52
  drawNodeBubbles,
54
53
  labelsAlignRight,
55
- noTree,
56
54
  showBranchLen,
57
- treeWidthMatchesArea,
58
- treeWidth,
59
55
  } = model
60
56
 
61
57
  return (
@@ -99,31 +95,6 @@ const TreeSettings = observer(function TreeSettings({
99
95
  model.setDrawLabels(!drawLabels)
100
96
  }}
101
97
  />
102
- {noTree ? null : (
103
- <div>
104
- <Checkbox2
105
- checked={treeWidthMatchesArea}
106
- label="Make tree width fit to tree area?"
107
- onChange={() => {
108
- model.setTreeWidthMatchesArea(!treeWidthMatchesArea)
109
- }}
110
- />
111
- {treeWidthMatchesArea ? null : (
112
- <div className={classes.flex}>
113
- <Typography>Tree width ({treeWidth}px)</Typography>
114
- <Slider
115
- className={classes.field}
116
- min={50}
117
- max={600}
118
- value={treeWidth}
119
- onChange={(_, val) => {
120
- model.setTreeWidth(val)
121
- }}
122
- />
123
- </div>
124
- )}
125
- </div>
126
- )}
127
98
  </div>
128
99
  )
129
100
  })
@@ -136,7 +107,6 @@ const MSASettings = observer(function MSASettings({
136
107
  const { classes } = useStyles()
137
108
  const {
138
109
  bgColor,
139
- contrastLettering,
140
110
  colWidth,
141
111
  allowedGappyness,
142
112
  drawMsaLetters,
@@ -162,13 +132,6 @@ const MSASettings = observer(function MSASettings({
162
132
  model.setBgColor(!bgColor)
163
133
  }}
164
134
  />
165
- <Checkbox2
166
- checked={contrastLettering}
167
- label="Use contrast lettering"
168
- onChange={() => {
169
- model.setContrastLettering(!contrastLettering)
170
- }}
171
- />
172
135
  <Checkbox2
173
136
  checked={hideGaps}
174
137
  label="Enable hiding gappy columns?"
@@ -0,0 +1,35 @@
1
+ import React from 'react'
2
+
3
+ import CascadingMenuButton from '@jbrowse/core/ui/CascadingMenuButton'
4
+ import Palette from '@mui/icons-material/Palette'
5
+ import { observer } from 'mobx-react'
6
+
7
+ import colorSchemes from '../../colorSchemes.ts'
8
+
9
+ import type { MsaViewModel } from '../../model.ts'
10
+
11
+ const ColorSchemeMenu = observer(function ({ model }: { model: MsaViewModel }) {
12
+ const { colorSchemeName } = model
13
+ return (
14
+ <CascadingMenuButton
15
+ closeAfterItemClick
16
+ anchorOrigin={{ vertical: 'bottom', horizontal: 'left' }}
17
+ transformOrigin={{ vertical: 'top', horizontal: 'left' }}
18
+ menuItems={Object.keys(colorSchemes).map(
19
+ option =>
20
+ ({
21
+ label: option,
22
+ type: 'radio',
23
+ checked: colorSchemeName === option,
24
+ onClick: () => {
25
+ model.setColorSchemeName(option)
26
+ },
27
+ }) as const,
28
+ )}
29
+ >
30
+ <Palette />
31
+ </CascadingMenuButton>
32
+ )
33
+ })
34
+
35
+ export default ColorSchemeMenu
@@ -0,0 +1,84 @@
1
+ import React, { lazy } from 'react'
2
+
3
+ import CascadingMenuButton from '@jbrowse/core/ui/CascadingMenuButton'
4
+ import Assignment from '@mui/icons-material/Assignment'
5
+ import FolderOpen from '@mui/icons-material/FolderOpen'
6
+ import PhotoCamera from '@mui/icons-material/PhotoCamera'
7
+ import Settings from '@mui/icons-material/Settings'
8
+ import { observer } from 'mobx-react'
9
+
10
+ import { getDomainMenu } from './getDomainsMenu.ts'
11
+
12
+ import type { MsaViewModel } from '../../model.ts'
13
+
14
+ const MetadataDialog = lazy(() => import('../dialogs/MetadataDialog.tsx'))
15
+ const ExportSVGDialog = lazy(() => import('../dialogs/ExportSVGDialog.tsx'))
16
+ const SettingsDialog = lazy(() => import('../dialogs/SettingsDialog.tsx'))
17
+
18
+ const FileMenu = observer(({ model }: { model: MsaViewModel }) => {
19
+ return (
20
+ <CascadingMenuButton
21
+ menuItems={[
22
+ {
23
+ label: 'Return to import form',
24
+ icon: FolderOpen,
25
+ onClick: () => {
26
+ model.reset()
27
+ },
28
+ },
29
+ {
30
+ label: 'Metadata',
31
+ icon: Assignment,
32
+ onClick: () => {
33
+ model.queueDialog(onClose => [
34
+ MetadataDialog,
35
+ {
36
+ model,
37
+ onClose,
38
+ },
39
+ ])
40
+ },
41
+ },
42
+ {
43
+ label: 'More settings',
44
+ icon: Settings,
45
+ onClick: () => {
46
+ model.queueDialog(onClose => [
47
+ SettingsDialog,
48
+ {
49
+ model,
50
+ onClose,
51
+ },
52
+ ])
53
+ },
54
+ },
55
+ {
56
+ label: 'Domain settings',
57
+ type: 'subMenu',
58
+ subMenu: getDomainMenu({
59
+ model,
60
+ }),
61
+ },
62
+ {
63
+ label: 'Export SVG',
64
+ icon: PhotoCamera,
65
+ onClick: () => {
66
+ model.queueDialog(onClose => [
67
+ ExportSVGDialog,
68
+ {
69
+ onClose,
70
+ model,
71
+ },
72
+ ])
73
+ },
74
+ },
75
+ ]}
76
+ anchorOrigin={{ vertical: 'bottom', horizontal: 'left' }}
77
+ transformOrigin={{ vertical: 'top', horizontal: 'left' }}
78
+ >
79
+ <FolderOpen />
80
+ </CascadingMenuButton>
81
+ )
82
+ })
83
+
84
+ export default FileMenu
@@ -5,15 +5,16 @@ import Help from '@mui/icons-material/Help'
5
5
  import { IconButton } from '@mui/material'
6
6
  import { observer } from 'mobx-react'
7
7
 
8
+ import ColorSchemeMenu from './ColorSchemeMenu.tsx'
9
+ import FileMenu from './FileMenu.tsx'
8
10
  import GappynessSlider from './GappynessSlider.tsx'
9
11
  import HeaderInfoArea from './HeaderInfoArea.tsx'
10
- import HeaderMenu from './HeaderMenu.tsx'
11
12
  import HeaderStatusArea from './HeaderStatusArea.tsx'
13
+ import MSASettingsMenu from './MSASettingsMenu.tsx'
12
14
  import MultiAlignmentSelector from './MultiAlignmentSelector.tsx'
13
- import SettingsMenu from './SettingsMenu.tsx'
15
+ import TreeSettingsMenu from './TreeSettingsMenu.tsx'
14
16
  import ZoomControls from './ZoomControls.tsx'
15
17
  import ZoomMenu from './ZoomMenu.tsx'
16
- import ZoomStar from './ZoomStar.tsx'
17
18
 
18
19
  import type { MsaViewModel } from '../../model.ts'
19
20
 
@@ -26,10 +27,11 @@ const Header = observer(function ({ model }: { model: MsaViewModel }) {
26
27
  }, [model, height])
27
28
  return (
28
29
  <div ref={ref} style={{ display: 'flex' }}>
29
- <HeaderMenu model={model} />
30
- <SettingsMenu model={model} />
30
+ <FileMenu model={model} />
31
+ <ColorSchemeMenu model={model} />
32
+ <TreeSettingsMenu model={model} />
33
+ <MSASettingsMenu model={model} />
31
34
  <ZoomControls model={model} />
32
- {model.showZoomStar ? <ZoomStar model={model} /> : null}
33
35
  <ZoomMenu model={model} />
34
36
  <GappynessSlider model={model} />
35
37
  <div style={{ paddingLeft: 20, margin: 'auto' }}>