react-msaview 5.0.16 → 5.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. package/bundle/index.js +23 -23
  2. package/bundle/index.js.LICENSE.txt +1 -1
  3. package/bundle/index.js.map +1 -1
  4. package/dist/clustalX.d.ts +5 -0
  5. package/dist/clustalX.js +88 -0
  6. package/dist/clustalX.js.map +1 -0
  7. package/dist/components/SequenceTextArea.js +2 -2
  8. package/dist/components/SequenceTextArea.js.map +1 -1
  9. package/dist/components/TextTrack.js +3 -5
  10. package/dist/components/TextTrack.js.map +1 -1
  11. package/dist/components/Track.d.ts +2 -1
  12. package/dist/components/Track.js +4 -3
  13. package/dist/components/Track.js.map +1 -1
  14. package/dist/components/VerticalScrollbar.js +6 -31
  15. package/dist/components/VerticalScrollbar.js.map +1 -1
  16. package/dist/components/dialogs/InterProScanDialog.js +5 -3
  17. package/dist/components/dialogs/InterProScanDialog.js.map +1 -1
  18. package/dist/components/header/HeaderStatusArea.js +4 -2
  19. package/dist/components/header/HeaderStatusArea.js.map +1 -1
  20. package/dist/components/header/MSASettingsMenu.js +2 -27
  21. package/dist/components/header/MSASettingsMenu.js.map +1 -1
  22. package/dist/components/header/SettingsMenu.js +4 -69
  23. package/dist/components/header/SettingsMenu.js.map +1 -1
  24. package/dist/components/header/TreeSettingsMenu.js +3 -41
  25. package/dist/components/header/TreeSettingsMenu.js.map +1 -1
  26. package/dist/components/header/settingsMenuItems.d.ts +4 -0
  27. package/dist/components/header/settingsMenuItems.js +59 -0
  28. package/dist/components/header/settingsMenuItems.js.map +1 -0
  29. package/dist/components/minimap/Minimap.js +6 -31
  30. package/dist/components/minimap/Minimap.js.map +1 -1
  31. package/dist/components/msa/MSACanvasBlock.js +31 -33
  32. package/dist/components/msa/MSACanvasBlock.js.map +1 -1
  33. package/dist/components/msa/renderMSABlock.js +15 -9
  34. package/dist/components/msa/renderMSABlock.js.map +1 -1
  35. package/dist/components/tree/TreeCanvas.js +12 -14
  36. package/dist/components/tree/TreeCanvas.js.map +1 -1
  37. package/dist/components/tree/renderTreeCanvas.d.ts +9 -3
  38. package/dist/components/tree/renderTreeCanvas.js +14 -40
  39. package/dist/components/tree/renderTreeCanvas.js.map +1 -1
  40. package/dist/fetchUtils.d.ts +2 -1
  41. package/dist/fetchUtils.js +16 -2
  42. package/dist/fetchUtils.js.map +1 -1
  43. package/dist/hierarchy.d.ts +2 -0
  44. package/dist/hierarchy.js +25 -0
  45. package/dist/hierarchy.js.map +1 -1
  46. package/dist/launchInterProScan.d.ts +2 -1
  47. package/dist/launchInterProScan.js +31 -15
  48. package/dist/launchInterProScan.js.map +1 -1
  49. package/dist/model.d.ts +150 -494
  50. package/dist/model.js +23 -116
  51. package/dist/model.js.map +1 -1
  52. package/dist/useColorContrast.d.ts +10 -0
  53. package/dist/useColorContrast.js +13 -0
  54. package/dist/useColorContrast.js.map +1 -0
  55. package/dist/useDragScroll.d.ts +12 -0
  56. package/dist/useDragScroll.js +47 -0
  57. package/dist/useDragScroll.js.map +1 -0
  58. package/dist/version.d.ts +1 -1
  59. package/dist/version.js +1 -1
  60. package/dist/version.js.map +1 -1
  61. package/package.json +6 -6
  62. package/src/clustalX.test.ts +76 -0
  63. package/src/clustalX.ts +131 -0
  64. package/src/components/SequenceTextArea.tsx +2 -2
  65. package/src/components/TextTrack.tsx +3 -8
  66. package/src/components/Track.tsx +6 -9
  67. package/src/components/VerticalScrollbar.tsx +13 -40
  68. package/src/components/dialogs/InterProScanDialog.tsx +7 -1
  69. package/src/components/header/HeaderStatusArea.tsx +6 -1
  70. package/src/components/header/MSASettingsMenu.tsx +3 -27
  71. package/src/components/header/SettingsMenu.tsx +7 -79
  72. package/src/components/header/TreeSettingsMenu.tsx +4 -48
  73. package/src/components/header/settingsMenuItems.ts +68 -0
  74. package/src/components/minimap/Minimap.tsx +13 -38
  75. package/src/components/msa/MSACanvasBlock.tsx +53 -36
  76. package/src/components/msa/renderMSABlock.ts +16 -9
  77. package/src/components/tree/TreeCanvas.tsx +13 -35
  78. package/src/components/tree/renderTreeCanvas.ts +23 -38
  79. package/src/fetchUtils.ts +16 -2
  80. package/src/hierarchy.ts +27 -0
  81. package/src/launchInterProScan.ts +34 -10
  82. package/src/model.ts +32 -143
  83. package/src/useColorContrast.ts +18 -0
  84. package/src/useDragScroll.ts +55 -0
  85. package/src/version.ts +1 -1
@@ -0,0 +1,131 @@
1
+ // ClustalX consensus coloring.
2
+ // ref http://www.jalview.org/help/html/colourSchemes/clustal.html
3
+
4
+ const BLUE = 'rgb(128,179,230)'
5
+ const RED = '#d88'
6
+ const MAGENTA_E = 'rgb(192, 72, 192)'
7
+ const MAGENTA_D = 'rgb(204, 77, 204)'
8
+ const GREEN = '#8f8'
9
+ const GREEN_ST = 'rgb(26,204,26)'
10
+ const PINK = 'rgb(240, 128, 128)'
11
+ const ORANGE = 'rgb(240, 144, 72)'
12
+ const YELLOW = 'rgb(204, 204, 0)'
13
+ const CYAN = 'rgb(26, 179, 179)'
14
+
15
+ /**
16
+ * Computes the ClustalX letter->color map for a single alignment column given
17
+ * its residue counts (`stats`) and total residue count (`total`).
18
+ */
19
+ export function clustalXColumnColors(
20
+ stats: Record<string, number>,
21
+ total: number,
22
+ ) {
23
+ const {
24
+ W = 0,
25
+ L = 0,
26
+ V = 0,
27
+ I = 0,
28
+ M = 0,
29
+ A = 0,
30
+ F = 0,
31
+ C = 0,
32
+ H = 0,
33
+ P = 0,
34
+ R = 0,
35
+ K = 0,
36
+ Q = 0,
37
+ E = 0,
38
+ D = 0,
39
+ T = 0,
40
+ S = 0,
41
+ G = 0,
42
+ Y = 0,
43
+ N = 0,
44
+ } = stats
45
+
46
+ const hydrophobic = W + L + V + I + M + A + F + C + H + P + Y
47
+ const KR = K + R
48
+ const QE = Q + E
49
+ const ED = E + D
50
+ const TS = T + S
51
+ const p = (n: number) => n / total
52
+
53
+ const colors: Record<string, string> = {}
54
+
55
+ if (p(hydrophobic) > 0.6) {
56
+ colors.W = BLUE
57
+ colors.L = BLUE
58
+ colors.V = BLUE
59
+ colors.A = BLUE
60
+ colors.I = BLUE
61
+ colors.M = BLUE
62
+ colors.F = BLUE
63
+ colors.C = BLUE
64
+ }
65
+
66
+ if (p(KR) > 0.6 || p(K) > 0.8 || p(R) > 0.8 || p(Q) > 0.8) {
67
+ colors.K = RED
68
+ colors.R = RED
69
+ }
70
+
71
+ if (p(KR) > 0.6 || p(QE) > 0.5 || p(E) > 0.8 || p(Q) > 0.8 || p(D) > 0.8) {
72
+ colors.E = MAGENTA_E
73
+ }
74
+
75
+ if (p(KR) > 0.6 || p(ED) > 0.5 || p(K) > 0.8 || p(R) > 0.8 || p(Q) > 0.8) {
76
+ colors.D = MAGENTA_D
77
+ }
78
+
79
+ if (p(N) > 0.5 || p(Y) > 0.85) {
80
+ colors.N = GREEN
81
+ }
82
+
83
+ if (
84
+ p(KR) > 0.6 ||
85
+ p(QE) > 0.6 ||
86
+ p(Q) > 0.85 ||
87
+ p(E) > 0.85 ||
88
+ p(K) > 0.85 ||
89
+ p(R) > 0.85
90
+ ) {
91
+ colors.Q = GREEN
92
+ }
93
+
94
+ if (p(hydrophobic) > 0.6 || p(TS) > 0.5 || p(S) > 0.85 || p(T) > 0.85) {
95
+ colors.S = GREEN_ST
96
+ colors.T = GREEN_ST
97
+ }
98
+
99
+ if (p(C) > 0.85) {
100
+ colors.C = PINK
101
+ }
102
+
103
+ if (G > 0) {
104
+ colors.G = ORANGE
105
+ }
106
+
107
+ if (P > 0) {
108
+ colors.P = YELLOW
109
+ }
110
+
111
+ if (
112
+ p(hydrophobic) > 0.6 ||
113
+ p(W) > 0.85 ||
114
+ p(Y) > 0.85 ||
115
+ p(A) > 0.85 ||
116
+ p(C) > 0.85 ||
117
+ p(P) > 0.85 ||
118
+ p(Q) > 0.85 ||
119
+ p(F) > 0.85 ||
120
+ p(H) > 0.85 ||
121
+ p(I) > 0.85 ||
122
+ p(L) > 0.85 ||
123
+ p(M) > 0.85 ||
124
+ p(V) > 0.85
125
+ ) {
126
+ colors.H = CYAN
127
+ colors.Y = CYAN
128
+ }
129
+
130
+ return colors
131
+ }
@@ -27,8 +27,8 @@ export default function SequenceTextArea({ str }: { str: [string, string][] }) {
27
27
  const removeGaps = (s: string) => s.replaceAll('-', '').replaceAll('.', '')
28
28
  const disp = str
29
29
  .map(([s1, s2]) => [s1, showGaps ? s2 : removeGaps(s2)] as const)
30
- .filter(f => (showEmpty ? true : !!f[1]))
31
- .map(([s1, s2]) => `>${s1}\n${showGaps ? s2 : removeGaps(s2)}`)
30
+ .filter(([, s2]) => showEmpty || !!s2)
31
+ .map(([s1, s2]) => `>${s1}\n${s2}`)
32
32
  .join('\n')
33
33
  return (
34
34
  <>
@@ -1,9 +1,8 @@
1
- import React, { useEffect, useMemo, useRef } from 'react'
1
+ import React, { useEffect, useRef } from 'react'
2
2
 
3
- import { useTheme } from '@mui/material'
4
3
  import { observer } from 'mobx-react'
5
4
 
6
- import { colorContrast } from '../util.ts'
5
+ import { useColorContrast } from '../useColorContrast.ts'
7
6
  import { drawTextTrackContent } from './tracks/renderTracksSvg.ts'
8
7
 
9
8
  import type { MsaViewModel } from '../model.ts'
@@ -33,12 +32,8 @@ const AnnotationBlock = observer(function ({
33
32
  } = track
34
33
 
35
34
  const colorScheme = customColorScheme || modelColorScheme
36
- const theme = useTheme()
37
35
  const ref = useRef<HTMLCanvasElement>(null)
38
- const contrastScheme = useMemo(
39
- () => colorContrast(colorScheme, theme),
40
- [colorScheme, theme],
41
- )
36
+ const { contrastScheme } = useColorContrast(colorScheme)
42
37
  useEffect(() => {
43
38
  if (!ref.current) {
44
39
  return
@@ -8,6 +8,7 @@ import { makeStyles } from 'tss-react/mui'
8
8
  import { useWheelScroll } from '../useWheelScroll.ts'
9
9
 
10
10
  import type { MsaViewModel } from '../model.ts'
11
+ import type { BasicTrack } from '../types.ts'
11
12
 
12
13
  const TrackInfoDialog = lazy(() => import('./dialogs/TrackInfoDialog.tsx'))
13
14
 
@@ -22,7 +23,7 @@ const TrackLabel = observer(function TrackLabel({
22
23
  track,
23
24
  }: {
24
25
  model: MsaViewModel
25
- track: any
26
+ track: BasicTrack
26
27
  }) {
27
28
  const [anchorEl, setAnchorEl] = useState<HTMLButtonElement>()
28
29
  const { drawLabels, rowHeight, treeAreaWidth: width } = model
@@ -96,11 +97,11 @@ const Track = observer(function ({
96
97
  track,
97
98
  }: {
98
99
  model: MsaViewModel
99
- track: any
100
+ track: BasicTrack
100
101
  }) {
101
102
  const { resizeHandleWidth, colWidth, scrollX, numColumns } = model
102
103
  const {
103
- model: { height, error },
104
+ model: { height },
104
105
  } = track
105
106
  const ref = useRef<HTMLDivElement>(null)
106
107
  const onScrollX = useCallback(
@@ -112,7 +113,7 @@ const Track = observer(function ({
112
113
  useWheelScroll({ ref, onScrollX })
113
114
 
114
115
  return (
115
- <div key={track.id} style={{ display: 'flex', height }}>
116
+ <div style={{ display: 'flex', height }}>
116
117
  <TrackLabel model={model} track={track} />
117
118
  <div style={{ width: resizeHandleWidth, flexShrink: 0 }} />
118
119
  <div
@@ -134,11 +135,7 @@ const Track = observer(function ({
134
135
  model.setMousePos(undefined, undefined)
135
136
  }}
136
137
  >
137
- {error ? (
138
- <div style={{ color: 'red', fontSize: 10 }}>{`${error}`}</div>
139
- ) : (
140
- <track.ReactComponent model={model} track={track} />
141
- )}
138
+ <track.ReactComponent model={model} track={track} />
142
139
  </div>
143
140
  </div>
144
141
  )
@@ -1,54 +1,30 @@
1
- import React, { useEffect, useRef, useState } from 'react'
1
+ import React, { useCallback, useState } from 'react'
2
2
 
3
3
  import { clamp } from '@jbrowse/core/util'
4
4
  import { observer } from 'mobx-react'
5
5
 
6
+ import { useDragScroll } from '../useDragScroll.ts'
7
+
6
8
  import type { MsaViewModel } from '../model.ts'
7
9
 
8
10
  const VerticalScrollbar = observer(({ model }: { model: MsaViewModel }) => {
9
11
  const { msaAreaHeight, scrollY, totalHeight } = model
10
12
  const [hovered, setHovered] = useState(false)
11
- const scheduled = useRef(false)
12
- const [mouseDown, setMouseDown] = useState<{
13
- clientY: number
14
- scrollY: number
15
- }>()
16
13
  const fill = 'rgba(66, 119, 127, 0.3)'
17
14
  const unit = msaAreaHeight / totalHeight
18
15
  const top = -scrollY
19
16
  const bottom = top + msaAreaHeight
20
17
  const t = top * unit
21
18
  const b = bottom * unit
22
- useEffect(() => {
23
- function fn(event: MouseEvent) {
24
- if (mouseDown !== undefined) {
25
- if (!scheduled.current) {
26
- scheduled.current = true
27
- window.requestAnimationFrame(() => {
28
- model.setScrollY(
29
- clamp(
30
- mouseDown.scrollY - (event.clientY - mouseDown.clientY) / unit,
31
- -totalHeight,
32
- 0,
33
- ),
34
- )
35
- scheduled.current = false
36
- })
37
- }
38
- }
39
- }
40
- function fn2() {
41
- setMouseDown(undefined)
42
- }
43
- if (mouseDown !== undefined) {
44
- document.addEventListener('mousemove', fn)
45
- document.addEventListener('mouseup', fn2)
46
- return () => {
47
- document.removeEventListener('mousemove', fn)
48
- document.removeEventListener('mousemove', fn2)
49
- }
50
- }
51
- }, [model, unit, totalHeight, mouseDown])
19
+ const { startDrag } = useDragScroll(
20
+ 'y',
21
+ useCallback(
22
+ (delta: number, startScroll: number) => {
23
+ model.setScrollY(clamp(startScroll - delta / unit, -totalHeight, 0))
24
+ },
25
+ [model, unit, totalHeight],
26
+ ),
27
+ )
52
28
  return (
53
29
  <div
54
30
  style={{
@@ -79,10 +55,7 @@ const VerticalScrollbar = observer(({ model }: { model: MsaViewModel }) => {
79
55
  setHovered(false)
80
56
  }}
81
57
  onMouseDown={event => {
82
- setMouseDown({
83
- clientY: event.clientY,
84
- scrollY: model.scrollY,
85
- })
58
+ startDrag(event, model.scrollY)
86
59
  }}
87
60
  />
88
61
  </div>
@@ -237,6 +237,7 @@ const InterProScanDialog = observer(function ({
237
237
  variant="contained"
238
238
  color="primary"
239
239
  onClick={() => {
240
+ const controller = new AbortController()
240
241
  // eslint-disable-next-line @typescript-eslint/no-floating-promises
241
242
  ;(async () => {
242
243
  try {
@@ -255,9 +256,14 @@ const InterProScanDialog = observer(function ({
255
256
  .map(row => `>${row[0]}\n${row[1]}`)
256
257
  .join('\n'),
257
258
  onProgress: arg => {
258
- model.setStatus(arg)
259
+ model.setStatus(
260
+ arg
261
+ ? { ...arg, onCancel: () => { controller.abort() } }
262
+ : undefined,
263
+ )
259
264
  },
260
265
  model,
266
+ signal: controller.signal,
261
267
  })
262
268
  } catch (e) {
263
269
  console.error(e)
@@ -1,7 +1,7 @@
1
1
  import React from 'react'
2
2
 
3
3
  import { LoadingEllipses } from '@jbrowse/core/ui'
4
- import { Typography } from '@mui/material'
4
+ import { Button, Typography } from '@mui/material'
5
5
  import { observer } from 'mobx-react'
6
6
  import { makeStyles } from 'tss-react/mui'
7
7
 
@@ -24,6 +24,11 @@ const HeaderStatusArea = observer(({ model }: { model: MsaViewModel }) => {
24
24
  <a href={status.url} target="_blank" rel="noreferrer">
25
25
  (status)
26
26
  </a>
27
+ ) : null}{' '}
28
+ {status.onCancel ? (
29
+ <Button size="small" onClick={() => status.onCancel?.()}>
30
+ Cancel
31
+ </Button>
27
32
  ) : null}
28
33
  </Typography>
29
34
  ) : null
@@ -4,41 +4,17 @@ import CascadingMenuButton from '@jbrowse/core/ui/CascadingMenuButton'
4
4
  import Tune from '@mui/icons-material/Tune'
5
5
  import { observer } from 'mobx-react'
6
6
 
7
+ import { msaSettingsMenuItems } from './settingsMenuItems.ts'
8
+
7
9
  import type { MsaViewModel } from '../../model.ts'
8
10
 
9
11
  const MSASettingsMenu = observer(function ({ model }: { model: MsaViewModel }) {
10
- const { drawMsaLetters, hideGaps, bgColor } = model
11
12
  return (
12
13
  <CascadingMenuButton
13
14
  closeAfterItemClick={false}
14
15
  anchorOrigin={{ vertical: 'bottom', horizontal: 'left' }}
15
16
  transformOrigin={{ vertical: 'top', horizontal: 'left' }}
16
- menuItems={[
17
- {
18
- label: 'Draw letters',
19
- type: 'checkbox',
20
- checked: drawMsaLetters,
21
- onClick: () => {
22
- model.setDrawMsaLetters(!drawMsaLetters)
23
- },
24
- },
25
- {
26
- label: 'Color letters instead of background of tiles',
27
- type: 'checkbox',
28
- checked: !bgColor,
29
- onClick: () => {
30
- model.setBgColor(!bgColor)
31
- },
32
- },
33
- {
34
- label: 'Enable hiding gappy columns?',
35
- type: 'checkbox',
36
- checked: hideGaps,
37
- onClick: () => {
38
- model.setHideGaps(!hideGaps)
39
- },
40
- },
41
- ]}
17
+ menuItems={msaSettingsMenuItems(model)}
42
18
  >
43
19
  <Tune />
44
20
  </CascadingMenuButton>
@@ -6,22 +6,16 @@ import Palette from '@mui/icons-material/Palette'
6
6
  import Settings from '@mui/icons-material/Settings'
7
7
  import { observer } from 'mobx-react'
8
8
 
9
+ import {
10
+ msaSettingsMenuItems,
11
+ treeSettingsMenuItems,
12
+ } from './settingsMenuItems.ts'
9
13
  import colorSchemes from '../../colorSchemes.ts'
10
14
 
11
15
  import type { MsaViewModel } from '../../model.ts'
12
16
 
13
17
  const SettingsMenu = observer(function ({ model }: { model: MsaViewModel }) {
14
- const {
15
- colorSchemeName,
16
- drawMsaLetters,
17
- hideGaps,
18
- bgColor,
19
- drawTree,
20
- showBranchLen,
21
- labelsAlignRight,
22
- drawNodeBubbles,
23
- drawLabels,
24
- } = model
18
+ const { colorSchemeName } = model
25
19
  return (
26
20
  <CascadingMenuButton
27
21
  closeAfterItemClick={false}
@@ -45,79 +39,13 @@ const SettingsMenu = observer(function ({ model }: { model: MsaViewModel }) {
45
39
  {
46
40
  label: 'MSA settings',
47
41
  type: 'subMenu',
48
- subMenu: [
49
- {
50
- label: 'Draw letters',
51
- type: 'checkbox',
52
- checked: drawMsaLetters,
53
- onClick: () => {
54
- model.setDrawMsaLetters(!drawMsaLetters)
55
- },
56
- },
57
- {
58
- label: 'Color letters instead of background of tiles',
59
- type: 'checkbox',
60
- checked: !bgColor,
61
- onClick: () => {
62
- model.setBgColor(!bgColor)
63
- },
64
- },
65
- {
66
- label: 'Enable hiding gappy columns?',
67
- type: 'checkbox',
68
- checked: hideGaps,
69
- onClick: () => {
70
- model.setHideGaps(!hideGaps)
71
- },
72
- },
73
- ],
42
+ subMenu: msaSettingsMenuItems(model),
74
43
  },
75
44
  {
76
45
  label: 'Tree settings',
77
46
  type: 'subMenu',
78
47
  icon: AccountTree,
79
- subMenu: [
80
- {
81
- label: 'Show branch length',
82
- type: 'checkbox',
83
- checked: showBranchLen,
84
- onClick: () => {
85
- model.setShowBranchLen(!showBranchLen)
86
- },
87
- },
88
- {
89
- label: 'Show tree',
90
- type: 'checkbox',
91
- checked: drawTree,
92
- onClick: () => {
93
- model.setDrawTree(!drawTree)
94
- },
95
- },
96
- {
97
- label: 'Draw clickable bubbles on tree branches',
98
- type: 'checkbox',
99
- checked: drawNodeBubbles,
100
- onClick: () => {
101
- model.setDrawNodeBubbles(!drawNodeBubbles)
102
- },
103
- },
104
- {
105
- label: 'Tree labels align right',
106
- type: 'checkbox',
107
- checked: labelsAlignRight,
108
- onClick: () => {
109
- model.setLabelsAlignRight(!labelsAlignRight)
110
- },
111
- },
112
- {
113
- label: 'Draw labels',
114
- type: 'checkbox',
115
- checked: drawLabels,
116
- onClick: () => {
117
- model.setDrawLabels(!drawLabels)
118
- },
119
- },
120
- ],
48
+ subMenu: treeSettingsMenuItems(model),
121
49
  },
122
50
  ]}
123
51
  >
@@ -4,6 +4,8 @@ import CascadingMenuButton from '@jbrowse/core/ui/CascadingMenuButton'
4
4
  import AccountTree from '@mui/icons-material/AccountTree'
5
5
  import { observer } from 'mobx-react'
6
6
 
7
+ import { treeSettingsMenuItems } from './settingsMenuItems.ts'
8
+
7
9
  import type { MsaViewModel } from '../../model.ts'
8
10
 
9
11
  const TreeSettingsMenu = observer(function ({
@@ -11,60 +13,14 @@ const TreeSettingsMenu = observer(function ({
11
13
  }: {
12
14
  model: MsaViewModel
13
15
  }) {
14
- const {
15
- drawTree,
16
- showBranchLen,
17
- labelsAlignRight,
18
- drawNodeBubbles,
19
- drawLabels,
20
- rows,
21
- } = model
16
+ const { rows } = model
22
17
  return (
23
18
  <CascadingMenuButton
24
19
  closeAfterItemClick={false}
25
20
  anchorOrigin={{ vertical: 'bottom', horizontal: 'left' }}
26
21
  transformOrigin={{ vertical: 'top', horizontal: 'left' }}
27
22
  menuItems={[
28
- {
29
- label: 'Show branch length',
30
- type: 'checkbox',
31
- checked: showBranchLen,
32
- onClick: () => {
33
- model.setShowBranchLen(!showBranchLen)
34
- },
35
- },
36
- {
37
- label: 'Show tree',
38
- type: 'checkbox',
39
- checked: drawTree,
40
- onClick: () => {
41
- model.setDrawTree(!drawTree)
42
- },
43
- },
44
- {
45
- label: 'Draw clickable bubbles on tree branches',
46
- type: 'checkbox',
47
- checked: drawNodeBubbles,
48
- onClick: () => {
49
- model.setDrawNodeBubbles(!drawNodeBubbles)
50
- },
51
- },
52
- {
53
- label: 'Tree labels align right',
54
- type: 'checkbox',
55
- checked: labelsAlignRight,
56
- onClick: () => {
57
- model.setLabelsAlignRight(!labelsAlignRight)
58
- },
59
- },
60
- {
61
- label: 'Draw labels',
62
- type: 'checkbox',
63
- checked: drawLabels,
64
- onClick: () => {
65
- model.setDrawLabels(!drawLabels)
66
- },
67
- },
23
+ ...treeSettingsMenuItems(model),
68
24
  ...(rows.length >= 2
69
25
  ? [
70
26
  {
@@ -0,0 +1,68 @@
1
+ import type { MsaViewModel } from '../../model.ts'
2
+ import type { MenuItem } from '@jbrowse/core/ui/Menu'
3
+
4
+ export function msaSettingsMenuItems(model: MsaViewModel): MenuItem[] {
5
+ const { drawMsaLetters, hideGaps, bgColor } = model
6
+ return [
7
+ {
8
+ label: 'Draw letters',
9
+ type: 'checkbox',
10
+ checked: drawMsaLetters,
11
+ onClick: () => { model.setDrawMsaLetters(!drawMsaLetters) },
12
+ },
13
+ {
14
+ label: 'Color letters instead of background of tiles',
15
+ type: 'checkbox',
16
+ checked: !bgColor,
17
+ onClick: () => { model.setBgColor(!bgColor) },
18
+ },
19
+ {
20
+ label: 'Enable hiding gappy columns?',
21
+ type: 'checkbox',
22
+ checked: hideGaps,
23
+ onClick: () => { model.setHideGaps(!hideGaps) },
24
+ },
25
+ ]
26
+ }
27
+
28
+ export function treeSettingsMenuItems(model: MsaViewModel): MenuItem[] {
29
+ const {
30
+ drawTree,
31
+ showBranchLen,
32
+ labelsAlignRight,
33
+ drawNodeBubbles,
34
+ drawLabels,
35
+ } = model
36
+ return [
37
+ {
38
+ label: 'Show branch length',
39
+ type: 'checkbox',
40
+ checked: showBranchLen,
41
+ onClick: () => { model.setShowBranchLen(!showBranchLen) },
42
+ },
43
+ {
44
+ label: 'Show tree',
45
+ type: 'checkbox',
46
+ checked: drawTree,
47
+ onClick: () => { model.setDrawTree(!drawTree) },
48
+ },
49
+ {
50
+ label: 'Draw clickable bubbles on tree branches',
51
+ type: 'checkbox',
52
+ checked: drawNodeBubbles,
53
+ onClick: () => { model.setDrawNodeBubbles(!drawNodeBubbles) },
54
+ },
55
+ {
56
+ label: 'Tree labels align right',
57
+ type: 'checkbox',
58
+ checked: labelsAlignRight,
59
+ onClick: () => { model.setLabelsAlignRight(!labelsAlignRight) },
60
+ },
61
+ {
62
+ label: 'Draw labels',
63
+ type: 'checkbox',
64
+ checked: drawLabels,
65
+ onClick: () => { model.setDrawLabels(!drawLabels) },
66
+ },
67
+ ]
68
+ }