react-msaview 5.4.1 → 5.6.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 (180) hide show
  1. package/LICENSE +21 -0
  2. package/bundle/index.js +487 -149
  3. package/bundle/index.js.map +7 -1
  4. package/dist/colorSchemes.js +11 -7
  5. package/dist/colorSchemes.js.map +1 -1
  6. package/dist/components/ConservationTrack.js +20 -26
  7. package/dist/components/ConservationTrack.js.map +1 -1
  8. package/dist/components/MSAViewer.d.ts +13 -1
  9. package/dist/components/MSAViewer.js +13 -7
  10. package/dist/components/MSAViewer.js.map +1 -1
  11. package/dist/components/SequenceTextArea.js +1 -1
  12. package/dist/components/SequenceTextArea.js.map +1 -1
  13. package/dist/components/TextTrack.js +20 -27
  14. package/dist/components/TextTrack.js.map +1 -1
  15. package/dist/components/Track.js +9 -6
  16. package/dist/components/Track.js.map +1 -1
  17. package/dist/components/dialogs/FeatureDialog.js +27 -23
  18. package/dist/components/dialogs/FeatureDialog.js.map +1 -1
  19. package/dist/components/dialogs/TrackInfoDialog.js +1 -1
  20. package/dist/components/dialogs/TrackInfoDialog.js.map +1 -1
  21. package/dist/components/header/ColorSchemeMenu.js +1 -1
  22. package/dist/components/header/ColorSchemeMenu.js.map +1 -1
  23. package/dist/components/header/FileMenu.js +1 -16
  24. package/dist/components/header/FileMenu.js.map +1 -1
  25. package/dist/components/header/Header.js +6 -5
  26. package/dist/components/header/Header.js.map +1 -1
  27. package/dist/components/header/HeaderInfoArea.js +1 -1
  28. package/dist/components/header/HeaderInfoArea.js.map +1 -1
  29. package/dist/components/header/HeaderStatusArea.js +1 -1
  30. package/dist/components/header/HeaderStatusArea.js.map +1 -1
  31. package/dist/components/header/MSASettingsMenu.js +1 -1
  32. package/dist/components/header/MSASettingsMenu.js.map +1 -1
  33. package/dist/components/header/MultiAlignmentSelector.js +0 -0
  34. package/dist/components/header/MultiAlignmentSelector.js.map +1 -1
  35. package/dist/components/header/TreeSettingsMenu.js +1 -1
  36. package/dist/components/header/TreeSettingsMenu.js.map +1 -1
  37. package/dist/components/header/ZoomControls.js +14 -12
  38. package/dist/components/header/ZoomControls.js.map +1 -1
  39. package/dist/components/header/ZoomMenu.js +1 -10
  40. package/dist/components/header/ZoomMenu.js.map +1 -1
  41. package/dist/components/header/settingsMenuItems.js +9 -1
  42. package/dist/components/header/settingsMenuItems.js.map +1 -1
  43. package/dist/components/{header/HeaderMenu.d.ts → msa/ColumnStats.d.ts} +3 -3
  44. package/dist/components/msa/ColumnStats.js +42 -0
  45. package/dist/components/msa/ColumnStats.js.map +1 -0
  46. package/dist/components/msa/DomainLegend.d.ts +6 -0
  47. package/dist/components/msa/DomainLegend.js +41 -0
  48. package/dist/components/msa/DomainLegend.js.map +1 -0
  49. package/dist/components/msa/MSACanvasBlock.js +41 -29
  50. package/dist/components/msa/MSACanvasBlock.js.map +1 -1
  51. package/dist/components/msa/MSAMouseoverCanvas.js +6 -12
  52. package/dist/components/msa/MSAMouseoverCanvas.js.map +1 -1
  53. package/dist/components/msa/MSAPanel.js +3 -1
  54. package/dist/components/msa/MSAPanel.js.map +1 -1
  55. package/dist/components/msa/renderBoxFeatureCanvasBlock.js +60 -14
  56. package/dist/components/msa/renderBoxFeatureCanvasBlock.js.map +1 -1
  57. package/dist/components/msa/renderMSABlock.js +3 -3
  58. package/dist/components/msa/renderMSABlock.js.map +1 -1
  59. package/dist/components/msa/renderMSAMouseover.js +33 -6
  60. package/dist/components/msa/renderMSAMouseover.js.map +1 -1
  61. package/dist/components/overlayColors.d.ts +3 -0
  62. package/dist/components/overlayColors.js +11 -0
  63. package/dist/components/overlayColors.js.map +1 -0
  64. package/dist/components/renderCtx.d.ts +1 -1
  65. package/dist/components/tracks/renderTracksSvg.d.ts +6 -3
  66. package/dist/components/tracks/renderTracksSvg.js +20 -11
  67. package/dist/components/tracks/renderTracksSvg.js.map +1 -1
  68. package/dist/components/tree/TreeCanvas.js +31 -38
  69. package/dist/components/tree/TreeCanvas.js.map +1 -1
  70. package/dist/components/tree/TreeCanvasBlock.js +12 -47
  71. package/dist/components/tree/TreeCanvasBlock.js.map +1 -1
  72. package/dist/components/tree/clickMap.d.ts +24 -0
  73. package/dist/components/tree/clickMap.js +33 -0
  74. package/dist/components/tree/clickMap.js.map +1 -0
  75. package/dist/components/tree/renderTreeCanvas.d.ts +1 -21
  76. package/dist/components/tree/renderTreeCanvas.js +8 -7
  77. package/dist/components/tree/renderTreeCanvas.js.map +1 -1
  78. package/dist/constants.d.ts +4 -0
  79. package/dist/constants.js +17 -0
  80. package/dist/constants.js.map +1 -1
  81. package/dist/createPaletteMap.d.ts +5 -2
  82. package/dist/createPaletteMap.js +15 -5
  83. package/dist/createPaletteMap.js.map +1 -1
  84. package/dist/fetchUtils.d.ts +2 -1
  85. package/dist/fetchUtils.js +3 -2
  86. package/dist/fetchUtils.js.map +1 -1
  87. package/dist/index.d.ts +1 -1
  88. package/dist/launchInterProScan.d.ts +2 -0
  89. package/dist/launchInterProScan.js.map +1 -1
  90. package/dist/model/msaModel.d.ts +25 -2
  91. package/dist/model/msaModel.js +15 -3
  92. package/dist/model/msaModel.js.map +1 -1
  93. package/dist/model/treeModel.d.ts +46 -5
  94. package/dist/model/treeModel.js +20 -7
  95. package/dist/model/treeModel.js.map +1 -1
  96. package/dist/model.d.ts +641 -133
  97. package/dist/model.js +191 -77
  98. package/dist/model.js.map +1 -1
  99. package/dist/propertyConservation.d.ts +7 -0
  100. package/dist/propertyConservation.js +66 -0
  101. package/dist/propertyConservation.js.map +1 -0
  102. package/dist/renderToSvg.js +104 -74
  103. package/dist/renderToSvg.js.map +1 -1
  104. package/dist/types.d.ts +4 -1
  105. package/dist/{webpack.js → umd.js} +1 -1
  106. package/dist/umd.js.map +1 -0
  107. package/dist/useCanvasAutorun.d.ts +2 -0
  108. package/dist/useCanvasAutorun.js +23 -0
  109. package/dist/useCanvasAutorun.js.map +1 -0
  110. package/dist/version.d.ts +1 -1
  111. package/dist/version.js +1 -1
  112. package/package.json +7 -8
  113. package/src/colorSchemes.docs.test.ts +55 -0
  114. package/src/colorSchemes.ts +12 -7
  115. package/src/columnStats.test.ts +94 -0
  116. package/src/components/ConservationTrack.tsx +17 -15
  117. package/src/components/MSAViewer.tsx +42 -18
  118. package/src/components/SequenceTextArea.tsx +1 -1
  119. package/src/components/TextTrack.tsx +7 -11
  120. package/src/components/Track.tsx +9 -6
  121. package/src/components/dialogs/FeatureDialog.tsx +62 -48
  122. package/src/components/dialogs/TrackInfoDialog.tsx +1 -1
  123. package/src/components/header/ColorSchemeMenu.tsx +1 -0
  124. package/src/components/header/FileMenu.tsx +1 -15
  125. package/src/components/header/Header.tsx +11 -8
  126. package/src/components/header/HeaderInfoArea.tsx +1 -1
  127. package/src/components/header/HeaderStatusArea.tsx +1 -1
  128. package/src/components/header/MSASettingsMenu.tsx +1 -0
  129. package/src/components/header/MultiAlignmentSelector.tsx +0 -0
  130. package/src/components/header/TreeSettingsMenu.tsx +1 -0
  131. package/src/components/header/ZoomControls.tsx +24 -17
  132. package/src/components/header/ZoomMenu.tsx +1 -10
  133. package/src/components/header/settingsMenuItems.ts +9 -1
  134. package/src/components/msa/ColumnStats.tsx +54 -0
  135. package/src/components/msa/DomainLegend.tsx +78 -0
  136. package/src/components/msa/MSACanvasBlock.tsx +46 -25
  137. package/src/components/msa/MSAMouseoverCanvas.tsx +10 -13
  138. package/src/components/msa/MSAPanel.tsx +2 -0
  139. package/src/components/msa/renderBoxFeatureCanvasBlock.ts +75 -17
  140. package/src/components/msa/renderMSABlock.ts +3 -3
  141. package/src/components/msa/renderMSAMouseover.ts +34 -6
  142. package/src/components/overlayColors.ts +12 -0
  143. package/src/components/renderCtx.ts +1 -0
  144. package/src/components/tracks/renderTracksSvg.ts +25 -11
  145. package/src/components/tree/TreeCanvas.tsx +47 -54
  146. package/src/components/tree/TreeCanvasBlock.tsx +10 -68
  147. package/src/components/tree/clickMap.ts +55 -0
  148. package/src/components/tree/renderTreeCanvas.ts +10 -34
  149. package/src/constants.ts +19 -0
  150. package/src/createPaletteMap.ts +18 -5
  151. package/src/fetchUtils.ts +6 -3
  152. package/src/index.ts +1 -1
  153. package/src/launchInterProScan.ts +3 -1
  154. package/src/model/msaModel.ts +21 -3
  155. package/src/model/treeModel.ts +21 -7
  156. package/src/model.ts +221 -119
  157. package/src/propertyConservation.test.ts +47 -0
  158. package/src/propertyConservation.ts +73 -0
  159. package/src/renderDomainLegendSvg.test.tsx +134 -0
  160. package/src/renderToSvg.tsx +210 -147
  161. package/src/segmentOverlay.test.ts +70 -0
  162. package/src/stripDefaultSnapshot.test.ts +54 -0
  163. package/src/types.ts +7 -1
  164. package/src/useCanvasAutorun.ts +28 -0
  165. package/src/version.ts +1 -1
  166. package/bundle/index.js.LICENSE.txt +0 -100
  167. package/dist/components/dialogs/SettingsDialog.d.ts +0 -7
  168. package/dist/components/dialogs/SettingsDialog.js +0 -100
  169. package/dist/components/dialogs/SettingsDialog.js.map +0 -1
  170. package/dist/components/header/HeaderMenu.js +0 -27
  171. package/dist/components/header/HeaderMenu.js.map +0 -1
  172. package/dist/components/util.d.ts +0 -1
  173. package/dist/components/util.js +0 -7
  174. package/dist/components/util.js.map +0 -1
  175. package/dist/webpack.js.map +0 -1
  176. package/src/components/dialogs/SettingsDialog.tsx +0 -234
  177. package/src/components/header/HeaderMenu.tsx +0 -38
  178. package/src/components/util.ts +0 -9
  179. /package/dist/{webpack.d.ts → umd.d.ts} +0 -0
  180. /package/src/{webpack.ts → umd.ts} +0 -0
@@ -0,0 +1,94 @@
1
+ // @vitest-environment jsdom
2
+ import { beforeAll, describe, expect, test } from 'vitest'
3
+
4
+ import MSAModelF from './model.ts'
5
+
6
+ beforeAll(() => {
7
+ HTMLCanvasElement.prototype.getContext = function () {
8
+ let font = '10px sans-serif'
9
+ return {
10
+ get font() {
11
+ return font
12
+ },
13
+ set font(v: string) {
14
+ font = v
15
+ },
16
+ measureText(t: string) {
17
+ const size = Number.parseFloat(font) || 10
18
+ return { width: t.length * size * 0.6 } as TextMetrics
19
+ },
20
+ } as unknown as CanvasRenderingContext2D
21
+ } as unknown as typeof HTMLCanvasElement.prototype.getContext
22
+ })
23
+
24
+ // col0 is invariant M; col3 varies V/V/A (all hydrophobic) so property
25
+ // conservation stays high while identity conservation drops
26
+ const protein = `>a
27
+ MKLVIL
28
+ >b
29
+ MRLVIL
30
+ >c
31
+ MKLAIL`
32
+
33
+ const dna = `>a
34
+ ACGTAC
35
+ >b
36
+ ACGTAG
37
+ >c
38
+ ACGAAC`
39
+
40
+ function make(msa: string, id: string) {
41
+ return MSAModelF().create({
42
+ id,
43
+ type: 'MsaView',
44
+ msaFormat: 'fasta',
45
+ data: { msa },
46
+ })
47
+ }
48
+
49
+ describe('property conservation track', () => {
50
+ test('present for protein alignments', () => {
51
+ const ids = make(protein, 'p').tracks.map(t => t.model.id)
52
+ expect(ids).toContain('conservation')
53
+ expect(ids).toContain('property-conservation')
54
+ })
55
+
56
+ test('omitted for nucleotide alignments', () => {
57
+ const ids = make(dna, 'd').tracks.map(t => t.model.id)
58
+ expect(ids).toContain('conservation')
59
+ expect(ids).not.toContain('property-conservation')
60
+ })
61
+
62
+ test('conservative-substitution column: property beats identity', () => {
63
+ const model = make(protein, 'pc')
64
+ // col3 = V/V/A: same property class, different identity
65
+ expect(model.propertyConservation[3]!).toBeGreaterThan(
66
+ model.conservation[3]!,
67
+ )
68
+ })
69
+ })
70
+
71
+ describe('mouseOverColumnStats', () => {
72
+ test('undefined when nothing hovered', () => {
73
+ expect(make(protein, 'm0').mouseOverColumnStats).toBeUndefined()
74
+ })
75
+
76
+ test('reports consensus, gaps and distribution for hovered column', () => {
77
+ const model = make(protein, 'm1')
78
+ model.setMousePos(0, 0)
79
+ const s = model.mouseOverColumnStats
80
+ expect(s?.consensusLetter).toBe('M')
81
+ expect(s?.consensusFraction).toBe(1)
82
+ expect(s?.gapFraction).toBe(0)
83
+ expect(s?.total).toBe(3)
84
+ })
85
+
86
+ test('mixed column reports the majority residue as consensus', () => {
87
+ const model = make(protein, 'm2')
88
+ // col1 = K/R/K -> consensus K at 2/3
89
+ model.setMousePos(1, 0)
90
+ const s = model.mouseOverColumnStats
91
+ expect(s?.consensusLetter).toBe('K')
92
+ expect(s?.consensusCount).toBe(2)
93
+ })
94
+ })
@@ -1,34 +1,33 @@
1
- import React, { useEffect, useRef } from 'react'
1
+ import React from 'react'
2
2
 
3
- import { autorun } from 'mobx'
4
3
  import { observer } from 'mobx-react'
5
4
 
6
5
  import { ConservationTrackResizeHandle } from './ResizeHandles.tsx'
7
- import { drawConservationBars } from './tracks/renderTracksSvg.ts'
6
+ import { useCanvasAutorun } from '../useCanvasAutorun.ts'
7
+ import {
8
+ barTrackValues,
9
+ drawConservationBars,
10
+ } from './tracks/renderTracksSvg.ts'
8
11
 
9
12
  import type { MsaViewModel } from '../model.ts'
10
13
  import type { BasicTrack } from '../types.ts'
11
14
 
12
15
  const ConservationBlock = observer(function ({
13
16
  model,
17
+ track,
14
18
  offsetX,
15
19
  trackHeight,
16
20
  }: {
17
21
  model: MsaViewModel
22
+ track: BasicTrack
18
23
  offsetX: number
19
24
  trackHeight: number
20
25
  }) {
21
26
  const { blockSize, scrollX, highResScaleFactor } = model
22
27
 
23
- const ref = useRef<HTMLCanvasElement>(null)
24
-
25
- useEffect(() => {
26
- const ctx = ref.current?.getContext('2d')
27
- if (!ctx) {
28
- return
29
- }
30
- return autorun(() => {
31
- const { blockSize, colWidth, highResScaleFactor, conservation } = model
28
+ const ref = useCanvasAutorun(
29
+ ctx => {
30
+ const { blockSize, colWidth, highResScaleFactor } = model
32
31
  ctx.resetTransform()
33
32
  ctx.scale(highResScaleFactor, highResScaleFactor)
34
33
  ctx.clearRect(0, 0, blockSize, trackHeight)
@@ -36,14 +35,16 @@ const ConservationBlock = observer(function ({
36
35
 
37
36
  drawConservationBars({
38
37
  ctx,
39
- conservation,
38
+ values: barTrackValues(model, track.model.id),
39
+ color: track.model.barColor ?? 'gray',
40
40
  colWidth,
41
41
  trackHeight,
42
42
  offsetX,
43
43
  blockSize,
44
44
  })
45
- })
46
- }, [model, offsetX, trackHeight])
45
+ },
46
+ [model, track, offsetX, trackHeight],
47
+ )
47
48
 
48
49
  return (
49
50
  <canvas
@@ -83,6 +84,7 @@ const ConservationTrack = observer(function ({
83
84
  <ConservationBlock
84
85
  key={bx}
85
86
  model={model}
87
+ track={track}
86
88
  offsetX={bx}
87
89
  trackHeight={trackHeight}
88
90
  />
@@ -1,4 +1,4 @@
1
- import React, { useEffect, useMemo } from 'react'
1
+ import React, { useEffect, useState } from 'react'
2
2
 
3
3
  import { createJBrowseTheme } from '@jbrowse/core/ui/theme'
4
4
  import useMeasure from '@jbrowse/core/util/useMeasure'
@@ -9,6 +9,8 @@ import Loading from './Loading.tsx'
9
9
 
10
10
  import type { FileLocation as FileLocationType } from '@jbrowse/core/util/types'
11
11
 
12
+ const theme = createJBrowseTheme()
13
+
12
14
  interface MSAViewerProps {
13
15
  msa?: string
14
16
  tree?: string
@@ -18,8 +20,20 @@ interface MSAViewerProps {
18
20
  gffFilehandle?: FileLocationType
19
21
  colorScheme?: string
20
22
  height?: number
23
+ /** initial per-column pixel width (zoom level) */
24
+ colWidth?: number
25
+ /** initial per-row pixel height */
26
+ rowHeight?: number
27
+ /** alignment columns (0-based) to highlight with a persistent overlay */
28
+ highlightColumns?: number[]
21
29
  /** row name to diff every other row against (matches render as ".") */
22
30
  relativeTo?: string
31
+ /** draw the phylogenetic tree (default true); false leaves a labels-only gutter */
32
+ drawTree?: boolean
33
+ /** fixed width (px) of the tree/label area */
34
+ treeAreaWidth?: number
35
+ /** auto-size the tree/label area to the labels (used when drawTree is false) */
36
+ autoTreeAreaWidth?: boolean
23
37
  }
24
38
 
25
39
  export default function MSAViewer({
@@ -31,25 +45,35 @@ export default function MSAViewer({
31
45
  gffFilehandle,
32
46
  colorScheme,
33
47
  height,
48
+ colWidth,
49
+ rowHeight,
50
+ highlightColumns,
34
51
  relativeTo,
52
+ drawTree,
53
+ treeAreaWidth,
54
+ autoTreeAreaWidth,
35
55
  }: MSAViewerProps) {
36
- const theme = useMemo(() => createJBrowseTheme(), [])
37
- const model = useMemo(
38
- () =>
39
- MSAModelF().create({
40
- type: 'MsaView',
41
- ...(msa || tree || gff
42
- ? { data: { msa: msa ?? '', tree: tree ?? '', gff } }
43
- : {}),
44
- ...(msaFilehandle ? { msaFilehandle } : {}),
45
- ...(treeFilehandle ? { treeFilehandle } : {}),
46
- ...(gffFilehandle ? { gffFilehandle } : {}),
47
- ...(colorScheme ? { colorSchemeName: colorScheme } : {}),
48
- ...(height ? { height } : {}),
49
- ...(relativeTo ? { relativeTo } : {}),
50
- }),
51
- // eslint-disable-next-line react-hooks/exhaustive-deps
52
- [],
56
+ // lazy initializer: the model is created exactly once from the initial props
57
+ // (a stable MST instance — not a value safe to recompute, so not useMemo)
58
+ const [model] = useState(() =>
59
+ MSAModelF().create({
60
+ type: 'MsaView',
61
+ ...(msa || tree || gff
62
+ ? { data: { msa: msa ?? '', tree: tree ?? '', gff } }
63
+ : {}),
64
+ ...(msaFilehandle ? { msaFilehandle } : {}),
65
+ ...(treeFilehandle ? { treeFilehandle } : {}),
66
+ ...(gffFilehandle ? { gffFilehandle } : {}),
67
+ ...(colorScheme ? { colorSchemeName: colorScheme } : {}),
68
+ ...(height ? { height } : {}),
69
+ ...(colWidth ? { colWidth } : {}),
70
+ ...(rowHeight ? { rowHeight } : {}),
71
+ ...(highlightColumns ? { highlightColumns } : {}),
72
+ ...(relativeTo ? { relativeTo } : {}),
73
+ ...(drawTree !== undefined ? { drawTree } : {}),
74
+ ...(treeAreaWidth ? { treeAreaWidth } : {}),
75
+ ...(autoTreeAreaWidth ? { autoTreeAreaWidth } : {}),
76
+ }),
53
77
  )
54
78
 
55
79
  const [ref, { width }] = useMeasure()
@@ -1,7 +1,7 @@
1
1
  import React, { useState } from 'react'
2
2
 
3
+ import { makeStyles } from '@jbrowse/core/util/tss-react'
3
4
  import { Button, TextField } from '@mui/material'
4
- import { makeStyles } from 'tss-react/mui'
5
5
 
6
6
  import Checkbox2 from './Checkbox2.tsx'
7
7
  import copy from '../vendor/copyToClipboard.ts'
@@ -1,9 +1,9 @@
1
- import React, { useEffect, useRef } from 'react'
1
+ import React from 'react'
2
2
 
3
- import { autorun } from 'mobx'
4
3
  import { observer } from 'mobx-react'
5
4
 
6
5
  import { setFontSize } from '../setFontSize.ts'
6
+ import { useCanvasAutorun } from '../useCanvasAutorun.ts'
7
7
  import { useColorContrast } from '../useColorContrast.ts'
8
8
  import { drawTextTrackContent } from './tracks/renderTracksSvg.ts'
9
9
 
@@ -31,14 +31,9 @@ const AnnotationBlock = observer(function ({
31
31
  } = track
32
32
 
33
33
  const colorScheme = customColorScheme ?? modelColorScheme
34
- const ref = useRef<HTMLCanvasElement>(null)
35
34
  const { contrastScheme } = useColorContrast(colorScheme)
36
- useEffect(() => {
37
- const ctx = ref.current?.getContext('2d')
38
- if (!ctx) {
39
- return
40
- }
41
- return autorun(() => {
35
+ const ref = useCanvasAutorun(
36
+ ctx => {
42
37
  const {
43
38
  blockSize,
44
39
  bgColor,
@@ -65,8 +60,9 @@ const AnnotationBlock = observer(function ({
65
60
  offsetX,
66
61
  blockSize,
67
62
  })
68
- })
69
- }, [model, offsetX, contrastScheme, colorScheme, data])
63
+ },
64
+ [model, offsetX, contrastScheme, colorScheme, data],
65
+ )
70
66
  return (
71
67
  <canvas
72
68
  ref={ref}
@@ -1,9 +1,9 @@
1
1
  import React, { lazy, useCallback, useRef, useState } from 'react'
2
2
 
3
+ import { makeStyles } from '@jbrowse/core/util/tss-react'
3
4
  import ArrowDropDownIcon from '@mui/icons-material/ArrowDropDown'
4
5
  import { IconButton, Menu, MenuItem } from '@mui/material'
5
6
  import { observer } from 'mobx-react'
6
- import { makeStyles } from 'tss-react/mui'
7
7
 
8
8
  import { useWheelScroll } from '../useWheelScroll.ts'
9
9
 
@@ -26,12 +26,15 @@ const TrackLabel = observer(function TrackLabel({
26
26
  track: BasicTrack
27
27
  }) {
28
28
  const [anchorEl, setAnchorEl] = useState<HTMLButtonElement>()
29
- const { drawLabels, rowHeight, treeAreaWidth: width } = model
29
+ const closeMenu = () => {
30
+ setAnchorEl(undefined)
31
+ }
32
+ const { drawLabels, fontSize, treeAreaWidth: width } = model
30
33
  const {
31
34
  model: { name, height },
32
35
  } = track
33
36
  const { classes } = useStyles()
34
- const trackLabelHeight = Math.min(height, Math.max(8, rowHeight - 8))
37
+ const trackLabelHeight = Math.min(height, fontSize)
35
38
 
36
39
  return (
37
40
  <div
@@ -62,14 +65,14 @@ const TrackLabel = observer(function TrackLabel({
62
65
  transitionDuration={0}
63
66
  open
64
67
  onClose={() => {
65
- setAnchorEl(undefined)
68
+ closeMenu()
66
69
  }}
67
70
  >
68
71
  <MenuItem
69
72
  dense
70
73
  onClick={() => {
71
74
  model.toggleTrack(track.model.id)
72
- setAnchorEl(undefined)
75
+ closeMenu()
73
76
  }}
74
77
  >
75
78
  Close
@@ -81,7 +84,7 @@ const TrackLabel = observer(function TrackLabel({
81
84
  TrackInfoDialog,
82
85
  { onClose, model: track.model },
83
86
  ])
84
- setAnchorEl(undefined)
87
+ closeMenu()
85
88
  }}
86
89
  >
87
90
  Get info
@@ -1,11 +1,18 @@
1
1
  import React from 'react'
2
2
 
3
3
  import { Dialog } from '@jbrowse/core/ui'
4
- import { Button, DialogContent } from '@mui/material'
4
+ import {
5
+ Button,
6
+ Checkbox,
7
+ DialogContent,
8
+ Table,
9
+ TableBody,
10
+ TableCell,
11
+ TableHead,
12
+ TableRow,
13
+ } from '@mui/material'
5
14
  import { observer } from 'mobx-react'
6
15
 
7
- import { getPalette } from '../../ggplotPalettes.ts'
8
-
9
16
  import type { MsaViewModel } from '../../model.ts'
10
17
 
11
18
  const Toggles = observer(function ({ model }: { model: MsaViewModel }) {
@@ -35,53 +42,60 @@ const Toggles = observer(function ({ model }: { model: MsaViewModel }) {
35
42
  )
36
43
  })
37
44
 
38
- const Table = observer(function ({ model }: { model: MsaViewModel }) {
39
- const { tidyInterProAnnotationTypes, featureFilters } = model
40
- const values = [...tidyInterProAnnotationTypes.values()]
41
- const palette = getPalette(values.length - 1)
45
+ const FeatureTable = observer(function ({ model }: { model: MsaViewModel }) {
46
+ const { tidyInterProAnnotationTypes, tidyInterProAnnotations, fillPalette } =
47
+ model
48
+ const counts = new Map<string, number>()
49
+ for (const annot of tidyInterProAnnotations) {
50
+ counts.set(annot.accession, (counts.get(annot.accession) ?? 0) + 1)
51
+ }
42
52
  return (
43
53
  <>
44
54
  <Toggles model={model} />
45
- <table>
46
- <thead>
47
- <tr>
48
- <td />
49
- <td>accession</td>
50
- <td>name</td>
51
- <td>description</td>
52
- </tr>
53
- </thead>
54
- <tbody>
55
- {values.map(({ accession, name, description }, idx) => (
56
- <tr key={accession}>
57
- <td>
58
- <input
59
- type="checkbox"
60
- checked={featureFilters.get(accession) ?? false}
61
- onChange={() => {
62
- model.setFilter(
63
- accession,
64
- !model.featureFilters.get(accession),
65
- )
66
- }}
67
- />
68
- </td>
69
- <td>{accession}</td>
70
- <td>{name}</td>
71
- <td>{description}</td>
72
- <td>
73
- <div
74
- style={{
75
- width: 20,
76
- height: 20,
77
- background: palette[idx] || 'black',
78
- }}
79
- />
80
- </td>
81
- </tr>
82
- ))}
83
- </tbody>
84
- </table>
55
+ <Table size="small">
56
+ <TableHead>
57
+ <TableRow>
58
+ <TableCell padding="checkbox" />
59
+ <TableCell>color</TableCell>
60
+ <TableCell>accession</TableCell>
61
+ <TableCell>name</TableCell>
62
+ <TableCell>count</TableCell>
63
+ <TableCell>description</TableCell>
64
+ </TableRow>
65
+ </TableHead>
66
+ <TableBody>
67
+ {[...tidyInterProAnnotationTypes.values()].map(
68
+ ({ accession, name, description }) => (
69
+ <TableRow key={accession}>
70
+ <TableCell padding="checkbox">
71
+ <Checkbox
72
+ checked={model.featureFilters.get(accession) ?? false}
73
+ onChange={() => {
74
+ model.setFilter(
75
+ accession,
76
+ !model.featureFilters.get(accession),
77
+ )
78
+ }}
79
+ />
80
+ </TableCell>
81
+ <TableCell>
82
+ <div
83
+ style={{
84
+ width: 20,
85
+ height: 20,
86
+ background: fillPalette[accession],
87
+ }}
88
+ />
89
+ </TableCell>
90
+ <TableCell>{accession}</TableCell>
91
+ <TableCell>{name}</TableCell>
92
+ <TableCell>{counts.get(accession)}</TableCell>
93
+ <TableCell>{description}</TableCell>
94
+ </TableRow>
95
+ ),
96
+ )}
97
+ </TableBody>
98
+ </Table>
85
99
  </>
86
100
  )
87
101
  })
@@ -103,7 +117,7 @@ const FeatureTypeDialog = observer(function ({
103
117
  maxWidth="xl"
104
118
  >
105
119
  <DialogContent>
106
- <Table model={model} />
120
+ <FeatureTable model={model} />
107
121
  </DialogContent>
108
122
  </Dialog>
109
123
  )
@@ -1,9 +1,9 @@
1
1
  import React, { useState } from 'react'
2
2
 
3
3
  import { Dialog } from '@jbrowse/core/ui'
4
+ import { makeStyles } from '@jbrowse/core/util/tss-react'
4
5
  import { Button, DialogActions, DialogContent } from '@mui/material'
5
6
  import { observer } from 'mobx-react'
6
- import { makeStyles } from 'tss-react/mui'
7
7
 
8
8
  import copy from '../../vendor/copyToClipboard.ts'
9
9
 
@@ -13,6 +13,7 @@ const ColorSchemeMenu = observer(function ({ model }: { model: MsaViewModel }) {
13
13
  return (
14
14
  <CascadingMenuButton
15
15
  data-testid="color_scheme_menu"
16
+ tooltip="Color scheme"
16
17
  closeAfterItemClick
17
18
  anchorOrigin={{ vertical: 'bottom', horizontal: 'left' }}
18
19
  transformOrigin={{ vertical: 'top', horizontal: 'left' }}
@@ -5,7 +5,6 @@ import Assignment from '@mui/icons-material/Assignment'
5
5
  import FolderOpen from '@mui/icons-material/FolderOpen'
6
6
  import Menu from '@mui/icons-material/Menu'
7
7
  import PhotoCamera from '@mui/icons-material/PhotoCamera'
8
- import Settings from '@mui/icons-material/Settings'
9
8
  import { observer } from 'mobx-react'
10
9
 
11
10
  import { getDomainMenu } from './getDomainsMenu.ts'
@@ -14,12 +13,12 @@ import type { MsaViewModel } from '../../model.ts'
14
13
 
15
14
  const MetadataDialog = lazy(() => import('../dialogs/MetadataDialog.tsx'))
16
15
  const ExportSVGDialog = lazy(() => import('../dialogs/ExportSVGDialog.tsx'))
17
- const SettingsDialog = lazy(() => import('../dialogs/SettingsDialog.tsx'))
18
16
 
19
17
  const FileMenu = observer(({ model }: { model: MsaViewModel }) => {
20
18
  return (
21
19
  <CascadingMenuButton
22
20
  data-testid="file_menu"
21
+ tooltip="File"
23
22
  menuItems={[
24
23
  {
25
24
  label: 'Return to import form',
@@ -41,19 +40,6 @@ const FileMenu = observer(({ model }: { model: MsaViewModel }) => {
41
40
  ])
42
41
  },
43
42
  },
44
- {
45
- label: 'More settings',
46
- icon: Settings,
47
- onClick: () => {
48
- model.queueDialog(onClose => [
49
- SettingsDialog,
50
- {
51
- model,
52
- onClose,
53
- },
54
- ])
55
- },
56
- },
57
43
  {
58
44
  label: 'Domain settings',
59
45
  type: 'subMenu',
@@ -2,7 +2,7 @@ import React, { lazy, useEffect } from 'react'
2
2
 
3
3
  import useMeasure from '@jbrowse/core/util/useMeasure'
4
4
  import Help from '@mui/icons-material/Help'
5
- import { IconButton } from '@mui/material'
5
+ import { IconButton, Tooltip } from '@mui/material'
6
6
  import { observer } from 'mobx-react'
7
7
 
8
8
  import ColorSchemeMenu from './ColorSchemeMenu.tsx'
@@ -40,13 +40,16 @@ const Header = observer(function ({ model }: { model: MsaViewModel }) {
40
40
  <HeaderInfoArea model={model} />
41
41
  <Spacer />
42
42
  <HeaderStatusArea model={model} />
43
- <IconButton
44
- onClick={() => {
45
- model.queueDialog(onClose => [AboutDialog, { onClose }])
46
- }}
47
- >
48
- <Help />
49
- </IconButton>
43
+ <Tooltip title="About">
44
+ <IconButton
45
+ aria-label="About"
46
+ onClick={() => {
47
+ model.queueDialog(onClose => [AboutDialog, { onClose }])
48
+ }}
49
+ >
50
+ <Help />
51
+ </IconButton>
52
+ </Tooltip>
50
53
  </div>
51
54
  )
52
55
  })
@@ -1,8 +1,8 @@
1
1
  import React from 'react'
2
2
 
3
+ import { makeStyles } from '@jbrowse/core/util/tss-react'
3
4
  import { Typography } from '@mui/material'
4
5
  import { observer } from 'mobx-react'
5
- import { makeStyles } from 'tss-react/mui'
6
6
 
7
7
  import type { MsaViewModel } from '../../model.ts'
8
8
 
@@ -1,9 +1,9 @@
1
1
  import React from 'react'
2
2
 
3
3
  import { LoadingEllipses } from '@jbrowse/core/ui'
4
+ import { makeStyles } from '@jbrowse/core/util/tss-react'
4
5
  import { Button, Typography } from '@mui/material'
5
6
  import { observer } from 'mobx-react'
6
- import { makeStyles } from 'tss-react/mui'
7
7
 
8
8
  import type { MsaViewModel } from '../../model.ts'
9
9
 
@@ -12,6 +12,7 @@ const MSASettingsMenu = observer(function ({ model }: { model: MsaViewModel }) {
12
12
  return (
13
13
  <CascadingMenuButton
14
14
  data-testid="msa_settings_menu"
15
+ tooltip="Alignment settings"
15
16
  closeAfterItemClick={false}
16
17
  anchorOrigin={{ vertical: 'bottom', horizontal: 'left' }}
17
18
  transformOrigin={{ vertical: 'top', horizontal: 'left' }}
@@ -17,6 +17,7 @@ const TreeSettingsMenu = observer(function ({
17
17
  return (
18
18
  <CascadingMenuButton
19
19
  data-testid="tree_settings_menu"
20
+ tooltip="Tree settings"
20
21
  closeAfterItemClick={false}
21
22
  anchorOrigin={{ vertical: 'bottom', horizontal: 'left' }}
22
23
  transformOrigin={{ vertical: 'top', horizontal: 'left' }}