react-msaview 5.9.0 → 6.0.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 (196) hide show
  1. package/bundle/index.js +99 -99
  2. package/bundle/index.js.map +4 -4
  3. package/dist/clustalX.js +22 -22
  4. package/dist/clustalX.js.map +1 -1
  5. package/dist/components/ConservationTrack.js +21 -16
  6. package/dist/components/ConservationTrack.js.map +1 -1
  7. package/dist/components/CopyButton.d.ts +9 -0
  8. package/dist/components/CopyButton.js +23 -0
  9. package/dist/components/CopyButton.js.map +1 -0
  10. package/dist/components/Loading.js +2 -2
  11. package/dist/components/Loading.js.map +1 -1
  12. package/dist/components/MSAView.js +18 -24
  13. package/dist/components/MSAView.js.map +1 -1
  14. package/dist/components/ResizeHandles.js +24 -40
  15. package/dist/components/ResizeHandles.js.map +1 -1
  16. package/dist/components/ScrollbarThumb.d.ts +14 -0
  17. package/dist/components/ScrollbarThumb.js +28 -0
  18. package/dist/components/ScrollbarThumb.js.map +1 -0
  19. package/dist/components/SequenceTextArea.js +3 -10
  20. package/dist/components/SequenceTextArea.js.map +1 -1
  21. package/dist/components/TextTrack.js +26 -21
  22. package/dist/components/TextTrack.js.map +1 -1
  23. package/dist/components/VerticalScrollbar.js +11 -25
  24. package/dist/components/VerticalScrollbar.js.map +1 -1
  25. package/dist/components/dialogs/FeatureDialog.js +3 -3
  26. package/dist/components/dialogs/FeatureDialog.js.map +1 -1
  27. package/dist/components/dialogs/{UserProvidedDomainsDialog.d.ts → InterProScanFileDialog.d.ts} +2 -2
  28. package/dist/components/dialogs/{UserProvidedDomainsDialog.js → InterProScanFileDialog.js} +6 -10
  29. package/dist/components/dialogs/InterProScanFileDialog.js.map +1 -0
  30. package/dist/components/dialogs/TrackInfoDialog.d.ts +2 -4
  31. package/dist/components/dialogs/TrackInfoDialog.js +8 -14
  32. package/dist/components/dialogs/TrackInfoDialog.js.map +1 -1
  33. package/dist/components/header/FileMenu.js +3 -3
  34. package/dist/components/header/FileMenu.js.map +1 -1
  35. package/dist/components/header/GappynessSlider.js +1 -1
  36. package/dist/components/header/GappynessSlider.js.map +1 -1
  37. package/dist/components/header/{getDomainsMenu.d.ts → getAnnotationsMenu.d.ts} +1 -1
  38. package/dist/components/header/{getDomainsMenu.js → getAnnotationsMenu.js} +10 -9
  39. package/dist/components/header/getAnnotationsMenu.js.map +1 -0
  40. package/dist/components/header/settingsMenuItems.js +39 -72
  41. package/dist/components/header/settingsMenuItems.js.map +1 -1
  42. package/dist/components/import/ImportForm.js +1 -1
  43. package/dist/components/import/ImportForm.js.map +1 -1
  44. package/dist/components/import/ImportFormExamples.js +66 -57
  45. package/dist/components/import/ImportFormExamples.js.map +1 -1
  46. package/dist/components/minimap/Minimap.js +11 -22
  47. package/dist/components/minimap/Minimap.js.map +1 -1
  48. package/dist/components/minimap/MinimapSVG.d.ts +2 -1
  49. package/dist/components/minimap/MinimapSVG.js +3 -4
  50. package/dist/components/minimap/MinimapSVG.js.map +1 -1
  51. package/dist/components/minimap/minimapLayout.d.ts +10 -1
  52. package/dist/components/minimap/minimapLayout.js +15 -10
  53. package/dist/components/minimap/minimapLayout.js.map +1 -1
  54. package/dist/components/msa/{DomainLegend.d.ts → AnnotationLegend.d.ts} +2 -2
  55. package/dist/components/msa/{DomainLegend.js → AnnotationLegend.js} +4 -4
  56. package/dist/components/msa/AnnotationLegend.js.map +1 -0
  57. package/dist/components/msa/MSACanvas.js +2 -2
  58. package/dist/components/msa/MSACanvas.js.map +1 -1
  59. package/dist/components/msa/MSACanvasBlock.js +19 -16
  60. package/dist/components/msa/MSACanvasBlock.js.map +1 -1
  61. package/dist/components/msa/MSAMouseoverCanvas.js +14 -8
  62. package/dist/components/msa/MSAMouseoverCanvas.js.map +1 -1
  63. package/dist/components/msa/MSAPanel.js +2 -2
  64. package/dist/components/msa/MSAPanel.js.map +1 -1
  65. package/dist/components/msa/domainBandCursor.d.ts +17 -0
  66. package/dist/components/msa/domainBandCursor.js +39 -0
  67. package/dist/components/msa/domainBandCursor.js.map +1 -0
  68. package/dist/components/msa/renderBoxFeatureCanvasBlock.js +9 -6
  69. package/dist/components/msa/renderBoxFeatureCanvasBlock.js.map +1 -1
  70. package/dist/components/msa/renderMSABlock.js +14 -20
  71. package/dist/components/msa/renderMSABlock.js.map +1 -1
  72. package/dist/components/msa/renderMSAMouseover.js +32 -33
  73. package/dist/components/msa/renderMSAMouseover.js.map +1 -1
  74. package/dist/components/overlayColors.d.ts +2 -0
  75. package/dist/components/overlayColors.js +5 -0
  76. package/dist/components/overlayColors.js.map +1 -1
  77. package/dist/components/tree/TreeCanvas.js +29 -24
  78. package/dist/components/tree/TreeCanvas.js.map +1 -1
  79. package/dist/components/tree/TreeCanvasBlock.js +19 -12
  80. package/dist/components/tree/TreeCanvasBlock.js.map +1 -1
  81. package/dist/components/tree/TreeRuler.js +5 -2
  82. package/dist/components/tree/TreeRuler.js.map +1 -1
  83. package/dist/components/tree/renderTreeCanvas.d.ts +0 -38
  84. package/dist/components/tree/renderTreeCanvas.js +17 -17
  85. package/dist/components/tree/renderTreeCanvas.js.map +1 -1
  86. package/dist/ggplotPalettes.d.ts +0 -1
  87. package/dist/ggplotPalettes.js +0 -3
  88. package/dist/ggplotPalettes.js.map +1 -1
  89. package/dist/hierarchy.d.ts +2 -6
  90. package/dist/hierarchy.js +4 -6
  91. package/dist/hierarchy.js.map +1 -1
  92. package/dist/index.d.ts +2 -2
  93. package/dist/launchInterProScan.d.ts +0 -23
  94. package/dist/launchInterProScan.js +2 -5
  95. package/dist/launchInterProScan.js.map +1 -1
  96. package/dist/measureTextCanvas.js +21 -6
  97. package/dist/measureTextCanvas.js.map +1 -1
  98. package/dist/model.d.ts +50 -100
  99. package/dist/model.js +209 -219
  100. package/dist/model.js.map +1 -1
  101. package/dist/neighborJoining.js +12 -19
  102. package/dist/neighborJoining.js.map +1 -1
  103. package/dist/parseAsn1.js +32 -43
  104. package/dist/parseAsn1.js.map +1 -1
  105. package/dist/renderToSvg.js +5 -2
  106. package/dist/renderToSvg.js.map +1 -1
  107. package/dist/rowCoordinateCalculations.d.ts +13 -17
  108. package/dist/rowCoordinateCalculations.js +45 -39
  109. package/dist/rowCoordinateCalculations.js.map +1 -1
  110. package/dist/seqPosToGlobalCol.d.ts +6 -19
  111. package/dist/seqPosToGlobalCol.js +6 -33
  112. package/dist/seqPosToGlobalCol.js.map +1 -1
  113. package/dist/types.d.ts +4 -11
  114. package/dist/useCanvasAutorun.d.ts +23 -1
  115. package/dist/useCanvasAutorun.js +20 -8
  116. package/dist/useCanvasAutorun.js.map +1 -1
  117. package/dist/version.d.ts +1 -1
  118. package/dist/version.js +1 -1
  119. package/package.json +5 -5
  120. package/src/clustalX.ts +22 -22
  121. package/src/colSpace.test.ts +27 -0
  122. package/src/components/ConservationTrack.tsx +7 -5
  123. package/src/components/CopyButton.tsx +44 -0
  124. package/src/components/Loading.tsx +10 -1
  125. package/src/components/MSAView.tsx +25 -32
  126. package/src/components/ResizeHandles.tsx +60 -69
  127. package/src/components/ScrollbarThumb.tsx +48 -0
  128. package/src/components/SequenceTextArea.tsx +3 -16
  129. package/src/components/TextTrack.tsx +7 -12
  130. package/src/components/VerticalScrollbar.tsx +19 -33
  131. package/src/components/dialogs/FeatureDialog.tsx +3 -4
  132. package/src/components/dialogs/{UserProvidedDomainsDialog.tsx → InterProScanFileDialog.tsx} +6 -14
  133. package/src/components/dialogs/TrackInfoDialog.tsx +18 -20
  134. package/src/components/header/FileMenu.tsx +3 -3
  135. package/src/components/header/GappynessSlider.tsx +4 -1
  136. package/src/components/header/{getDomainsMenu.ts → getAnnotationsMenu.ts} +10 -9
  137. package/src/components/header/settingsMenuItems.ts +40 -72
  138. package/src/components/import/ImportForm.tsx +1 -1
  139. package/src/components/import/ImportFormExamples.tsx +82 -133
  140. package/src/components/minimap/Minimap.tsx +23 -31
  141. package/src/components/minimap/MinimapSVG.tsx +32 -31
  142. package/src/components/minimap/minimapLayout.test.ts +60 -0
  143. package/src/components/minimap/minimapLayout.ts +15 -10
  144. package/src/components/msa/{DomainLegend.tsx → AnnotationLegend.tsx} +7 -3
  145. package/src/components/msa/MSACanvas.tsx +3 -10
  146. package/src/components/msa/MSACanvasBlock.tsx +16 -21
  147. package/src/components/msa/MSAMouseoverCanvas.tsx +11 -9
  148. package/src/components/msa/MSAPanel.tsx +2 -2
  149. package/src/components/msa/domainBandCursor.ts +41 -0
  150. package/src/components/msa/renderBoxFeatureCanvasBlock.test.ts +51 -0
  151. package/src/components/msa/renderBoxFeatureCanvasBlock.ts +13 -8
  152. package/src/components/msa/renderMSABlock.ts +19 -23
  153. package/src/components/msa/renderMSAMouseover.ts +38 -35
  154. package/src/components/overlayColors.ts +6 -0
  155. package/src/components/tree/TreeCanvas.tsx +6 -4
  156. package/src/components/tree/TreeCanvasBlock.tsx +11 -12
  157. package/src/components/tree/TreeRuler.tsx +7 -2
  158. package/src/components/tree/renderTreeCanvas.test.ts +15 -7
  159. package/src/components/tree/renderTreeCanvas.ts +20 -17
  160. package/src/domainBands.test.ts +24 -0
  161. package/src/domainLetterColor.test.ts +80 -0
  162. package/src/ggplotPalettes.ts +0 -4
  163. package/src/hierarchy.test.ts +7 -7
  164. package/src/hierarchy.ts +6 -9
  165. package/src/index.ts +4 -1
  166. package/src/largeTree.test.ts +23 -2
  167. package/src/launchInterProScan.ts +3 -30
  168. package/src/loadErrorDisplay.test.tsx +102 -0
  169. package/src/measureTextCanvas.ts +22 -6
  170. package/src/model.ts +233 -244
  171. package/src/modelFilehandleLoaders.test.ts +137 -0
  172. package/src/neighborJoining.test.ts +12 -0
  173. package/src/neighborJoining.ts +12 -20
  174. package/src/parseAsn1.ts +32 -40
  175. package/src/{renderDomainLegendSvg.test.tsx → renderDomainsSvg.test.tsx} +44 -20
  176. package/src/renderMinimapSvg.test.tsx +109 -0
  177. package/src/renderToSvg.tsx +5 -2
  178. package/src/rowCoordinateCalculations.ts +48 -44
  179. package/src/seqPosToGlobalCol.test.ts +39 -51
  180. package/src/seqPosToGlobalCol.ts +6 -41
  181. package/src/types.ts +10 -15
  182. package/src/useCanvasAutorun.test.tsx +98 -0
  183. package/src/useCanvasAutorun.ts +30 -11
  184. package/src/version.ts +1 -1
  185. package/src/viewChrome.test.tsx +134 -0
  186. package/dist/components/dialogs/DomainDialog.d.ts +0 -6
  187. package/dist/components/dialogs/DomainDialog.js +0 -19
  188. package/dist/components/dialogs/DomainDialog.js.map +0 -1
  189. package/dist/components/dialogs/UserProvidedDomainsDialog.js.map +0 -1
  190. package/dist/components/header/SettingsMenu.d.ts +0 -6
  191. package/dist/components/header/SettingsMenu.js +0 -40
  192. package/dist/components/header/SettingsMenu.js.map +0 -1
  193. package/dist/components/header/getDomainsMenu.js.map +0 -1
  194. package/dist/components/msa/DomainLegend.js.map +0 -1
  195. package/src/components/dialogs/DomainDialog.tsx +0 -45
  196. package/src/components/header/SettingsMenu.tsx +0 -57
@@ -9,27 +9,29 @@ import type { MsaViewModel } from '../model.ts'
9
9
  const handleBg = 'rgba(200,200,200)'
10
10
  const handleBgHover = 'rgba(150,150,150)'
11
11
 
12
- export const VerticalResizeHandle = observer(function ({
13
- model,
12
+ /**
13
+ * A draggable divider. `getStart` is read at mousedown and handed back to
14
+ * `onDrag` alongside the pixel delta since, so each handle below only has to say
15
+ * which model dimension it resizes and where it sits.
16
+ */
17
+ function ResizeHandle({
18
+ axis,
19
+ getStart,
20
+ onDrag,
21
+ style,
14
22
  }: {
15
- model: MsaViewModel
23
+ axis: 'x' | 'y'
24
+ getStart: () => number
25
+ onDrag: (delta: number, startValue: number) => void
26
+ style: React.CSSProperties
16
27
  }) {
17
- const { resizeHandleWidth } = model
18
28
  const [hovered, setHovered] = useState(false)
19
- const { startDrag } = useDragScroll(
20
- 'x',
21
- useCallback(
22
- (delta: number, startWidth: number) => {
23
- model.setTreeAreaWidth(startWidth + delta)
24
- },
25
- [model],
26
- ),
27
- )
29
+ const { startDrag } = useDragScroll(axis, onDrag)
28
30
 
29
31
  return (
30
32
  <div
31
33
  onMouseDown={event => {
32
- startDrag(event, model.treeAreaWidth)
34
+ startDrag(event, getStart())
33
35
  }}
34
36
  onMouseEnter={() => {
35
37
  setHovered(true)
@@ -38,13 +40,34 @@ export const VerticalResizeHandle = observer(function ({
38
40
  setHovered(false)
39
41
  }}
40
42
  style={{
41
- cursor: 'ew-resize',
42
- width: resizeHandleWidth,
43
- background: hovered ? handleBgHover : handleBg,
44
43
  position: 'relative',
44
+ cursor: axis === 'x' ? 'ew-resize' : 'ns-resize',
45
+ background: hovered ? handleBgHover : handleBg,
46
+ ...style,
45
47
  }}
46
48
  />
47
49
  )
50
+ }
51
+
52
+ export const VerticalResizeHandle = observer(function ({
53
+ model,
54
+ }: {
55
+ model: MsaViewModel
56
+ }) {
57
+ const onDrag = useCallback(
58
+ (delta: number, startWidth: number) => {
59
+ model.setTreeAreaWidth(startWidth + delta)
60
+ },
61
+ [model],
62
+ )
63
+ return (
64
+ <ResizeHandle
65
+ axis="x"
66
+ getStart={() => model.treeAreaWidth}
67
+ onDrag={onDrag}
68
+ style={{ width: model.resizeHandleWidth }}
69
+ />
70
+ )
48
71
  })
49
72
 
50
73
  export const HorizontalResizeHandle = observer(function ({
@@ -52,36 +75,18 @@ export const HorizontalResizeHandle = observer(function ({
52
75
  }: {
53
76
  model: MsaViewModel
54
77
  }) {
55
- const { resizeHandleWidth } = model
56
- const [hovered, setHovered] = useState(false)
57
- const { startDrag } = useDragScroll(
58
- 'y',
59
- useCallback(
60
- (delta: number, startHeight: number) => {
61
- model.setHeight(startHeight + delta)
62
- },
63
- [model],
64
- ),
78
+ const onDrag = useCallback(
79
+ (delta: number, startHeight: number) => {
80
+ model.setHeight(startHeight + delta)
81
+ },
82
+ [model],
65
83
  )
66
-
67
84
  return (
68
- <div
69
- onMouseDown={event => {
70
- startDrag(event, model.height)
71
- }}
72
- onMouseEnter={() => {
73
- setHovered(true)
74
- }}
75
- onMouseLeave={() => {
76
- setHovered(false)
77
- }}
78
- style={{
79
- cursor: 'ns-resize',
80
- width: '100%',
81
- height: resizeHandleWidth,
82
- background: hovered ? handleBgHover : handleBg,
83
- position: 'relative',
84
- }}
85
+ <ResizeHandle
86
+ axis="y"
87
+ getStart={() => model.height}
88
+ onDrag={onDrag}
89
+ style={{ width: '100%', height: model.resizeHandleWidth }}
85
90
  />
86
91
  )
87
92
  })
@@ -91,38 +96,24 @@ export const ConservationTrackResizeHandle = observer(function ({
91
96
  }: {
92
97
  model: MsaViewModel
93
98
  }) {
94
- const { resizeHandleWidth } = model
95
- const [hovered, setHovered] = useState(false)
96
- const { startDrag } = useDragScroll(
97
- 'y',
98
- useCallback(
99
- (delta: number, startHeight: number) => {
100
- model.setConservationTrackHeight(Math.max(10, startHeight + delta))
101
- },
102
- [model],
103
- ),
99
+ const onDrag = useCallback(
100
+ (delta: number, startHeight: number) => {
101
+ model.setConservationTrackHeight(Math.max(10, startHeight + delta))
102
+ },
103
+ [model],
104
104
  )
105
-
106
105
  return (
107
- <div
108
- onMouseDown={event => {
109
- startDrag(event, model.conservationTrackHeight)
110
- }}
111
- onMouseEnter={() => {
112
- setHovered(true)
113
- }}
114
- onMouseLeave={() => {
115
- setHovered(false)
116
- }}
106
+ <ResizeHandle
107
+ axis="y"
108
+ getStart={() => model.conservationTrackHeight}
109
+ onDrag={onDrag}
117
110
  style={{
118
111
  position: 'absolute',
119
112
  bottom: 0,
120
113
  left: 0,
121
- cursor: 'ns-resize',
122
114
  width: '100%',
123
- height: resizeHandleWidth,
115
+ height: model.resizeHandleWidth,
124
116
  zIndex: 1,
125
- background: hovered ? handleBgHover : handleBg,
126
117
  }}
127
118
  />
128
119
  )
@@ -0,0 +1,48 @@
1
+ import React, { useState } from 'react'
2
+
3
+ import { useDragScroll } from '../useDragScroll.ts'
4
+
5
+ export const scrollbarThumbFill = 'rgba(66,119,127,0.3)'
6
+ const scrollbarThumbFillHovered = 'rgba(66,119,127,0.6)'
7
+
8
+ /**
9
+ * The draggable thumb shared by the horizontal Minimap and the
10
+ * VerticalScrollbar. `getStart` is read at mousedown and handed back to
11
+ * `onDrag` alongside the pixel delta since, so a caller only supplies the
12
+ * scroll offset it drives and where the thumb sits.
13
+ */
14
+ export default function ScrollbarThumb({
15
+ axis,
16
+ getStart,
17
+ onDrag,
18
+ style,
19
+ }: {
20
+ axis: 'x' | 'y'
21
+ getStart: () => number
22
+ onDrag: (delta: number, startValue: number) => void
23
+ style: React.CSSProperties
24
+ }) {
25
+ const [hovered, setHovered] = useState(false)
26
+ const { startDrag } = useDragScroll(axis, onDrag)
27
+
28
+ return (
29
+ <div
30
+ onMouseOver={() => {
31
+ setHovered(true)
32
+ }}
33
+ onMouseOut={() => {
34
+ setHovered(false)
35
+ }}
36
+ onMouseDown={event => {
37
+ startDrag(event, getStart())
38
+ }}
39
+ style={{
40
+ position: 'absolute',
41
+ zIndex: 100,
42
+ cursor: 'pointer',
43
+ background: hovered ? scrollbarThumbFillHovered : scrollbarThumbFill,
44
+ ...style,
45
+ }}
46
+ />
47
+ )
48
+ }
@@ -1,11 +1,11 @@
1
1
  import React, { useState } from 'react'
2
2
 
3
3
  import { makeStyles } from '@jbrowse/core/util/tss-react'
4
- import { Button, TextField } from '@mui/material'
4
+ import { TextField } from '@mui/material'
5
5
  import { getUngappedSequence } from 'msa-parsers'
6
6
 
7
- import copy from '../vendor/copyToClipboard.ts'
8
7
  import Checkbox2 from './Checkbox2.tsx'
8
+ import CopyButton from './CopyButton.tsx'
9
9
 
10
10
  const useStyles = makeStyles()({
11
11
  textAreaFont: {
@@ -21,7 +21,6 @@ const useStyles = makeStyles()({
21
21
 
22
22
  export default function SequenceTextArea({ str }: { str: [string, string][] }) {
23
23
  const { classes } = useStyles()
24
- const [copied, setCopied] = useState(false)
25
24
  const [showGaps, setShowGaps] = useState(false)
26
25
  const [showEmpty, setShowEmpty] = useState(false)
27
26
 
@@ -32,19 +31,7 @@ export default function SequenceTextArea({ str }: { str: [string, string][] }) {
32
31
  .join('\n')
33
32
  return (
34
33
  <>
35
- <Button
36
- color="primary"
37
- variant="contained"
38
- onClick={() => {
39
- copy(disp)
40
- setCopied(true)
41
- setTimeout(() => {
42
- setCopied(false)
43
- }, 500)
44
- }}
45
- >
46
- {copied ? 'Copied!' : 'Copy to clipboard'}
47
- </Button>
34
+ <CopyButton text={disp} />
48
35
  <Checkbox2
49
36
  label="Show gaps"
50
37
  checked={showGaps}
@@ -32,16 +32,9 @@ const AnnotationBlock = observer(function ({
32
32
 
33
33
  const colorScheme = customColorScheme ?? modelColorScheme
34
34
  const { theme, contrastScheme } = useColorContrast(colorScheme)
35
- const ref = useCanvasAutorun(
36
- ctx => {
37
- const {
38
- blockSize,
39
- bgColor,
40
- colWidth,
41
- fontSize,
42
- rowHeight,
43
- highResScaleFactor,
44
- } = model
35
+ const ref = useCanvasAutorun({
36
+ draw: ctx => {
37
+ const { bgColor, colWidth, fontSize } = model
45
38
  ctx.resetTransform()
46
39
  ctx.scale(highResScaleFactor, highResScaleFactor)
47
40
  ctx.clearRect(0, 0, blockSize, rowHeight)
@@ -62,8 +55,10 @@ const AnnotationBlock = observer(function ({
62
55
  blockSize,
63
56
  })
64
57
  },
65
- [model, offsetX, theme, contrastScheme, colorScheme, data],
66
- )
58
+ width: blockSize * highResScaleFactor,
59
+ height: rowHeight * highResScaleFactor,
60
+ deps: [model, offsetX, theme, contrastScheme, colorScheme, data],
61
+ })
67
62
  return (
68
63
  <canvas
69
64
  ref={ref}
@@ -1,61 +1,47 @@
1
- import React, { useCallback, useState } from 'react'
1
+ import React, { useCallback } from 'react'
2
2
 
3
- import { clamp } from '@jbrowse/core/util'
4
3
  import { observer } from 'mobx-react'
5
4
 
6
- import { useDragScroll } from '../useDragScroll.ts'
5
+ import ScrollbarThumb from './ScrollbarThumb.tsx'
7
6
 
8
7
  import type { MsaViewModel } from '../model.ts'
9
8
 
9
+ const width = 20
10
+
10
11
  const VerticalScrollbar = observer(({ model }: { model: MsaViewModel }) => {
11
12
  const { msaAreaHeight, scrollY, totalHeight } = model
12
- const [hovered, setHovered] = useState(false)
13
- const fill = 'rgba(66, 119, 127, 0.3)'
14
13
  const unit = msaAreaHeight / totalHeight
15
- const top = -scrollY
16
- const bottom = top + msaAreaHeight
17
- const t = top * unit
18
- const b = bottom * unit
19
- const { startDrag } = useDragScroll(
20
- 'y',
21
- useCallback(
22
- (delta: number, startScroll: number) => {
23
- model.setScrollY(clamp(startScroll - delta / unit, model.maxScrollY, 0))
24
- },
25
- [model, unit],
26
- ),
14
+ const t = -scrollY * unit
15
+ const b = (-scrollY + msaAreaHeight) * unit
16
+
17
+ const onDrag = useCallback(
18
+ (delta: number, startScroll: number) => {
19
+ model.setScrollY(startScroll - delta / unit)
20
+ },
21
+ [model, unit],
27
22
  )
23
+
28
24
  return (
29
25
  <div
30
26
  style={{
31
27
  position: 'relative',
32
- width: 20,
28
+ width,
33
29
  height: msaAreaHeight,
34
30
  borderLeft: '1px solid #555',
35
31
  borderTop: '1px solid #555',
36
32
  boxSizing: 'border-box',
37
33
  }}
38
34
  >
39
- <div
35
+ <ScrollbarThumb
36
+ axis="y"
37
+ getStart={() => model.scrollY}
38
+ onDrag={onDrag}
40
39
  style={{
41
- position: 'absolute',
42
40
  top: Math.max(0, t),
43
41
  left: 0,
44
- background: hovered ? 'rgba(66,119,127,0.6)' : fill,
45
- cursor: 'pointer',
46
42
  boxSizing: 'border-box',
47
- width: 20,
43
+ width,
48
44
  height: Math.max(b - t, 20),
49
- zIndex: 100,
50
- }}
51
- onMouseOver={() => {
52
- setHovered(true)
53
- }}
54
- onMouseOut={() => {
55
- setHovered(false)
56
- }}
57
- onMouseDown={event => {
58
- startDrag(event, model.scrollY)
59
45
  }}
60
46
  />
61
47
  </div>
@@ -43,10 +43,9 @@ const Toggles = observer(function ({ model }: { model: MsaViewModel }) {
43
43
  })
44
44
 
45
45
  const FeatureTable = observer(function ({ model }: { model: MsaViewModel }) {
46
- const { tidyInterProAnnotationTypes, tidyInterProAnnotations, fillPalette } =
47
- model
46
+ const { annotationTypes, annotations, fillPalette } = model
48
47
  const counts = new Map<string, number>()
49
- for (const annot of tidyInterProAnnotations) {
48
+ for (const annot of annotations) {
50
49
  counts.set(annot.accession, (counts.get(annot.accession) ?? 0) + 1)
51
50
  }
52
51
  return (
@@ -64,7 +63,7 @@ const FeatureTable = observer(function ({ model }: { model: MsaViewModel }) {
64
63
  </TableRow>
65
64
  </TableHead>
66
65
  <TableBody>
67
- {[...tidyInterProAnnotationTypes.values()].map(
66
+ {[...annotationTypes.values()].map(
68
67
  ({ accession, name, description }) => (
69
68
  <TableRow key={accession}>
70
69
  <TableCell padding="checkbox">
@@ -14,13 +14,14 @@ import {
14
14
  Typography,
15
15
  } from '@mui/material'
16
16
  import { observer } from 'mobx-react'
17
+ import { interProScanResponseToAnnotations } from 'msa-parsers'
17
18
 
18
19
  import { jsonfetch } from '../../fetchUtils.ts'
19
20
 
20
- import type { InterProScanResponse } from '../../launchInterProScan.ts'
21
21
  import type { MsaViewModel } from '../../model.ts'
22
+ import type { InterProScanResponse } from 'msa-parsers'
22
23
 
23
- const UserProvidedDomainsDialog = observer(function ({
24
+ const InterProScanFileDialog = observer(function ({
24
25
  handleClose,
25
26
  model,
26
27
  }: {
@@ -34,7 +35,7 @@ const UserProvidedDomainsDialog = observer(function ({
34
35
  return (
35
36
  <Dialog
36
37
  maxWidth="xl"
37
- title="Open protein domains from file"
38
+ title="Open InterProScan results"
38
39
  onClose={() => {
39
40
  handleClose()
40
41
  }}
@@ -112,16 +113,7 @@ const UserProvidedDomainsDialog = observer(function ({
112
113
  ? JSON.parse(await file.text())
113
114
  : await jsonfetch(interProURL)
114
115
 
115
- // key each result by its xref id; a result without one has no
116
- // row to attach to, and keying it `undefined` would draw a
117
- // phantom row's worth of domains
118
- model.setDomains(
119
- Object.fromEntries(
120
- ret.results
121
- .map(r => [r.xref[0]?.id, r] as const)
122
- .filter(e => e[0] !== undefined),
123
- ),
124
- )
116
+ model.setAnnotations(interProScanResponseToAnnotations(ret))
125
117
  getSession(model).notify(
126
118
  'Loaded interproscan results',
127
119
  'success',
@@ -143,4 +135,4 @@ const UserProvidedDomainsDialog = observer(function ({
143
135
  )
144
136
  })
145
137
 
146
- export default UserProvidedDomainsDialog
138
+ export default InterProScanFileDialog
@@ -1,11 +1,13 @@
1
- import React, { useState } from 'react'
1
+ import React from 'react'
2
2
 
3
3
  import { Dialog } from '@jbrowse/core/ui'
4
4
  import { makeStyles } from '@jbrowse/core/util/tss-react'
5
- import { Button, DialogActions, DialogContent } from '@mui/material'
5
+ import { Button, DialogActions, DialogContent, Typography } from '@mui/material'
6
6
  import { observer } from 'mobx-react'
7
7
 
8
- import copy from '../../vendor/copyToClipboard.ts'
8
+ import CopyButton from '../CopyButton.tsx'
9
+
10
+ import type { BasicTrack } from '../../types.ts'
9
11
 
10
12
  const useStyles = makeStyles()(theme => ({
11
13
  textArea: {
@@ -20,10 +22,10 @@ const TrackInfoDialog = observer(function ({
20
22
  model,
21
23
  onClose,
22
24
  }: {
23
- model: { name: string; data: string }
25
+ model: BasicTrack['model']
24
26
  onClose: () => void
25
27
  }) {
26
- const [label, setLabel] = useState('Copy to clipboard')
28
+ const { name, data } = model
27
29
  const { classes } = useStyles()
28
30
  return (
29
31
  <Dialog
@@ -31,23 +33,19 @@ const TrackInfoDialog = observer(function ({
31
33
  onClose={onClose}
32
34
  fullWidth
33
35
  maxWidth="lg"
34
- title={`Track info - ${model.name}`}
36
+ title={`Track info - ${name}`}
35
37
  >
36
38
  <DialogContent>
37
- <Button
38
- variant="contained"
39
- color="primary"
40
- onClick={() => {
41
- copy(model.data)
42
- setLabel('Copied!')
43
- setTimeout(() => {
44
- setLabel('Copy to clipboard')
45
- }, 300)
46
- }}
47
- >
48
- {label}
49
- </Button>
50
- <pre className={classes.textArea}>{model.data}</pre>
39
+ {/* the bar tracks (conservation) are computed per column and carry no
40
+ text of their own, so there is nothing to show or copy for them */}
41
+ {data === undefined ? (
42
+ <Typography>This track has no text data.</Typography>
43
+ ) : (
44
+ <>
45
+ <CopyButton text={data} />
46
+ <pre className={classes.textArea}>{data}</pre>
47
+ </>
48
+ )}
51
49
  </DialogContent>
52
50
  <DialogActions>
53
51
  <Button variant="contained" onClick={onClose} color="secondary">
@@ -7,7 +7,7 @@ import Menu from '@mui/icons-material/Menu'
7
7
  import PhotoCamera from '@mui/icons-material/PhotoCamera'
8
8
  import { observer } from 'mobx-react'
9
9
 
10
- import { getDomainMenu } from './getDomainsMenu.ts'
10
+ import { getAnnotationsMenu } from './getAnnotationsMenu.ts'
11
11
 
12
12
  import type { MsaViewModel } from '../../model.ts'
13
13
 
@@ -41,9 +41,9 @@ const FileMenu = observer(({ model }: { model: MsaViewModel }) => {
41
41
  },
42
42
  },
43
43
  {
44
- label: 'Domain settings',
44
+ label: 'Annotations',
45
45
  type: 'subMenu',
46
- subMenu: getDomainMenu({
46
+ subMenu: getAnnotationsMenu({
47
47
  model,
48
48
  }),
49
49
  },
@@ -16,8 +16,11 @@ const GappynessSlider = observer(function GappynessSlider({
16
16
  }
17
17
  return (
18
18
  <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
19
+ {/* the threshold is inclusive: blanks are columns whose gap count is at
20
+ least ceil(allowedGappyness% of the rows), so at 100 it hides the
21
+ columns that are entirely gaps rather than nothing */}
19
22
  <Typography style={{ whiteSpace: 'nowrap' }}>
20
- Hide columns w/ &gt;{allowedGappyness}% gaps
23
+ Hide columns w/ &ge;{allowedGappyness}% gaps
21
24
  </Typography>
22
25
  <Slider
23
26
  // Named, because the class alone is not enough to find it from outside.
@@ -9,22 +9,23 @@ import Visibility from '@mui/icons-material/Visibility'
9
9
  import type { MsaViewModel } from '../../model.ts'
10
10
 
11
11
  const FeatureFilterDialog = lazy(() => import('../dialogs/FeatureDialog.tsx'))
12
- const UserProvidedDomainsDialog = lazy(
13
- () => import('../dialogs/UserProvidedDomainsDialog.tsx'),
12
+ const InterProScanFileDialog = lazy(
13
+ () => import('../dialogs/InterProScanFileDialog.tsx'),
14
14
  )
15
15
  const InterProScanDialog = lazy(
16
16
  () => import('../dialogs/InterProScanDialog.tsx'),
17
17
  )
18
18
 
19
- export function getDomainMenu({ model }: { model: MsaViewModel }) {
19
+ export function getAnnotationsMenu({ model }: { model: MsaViewModel }) {
20
20
  const { showDomains, actuallyShowDomains, subFeatureRows, noDomains } = model
21
+ const noneLoaded = noDomains ? ' (none loaded)' : ''
21
22
  return [
22
23
  {
23
- label: 'Open domains...',
24
+ label: 'Open InterProScan results...',
24
25
  icon: FolderOpen,
25
26
  onClick: () => {
26
27
  model.queueDialog(handleClose => [
27
- UserProvidedDomainsDialog,
28
+ InterProScanFileDialog,
28
29
  {
29
30
  handleClose,
30
31
  model,
@@ -33,7 +34,7 @@ export function getDomainMenu({ model }: { model: MsaViewModel }) {
33
34
  },
34
35
  },
35
36
  {
36
- label: 'Query InterProScan for domains...',
37
+ label: 'Query InterProScan...',
37
38
  icon: Search,
38
39
  onClick: () => {
39
40
  model.queueDialog(handleClose => [
@@ -46,7 +47,7 @@ export function getDomainMenu({ model }: { model: MsaViewModel }) {
46
47
  },
47
48
  },
48
49
  {
49
- label: `Show domains${noDomains ? ' (no domains loaded)' : ''}`,
50
+ label: `Show annotations${noneLoaded}`,
50
51
  disabled: noDomains,
51
52
  icon: Visibility,
52
53
  checked: actuallyShowDomains,
@@ -56,7 +57,7 @@ export function getDomainMenu({ model }: { model: MsaViewModel }) {
56
57
  },
57
58
  },
58
59
  {
59
- label: `Use sub-row layout${noDomains ? ' (no domains loaded)' : ''}`,
60
+ label: `Use sub-row layout${noneLoaded}`,
60
61
  disabled: noDomains,
61
62
  checked: actuallyShowDomains ? subFeatureRows : false,
62
63
  icon: Sort,
@@ -66,7 +67,7 @@ export function getDomainMenu({ model }: { model: MsaViewModel }) {
66
67
  },
67
68
  },
68
69
  {
69
- label: `Filter domains${noDomains ? ' (no domains loaded)' : ''}`,
70
+ label: `Filter annotations${noneLoaded}`,
70
71
  icon: FilterAlt,
71
72
  disabled: noDomains,
72
73
  onClick: () => {