react-msaview 6.5.0 → 7.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 (212) hide show
  1. package/README.md +6 -5
  2. package/bundle/index.js +100 -101
  3. package/bundle/index.js.map +4 -4
  4. package/dist/components/Loading.js +2 -2
  5. package/dist/components/Loading.js.map +1 -1
  6. package/dist/components/MSAViewer.d.ts +10 -2
  7. package/dist/components/MSAViewer.js +90 -2
  8. package/dist/components/MSAViewer.js.map +1 -1
  9. package/dist/components/ResizeHandles.d.ts +3 -3
  10. package/dist/components/ResizeHandles.js +28 -21
  11. package/dist/components/ResizeHandles.js.map +1 -1
  12. package/dist/components/dialogs/{InterProScanDialog.d.ts → AnnotationFileDialog.d.ts} +2 -2
  13. package/dist/components/dialogs/AnnotationFileDialog.js +92 -0
  14. package/dist/components/dialogs/AnnotationFileDialog.js.map +1 -0
  15. package/dist/components/dialogs/FeatureDialog.js +13 -9
  16. package/dist/components/dialogs/FeatureDialog.js.map +1 -1
  17. package/dist/components/header/Header.js +4 -0
  18. package/dist/components/header/Header.js.map +1 -1
  19. package/dist/components/header/HeaderStatusArea.js +0 -2
  20. package/dist/components/header/HeaderStatusArea.js.map +1 -1
  21. package/dist/components/header/LoadWarnings.d.ts +6 -0
  22. package/dist/components/header/LoadWarnings.js +36 -0
  23. package/dist/components/header/LoadWarnings.js.map +1 -0
  24. package/dist/components/header/MultiAlignmentSelector.js +0 -0
  25. package/dist/components/header/MultiAlignmentSelector.js.map +1 -1
  26. package/dist/components/header/TreeSettingsMenu.js +9 -1
  27. package/dist/components/header/TreeSettingsMenu.js.map +1 -1
  28. package/dist/components/header/UnshareableDataWarning.d.ts +6 -0
  29. package/dist/components/header/UnshareableDataWarning.js +42 -0
  30. package/dist/components/header/UnshareableDataWarning.js.map +1 -0
  31. package/dist/components/header/getAnnotationsMenu.js +14 -14
  32. package/dist/components/header/getAnnotationsMenu.js.map +1 -1
  33. package/dist/components/import/ImportForm.js +1 -1
  34. package/dist/components/import/ImportForm.js.map +1 -1
  35. package/dist/components/import/ImportFormExamples.js +2 -6
  36. package/dist/components/import/ImportFormExamples.js.map +1 -1
  37. package/dist/components/import/util.js +3 -0
  38. package/dist/components/import/util.js.map +1 -1
  39. package/dist/components/msa/MSACanvasBlock.js +4 -5
  40. package/dist/components/msa/MSACanvasBlock.js.map +1 -1
  41. package/dist/components/msa/msaRaster.d.ts +13 -3
  42. package/dist/components/msa/msaRaster.js +173 -61
  43. package/dist/components/msa/msaRaster.js.map +1 -1
  44. package/dist/components/msa/renderHighlights.d.ts +39 -1
  45. package/dist/components/msa/renderHighlights.js +71 -6
  46. package/dist/components/msa/renderHighlights.js.map +1 -1
  47. package/dist/components/msa/renderMSABlock.d.ts +1 -2
  48. package/dist/components/msa/renderMSABlock.js +24 -28
  49. package/dist/components/msa/renderMSABlock.js.map +1 -1
  50. package/dist/components/msa/renderMSAMouseover.js +9 -30
  51. package/dist/components/msa/renderMSAMouseover.js.map +1 -1
  52. package/dist/components/renderCtx.d.ts +1 -1
  53. package/dist/components/tracks/TrackBlocks.js +10 -11
  54. package/dist/components/tracks/TrackBlocks.js.map +1 -1
  55. package/dist/components/tracks/drawTracks.d.ts +26 -8
  56. package/dist/components/tracks/drawTracks.js +123 -13
  57. package/dist/components/tracks/drawTracks.js.map +1 -1
  58. package/dist/components/tree/TreeCanvas.js +2 -3
  59. package/dist/components/tree/TreeCanvas.js.map +1 -1
  60. package/dist/components/tree/TreeCanvasBlock.js +4 -2
  61. package/dist/components/tree/TreeCanvasBlock.js.map +1 -1
  62. package/dist/components/tree/TreeRuler.js +13 -3
  63. package/dist/components/tree/TreeRuler.js.map +1 -1
  64. package/dist/components/tree/renderTreeCanvas.d.ts +0 -1
  65. package/dist/components/tree/renderTreeCanvas.js +70 -30
  66. package/dist/components/tree/renderTreeCanvas.js.map +1 -1
  67. package/dist/components/tree/scaleBar.d.ts +10 -0
  68. package/dist/components/tree/scaleBar.js +24 -0
  69. package/dist/components/tree/scaleBar.js.map +1 -0
  70. package/dist/constants.d.ts +3 -1
  71. package/dist/constants.js +18 -1
  72. package/dist/constants.js.map +1 -1
  73. package/dist/fetchUtils.d.ts +7 -7
  74. package/dist/fetchUtils.js +4 -32
  75. package/dist/fetchUtils.js.map +1 -1
  76. package/dist/hierarchy.d.ts +2 -1
  77. package/dist/hierarchy.js +16 -10
  78. package/dist/hierarchy.js.map +1 -1
  79. package/dist/index.d.ts +2 -1
  80. package/dist/index.js +8 -2
  81. package/dist/index.js.map +1 -1
  82. package/dist/model/DataModel.js +5 -2
  83. package/dist/model/DataModel.js.map +1 -1
  84. package/dist/model.d.ts +454 -177
  85. package/dist/model.js +1180 -511
  86. package/dist/model.js.map +1 -1
  87. package/dist/renderToStaticMarkup.d.ts +11 -0
  88. package/dist/renderToStaticMarkup.js +30 -7
  89. package/dist/renderToStaticMarkup.js.map +1 -1
  90. package/dist/renderToSvg.js +25 -19
  91. package/dist/renderToSvg.js.map +1 -1
  92. package/dist/rowCoordinateCalculations.d.ts +25 -25
  93. package/dist/rowCoordinateCalculations.js +12 -36
  94. package/dist/rowCoordinateCalculations.js.map +1 -1
  95. package/dist/seqPosToGlobalCol.js +3 -1
  96. package/dist/seqPosToGlobalCol.js.map +1 -1
  97. package/dist/svgTestUtil.d.ts +144 -82
  98. package/dist/types.d.ts +97 -3
  99. package/dist/util.d.ts +11 -4
  100. package/dist/util.js +30 -10
  101. package/dist/util.js.map +1 -1
  102. package/dist/version.d.ts +1 -1
  103. package/dist/version.js +1 -1
  104. package/dist/wuss.d.ts +14 -0
  105. package/dist/wuss.js +55 -0
  106. package/dist/wuss.js.map +1 -0
  107. package/package.json +10 -8
  108. package/src/components/Loading.tsx +12 -2
  109. package/src/components/MSAViewer.tsx +106 -2
  110. package/src/components/ResizeHandles.tsx +31 -31
  111. package/src/components/dialogs/AnnotationFileDialog.tsx +162 -0
  112. package/src/components/dialogs/FeatureDialog.tsx +13 -9
  113. package/src/components/header/Header.tsx +4 -0
  114. package/src/components/header/HeaderStatusArea.tsx +0 -5
  115. package/src/components/header/LoadWarnings.tsx +49 -0
  116. package/src/components/header/MultiAlignmentSelector.tsx +0 -0
  117. package/src/components/header/TreeSettingsMenu.tsx +9 -1
  118. package/src/components/header/UnshareableDataWarning.tsx +58 -0
  119. package/src/components/header/getAnnotationsMenu.ts +16 -17
  120. package/src/components/import/ImportForm.tsx +2 -2
  121. package/src/components/import/ImportFormExamples.tsx +2 -6
  122. package/src/components/import/util.ts +3 -0
  123. package/src/components/msa/MSACanvasBlock.tsx +4 -5
  124. package/src/components/msa/msaRaster.ts +216 -59
  125. package/src/components/msa/renderHighlights.ts +103 -5
  126. package/src/components/msa/renderMSABlock.ts +25 -36
  127. package/src/components/msa/renderMSAMouseover.ts +9 -39
  128. package/src/components/renderCtx.ts +1 -0
  129. package/src/components/tracks/TrackBlocks.tsx +10 -13
  130. package/src/components/tracks/drawTracks.ts +165 -17
  131. package/src/components/tree/TreeCanvas.tsx +2 -3
  132. package/src/components/tree/TreeCanvasBlock.tsx +4 -2
  133. package/src/components/tree/TreeRuler.tsx +38 -3
  134. package/src/components/tree/renderTreeCanvas.ts +79 -35
  135. package/src/components/tree/scaleBar.ts +23 -0
  136. package/src/constants.ts +20 -1
  137. package/src/fetchUtils.ts +7 -40
  138. package/src/hierarchy.ts +20 -11
  139. package/src/index.ts +18 -2
  140. package/src/model/DataModel.ts +6 -2
  141. package/src/model.ts +1352 -616
  142. package/src/rawImports.d.ts +9 -0
  143. package/src/renderToStaticMarkup.ts +34 -7
  144. package/src/renderToSvg.tsx +33 -27
  145. package/src/rowCoordinateCalculations.ts +15 -59
  146. package/src/seqPosToGlobalCol.ts +3 -1
  147. package/src/types.ts +131 -4
  148. package/src/util.ts +31 -14
  149. package/src/version.ts +1 -1
  150. package/src/wuss.ts +62 -0
  151. package/dist/components/dialogs/InterProScanDialog.js +0 -137
  152. package/dist/components/dialogs/InterProScanDialog.js.map +0 -1
  153. package/dist/components/dialogs/InterProScanFileDialog.d.ts +0 -7
  154. package/dist/components/dialogs/InterProScanFileDialog.js +0 -64
  155. package/dist/components/dialogs/InterProScanFileDialog.js.map +0 -1
  156. package/dist/launchInterProScan.d.ts +0 -13
  157. package/dist/launchInterProScan.js +0 -110
  158. package/dist/launchInterProScan.js.map +0 -1
  159. package/dist/useColorContrast.d.ts +0 -10
  160. package/dist/useColorContrast.js +0 -13
  161. package/dist/useColorContrast.js.map +0 -1
  162. package/src/clustalX.test.ts +0 -74
  163. package/src/colSpace.test.ts +0 -67
  164. package/src/collapseLogic.test.ts +0 -115
  165. package/src/colorSchemes.docs.test.ts +0 -57
  166. package/src/columnCounts.test.ts +0 -73
  167. package/src/columnStats.test.ts +0 -94
  168. package/src/columnTracks.test.ts +0 -97
  169. package/src/components/dialogs/InterProScanDialog.tsx +0 -214
  170. package/src/components/dialogs/InterProScanFileDialog.tsx +0 -138
  171. package/src/components/minimap/minimapLayout.test.ts +0 -71
  172. package/src/components/msa/msaRaster.test.ts +0 -145
  173. package/src/components/msa/renderBoxFeatureCanvasBlock.test.ts +0 -51
  174. package/src/components/tree/renderTreeCanvas.test.ts +0 -296
  175. package/src/dataInitialized.test.ts +0 -42
  176. package/src/domainBands.test.ts +0 -122
  177. package/src/domainLetterColor.test.ts +0 -80
  178. package/src/exportFileName.test.ts +0 -40
  179. package/src/fetchUtils.test.ts +0 -83
  180. package/src/flatToTree.test.ts +0 -51
  181. package/src/hierarchy.test.ts +0 -228
  182. package/src/highlights.test.ts +0 -70
  183. package/src/impgRender.test.tsx +0 -68
  184. package/src/largeTree.test.ts +0 -67
  185. package/src/launchInterProScan.ts +0 -167
  186. package/src/loadErrorDisplay.test.tsx +0 -102
  187. package/src/modelFilehandleLoaders.test.ts +0 -137
  188. package/src/modelReset.test.ts +0 -120
  189. package/src/neighborJoining.test.ts +0 -164
  190. package/src/parseAsn1.test.ts +0 -36
  191. package/src/propertyConservation.test.ts +0 -36
  192. package/src/renderColumnTracksSvg.test.tsx +0 -108
  193. package/src/renderDomainsSvg.test.tsx +0 -118
  194. package/src/renderHighlightsSvg.test.tsx +0 -85
  195. package/src/renderMinimapSvg.test.tsx +0 -62
  196. package/src/renderRasterSvg.test.tsx +0 -144
  197. package/src/renderSequenceLogoSvg.test.tsx +0 -165
  198. package/src/renderToSvg.test.tsx +0 -65
  199. package/src/reset.test.ts +0 -61
  200. package/src/rowCoordinateCalculations.test.ts +0 -211
  201. package/src/rowSpace.test.ts +0 -46
  202. package/src/segmentOverlay.test.ts +0 -70
  203. package/src/seqPosToGlobalCol.test.ts +0 -59
  204. package/src/sequenceLogo.test.ts +0 -88
  205. package/src/setFontSize.test.ts +0 -33
  206. package/src/stripDefault.test.ts +0 -48
  207. package/src/stripDefaultSnapshot.test.ts +0 -87
  208. package/src/treeMetadata.test.ts +0 -39
  209. package/src/useCanvasAutorun.test.tsx +0 -98
  210. package/src/useColorContrast.ts +0 -18
  211. package/src/viewChrome.test.tsx +0 -134
  212. package/src/wheelZoom.test.ts +0 -46
@@ -0,0 +1,162 @@
1
+ import React, { useState } from 'react'
2
+
3
+ import { Dialog, ErrorMessage } from '@jbrowse/core/ui'
4
+ import {
5
+ Button,
6
+ DialogActions,
7
+ DialogContent,
8
+ FormControl,
9
+ FormControlLabel,
10
+ Radio,
11
+ RadioGroup,
12
+ TextField,
13
+ Typography,
14
+ } from '@mui/material'
15
+ import { observer } from 'mobx-react'
16
+ import {
17
+ annotationsToGFF,
18
+ interProScanResponseToAnnotations,
19
+ } from 'msa-parsers'
20
+
21
+ import type { MsaViewModel } from '../../model.ts'
22
+ import type { InterProScanResponse } from 'msa-parsers'
23
+
24
+ // GFF3 as the CLI writes it, or the JSON an InterProScan run returns. Both
25
+ // arrive as text and the model keeps GFF, so the JSON is converted on the way
26
+ // in rather than given a second path through the model
27
+ function toGFF(text: string) {
28
+ const trimmed = text.trimStart()
29
+ if (!trimmed.startsWith('{')) {
30
+ return text
31
+ }
32
+ const response: InterProScanResponse = JSON.parse(trimmed)
33
+ return annotationsToGFF(interProScanResponseToAnnotations(response))
34
+ }
35
+
36
+ async function fetchText(url: string) {
37
+ const response = await fetch(url)
38
+ if (!response.ok) {
39
+ throw new Error(
40
+ `HTTP ${response.status} fetching ${url} ${await response.text()}`,
41
+ )
42
+ }
43
+ return response.text()
44
+ }
45
+
46
+ const AnnotationFileDialog = observer(function ({
47
+ handleClose,
48
+ model,
49
+ }: {
50
+ handleClose: () => void
51
+ model: MsaViewModel
52
+ }) {
53
+ const [file, setFile] = useState<File>()
54
+ const [choice, setChoice] = useState('file')
55
+ const [url, setUrl] = useState('')
56
+ const [error, setError] = useState<unknown>()
57
+ const [loading, setLoading] = useState(false)
58
+
59
+ // the error is shown here rather than notified: the standalone app and the
60
+ // MSAViewer component have no jbrowse session to notify, and asking one for
61
+ // its notify() threw out of the success path into the catch, which threw
62
+ // again on the same missing session
63
+ return (
64
+ <Dialog
65
+ maxWidth="xl"
66
+ title="Open annotation file"
67
+ onClose={() => {
68
+ handleClose()
69
+ }}
70
+ open
71
+ >
72
+ <DialogContent>
73
+ {error ? <ErrorMessage error={error} /> : null}
74
+ <Typography>
75
+ Open a GFF3 file of annotations, such as the one `react-msaview-cli
76
+ interpro` writes, or the JSON an InterProScan run returns.
77
+ </Typography>
78
+
79
+ <div style={{ display: 'flex', margin: 30 }}>
80
+ <FormControl component="fieldset">
81
+ <RadioGroup
82
+ value={choice}
83
+ onChange={event => {
84
+ setChoice(event.target.value)
85
+ }}
86
+ >
87
+ <FormControlLabel value="url" control={<Radio />} label="URL" />
88
+ <FormControlLabel value="file" control={<Radio />} label="File" />
89
+ </RadioGroup>
90
+ </FormControl>
91
+ {choice === 'url' ? (
92
+ <div>
93
+ <Typography>Open an annotation file from a URL</Typography>
94
+ <TextField
95
+ label="URL"
96
+ value={url}
97
+ onChange={event => {
98
+ setUrl(event.target.value)
99
+ }}
100
+ />
101
+ </div>
102
+ ) : (
103
+ <div style={{ paddingTop: 20 }}>
104
+ <Typography>
105
+ Open an annotation file from your local drive
106
+ </Typography>
107
+ <Button variant="outlined" component="label">
108
+ Choose File
109
+ <input
110
+ type="file"
111
+ hidden
112
+ onChange={({ target }) => {
113
+ const chosen = target.files?.[0]
114
+ if (chosen) {
115
+ setFile(chosen)
116
+ }
117
+ }}
118
+ />
119
+ </Button>
120
+ {file ? <Typography>{file.name}</Typography> : null}
121
+ </div>
122
+ )}
123
+ </div>
124
+ </DialogContent>
125
+ <DialogActions>
126
+ <Button
127
+ variant="contained"
128
+ color="primary"
129
+ disabled={loading}
130
+ onClick={() => {
131
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
132
+ ;(async () => {
133
+ setLoading(true)
134
+ setError(undefined)
135
+ try {
136
+ const text = await (choice === 'file' && file
137
+ ? file.text()
138
+ : fetchText(url))
139
+ // through the model's own GFF text, so the annotations travel
140
+ // in the snapshot and the shared URL like every other layer.
141
+ // Setting them straight onto the volatile left a view whose
142
+ // link opened without them
143
+ model.setGFF(toGFF(text))
144
+ model.setShowDomains(true)
145
+ handleClose()
146
+ } catch (e) {
147
+ console.error(e)
148
+ setError(e)
149
+ } finally {
150
+ setLoading(false)
151
+ }
152
+ })()
153
+ }}
154
+ >
155
+ Open annotations
156
+ </Button>
157
+ </DialogActions>
158
+ </Dialog>
159
+ )
160
+ })
161
+
162
+ export default AnnotationFileDialog
@@ -16,14 +16,20 @@ import { observer } from 'mobx-react'
16
16
  import type { MsaViewModel } from '../../model.ts'
17
17
 
18
18
  const Toggles = observer(function ({ model }: { model: MsaViewModel }) {
19
- const { featureFilters } = model
19
+ // the accessions the file has, not the ones the filter map happens to hold:
20
+ // that map now carries only what the reader turned off, and before that it
21
+ // accumulated every accession of every file opened in this view
22
+ const { annotationTypes } = model
23
+ const setAll = (shown: boolean) => {
24
+ for (const accession of annotationTypes.keys()) {
25
+ model.setFilter(accession, shown)
26
+ }
27
+ }
20
28
  return (
21
29
  <div>
22
30
  <Button
23
31
  onClick={() => {
24
- for (const key of featureFilters.keys()) {
25
- model.setFilter(key, true)
26
- }
32
+ setAll(true)
27
33
  }}
28
34
  >
29
35
  Toggle all on
@@ -31,9 +37,7 @@ const Toggles = observer(function ({ model }: { model: MsaViewModel }) {
31
37
 
32
38
  <Button
33
39
  onClick={() => {
34
- for (const key of featureFilters.keys()) {
35
- model.setFilter(key, false)
36
- }
40
+ setAll(false)
37
41
  }}
38
42
  >
39
43
  Toggle all off
@@ -68,11 +72,11 @@ const FeatureTable = observer(function ({ model }: { model: MsaViewModel }) {
68
72
  <TableRow key={accession}>
69
73
  <TableCell padding="checkbox">
70
74
  <Checkbox
71
- checked={model.featureFilters.get(accession) ?? false}
75
+ checked={!model.turnedOffFeatures.get(accession)}
72
76
  onChange={() => {
73
77
  model.setFilter(
74
78
  accession,
75
- !model.featureFilters.get(accession),
79
+ !!model.turnedOffFeatures.get(accession),
76
80
  )
77
81
  }}
78
82
  />
@@ -10,9 +10,11 @@ import FileMenu from './FileMenu.tsx'
10
10
  import GappynessSlider from './GappynessSlider.tsx'
11
11
  import HeaderInfoArea from './HeaderInfoArea.tsx'
12
12
  import HeaderStatusArea from './HeaderStatusArea.tsx'
13
+ import LoadWarnings from './LoadWarnings.tsx'
13
14
  import MSASettingsMenu from './MSASettingsMenu.tsx'
14
15
  import MultiAlignmentSelector from './MultiAlignmentSelector.tsx'
15
16
  import TreeSettingsMenu from './TreeSettingsMenu.tsx'
17
+ import UnshareableDataWarning from './UnshareableDataWarning.tsx'
16
18
  import ZoomControls from './ZoomControls.tsx'
17
19
  import ZoomMenu from './ZoomMenu.tsx'
18
20
 
@@ -39,6 +41,8 @@ const Header = observer(function ({ model }: { model: MsaViewModel }) {
39
41
  </div>
40
42
  <HeaderInfoArea model={model} />
41
43
  <Spacer />
44
+ <LoadWarnings model={model} />
45
+ <UnshareableDataWarning model={model} />
42
46
  <HeaderStatusArea model={model} />
43
47
  <Tooltip title="About">
44
48
  <IconButton
@@ -20,11 +20,6 @@ const HeaderStatusArea = observer(({ model }: { model: MsaViewModel }) => {
20
20
  return status ? (
21
21
  <Typography className={classes.margin}>
22
22
  <LoadingEllipses message={status.msg} component="span" />{' '}
23
- {status.url ? (
24
- <a href={status.url} target="_blank" rel="noreferrer">
25
- (status)
26
- </a>
27
- ) : null}{' '}
28
23
  {status.onCancel ? (
29
24
  <Button size="small" onClick={() => status.onCancel?.()}>
30
25
  Cancel
@@ -0,0 +1,49 @@
1
+ import React from 'react'
2
+
3
+ import { makeStyles } from '@jbrowse/core/util/tss-react'
4
+ import Warning from '@mui/icons-material/Warning'
5
+ import { Tooltip, Typography } from '@mui/material'
6
+ import { observer } from 'mobx-react'
7
+
8
+ import type { MsaViewModel } from '../../model.ts'
9
+
10
+ const useStyles = makeStyles()(theme => ({
11
+ warning: {
12
+ display: 'flex',
13
+ alignItems: 'center',
14
+ gap: 4,
15
+ margin: 'auto',
16
+ marginLeft: 10,
17
+ whiteSpace: 'nowrap',
18
+ color: theme.palette.warning.main,
19
+ cursor: 'pointer',
20
+ },
21
+ }))
22
+
23
+ // An optional layer that failed -- a 404 on the annotations, a row-metadata
24
+ // file that is not JSON -- used to go to `error`, which takes dataInitialized
25
+ // down with it and replaces a perfectly good alignment with an error screen.
26
+ // It says so here instead, and the reader dismisses it.
27
+ const LoadWarnings = observer(function ({ model }: { model: MsaViewModel }) {
28
+ const { warnings } = model
29
+ const { classes } = useStyles()
30
+ if (warnings.length === 0) {
31
+ return null
32
+ }
33
+ return (
34
+ <Tooltip title={`${warnings.join(' ')} (click to dismiss)`}>
35
+ <Typography
36
+ component="div"
37
+ className={classes.warning}
38
+ onClick={() => {
39
+ model.clearWarnings()
40
+ }}
41
+ >
42
+ <Warning fontSize="small" />
43
+ {warnings.length > 1 ? `${warnings.length} warnings` : 'Warning'}
44
+ </Typography>
45
+ </Tooltip>
46
+ )
47
+ })
48
+
49
+ export default LoadWarnings
@@ -4,6 +4,7 @@ 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 { maxNeighborJoiningRows } from '../../constants.ts'
7
8
  import { treeSettingsMenuItems } from './settingsMenuItems.ts'
8
9
 
9
10
  import type { MsaViewModel } from '../../model.ts'
@@ -14,6 +15,7 @@ const TreeSettingsMenu = observer(function ({
14
15
  model: MsaViewModel
15
16
  }) {
16
17
  const { rows } = model
18
+ const tooManyRows = rows.length > maxNeighborJoiningRows
17
19
  return (
18
20
  <CascadingMenuButton
19
21
  data-testid="tree_settings_menu"
@@ -30,7 +32,13 @@ const TreeSettingsMenu = observer(function ({
30
32
  type: 'subMenu' as const,
31
33
  subMenu: [
32
34
  {
33
- label: 'Calculate neighbor joining tree (BLOSUM62)',
35
+ // the cap shows in the label rather than arriving as an
36
+ // error after the click, since the click is what would
37
+ // freeze the tab
38
+ label: tooManyRows
39
+ ? `Calculate neighbor joining tree (over ${maxNeighborJoiningRows} rows, use FastTree)`
40
+ : 'Calculate neighbor joining tree (BLOSUM62)',
41
+ disabled: tooManyRows,
34
42
  onClick: () => {
35
43
  try {
36
44
  model.calculateNeighborJoiningTreeFromMSA()
@@ -0,0 +1,58 @@
1
+ import React from 'react'
2
+
3
+ import { makeStyles } from '@jbrowse/core/util/tss-react'
4
+ import LinkOff from '@mui/icons-material/LinkOff'
5
+ import { Tooltip, Typography } from '@mui/material'
6
+ import { observer } from 'mobx-react'
7
+
8
+ import type { MsaViewModel } from '../../model.ts'
9
+
10
+ const useStyles = makeStyles()(theme => ({
11
+ warning: {
12
+ display: 'flex',
13
+ alignItems: 'center',
14
+ gap: 4,
15
+ margin: 'auto',
16
+ marginLeft: 10,
17
+ whiteSpace: 'nowrap',
18
+ color: theme.palette.warning.main,
19
+ cursor: 'help',
20
+ },
21
+ }))
22
+
23
+ function size(bytes: number) {
24
+ return bytes < 1_000_000
25
+ ? `${Math.round(bytes / 1000)} kB`
26
+ : `${(bytes / 1_000_000).toFixed(1)} MB`
27
+ }
28
+
29
+ // The viewer holds documents its own snapshot does not carry, and until this
30
+ // existed it said nothing about it: the view worked, the URL quietly lost the
31
+ // data, and whoever copied the link found out from the person who opened it.
32
+ // See `unshareableData` on the model.
33
+ const UnshareableDataWarning = observer(function ({
34
+ model,
35
+ }: {
36
+ model: MsaViewModel
37
+ }) {
38
+ const { unshareableData } = model
39
+ const { classes } = useStyles()
40
+ if (unshareableData.length === 0) {
41
+ return null
42
+ }
43
+ const what = unshareableData
44
+ .map(({ what, bytes }) => `${what} (${size(bytes)})`)
45
+ .join(' and the ')
46
+ return (
47
+ <Tooltip
48
+ title={`The ${what} came from this computer and is too large to travel in a URL, so a link to this page opens without it. Serve the file over HTTP and open it by URL to share the view.`}
49
+ >
50
+ <Typography component="div" className={classes.warning}>
51
+ <LinkOff fontSize="small" />
52
+ Not in the link
53
+ </Typography>
54
+ </Tooltip>
55
+ )
56
+ })
57
+
58
+ export default UnshareableDataWarning
@@ -2,30 +2,29 @@ import { lazy } from 'react'
2
2
 
3
3
  import FilterAlt from '@mui/icons-material/FilterAlt'
4
4
  import FolderOpen from '@mui/icons-material/FolderOpen'
5
- import Search from '@mui/icons-material/Search'
5
+ import MenuBook from '@mui/icons-material/MenuBook'
6
6
  import Sort from '@mui/icons-material/Sort'
7
7
  import Visibility from '@mui/icons-material/Visibility'
8
8
 
9
9
  import type { MsaViewModel } from '../../model.ts'
10
10
 
11
11
  const FeatureFilterDialog = lazy(() => import('../dialogs/FeatureDialog.tsx'))
12
- const InterProScanFileDialog = lazy(
13
- () => import('../dialogs/InterProScanFileDialog.tsx'),
14
- )
15
- const InterProScanDialog = lazy(
16
- () => import('../dialogs/InterProScanDialog.tsx'),
12
+ const AnnotationFileDialog = lazy(
13
+ () => import('../dialogs/AnnotationFileDialog.tsx'),
17
14
  )
18
15
 
16
+ const domainsTutorial = 'https://gmod.org/JBrowseMSA/tutorials/protein_family'
17
+
19
18
  export function getAnnotationsMenu({ model }: { model: MsaViewModel }) {
20
19
  const { showDomains, actuallyShowDomains, subFeatureRows, noDomains } = model
21
20
  const noneLoaded = noDomains ? ' (none loaded)' : ''
22
21
  return [
23
22
  {
24
- label: 'Open InterProScan results...',
23
+ label: 'Open annotation file...',
25
24
  icon: FolderOpen,
26
25
  onClick: () => {
27
26
  model.queueDialog(handleClose => [
28
- InterProScanFileDialog,
27
+ AnnotationFileDialog,
29
28
  {
30
29
  handleClose,
31
30
  model,
@@ -33,17 +32,17 @@ export function getAnnotationsMenu({ model }: { model: MsaViewModel }) {
33
32
  ])
34
33
  },
35
34
  },
35
+ // The viewer used to submit the whole alignment to the EBI iprscan5 queue
36
+ // and poll it for about fifteen minutes, capped at 140 rows and under a
37
+ // hardcoded email address. `react-msaview-cli interpro` answers the same
38
+ // question from InterPro's precomputed matches in seconds, and
39
+ // `interproscan` covers rows InterPro has not seen. Neither belongs in a
40
+ // browser tab, so this points at the page that walks through them.
36
41
  {
37
- label: 'Query InterProScan...',
38
- icon: Search,
42
+ label: 'How to get a domain file...',
43
+ icon: MenuBook,
39
44
  onClick: () => {
40
- model.queueDialog(handleClose => [
41
- InterProScanDialog,
42
- {
43
- handleClose,
44
- model,
45
- },
46
- ])
45
+ window.open(domainsTutorial, '_blank', 'noopener,noreferrer')
47
46
  },
48
47
  },
49
48
  {
@@ -21,8 +21,8 @@ const ImportForm = observer(function ({ model }: { model: MsaViewModel }) {
21
21
  <div style={{ width: '50%' }}>
22
22
  {error ? <ErrorMessage error={error} /> : null}
23
23
  <Typography>
24
- Open an MSA file (stockholm or clustal format) and/or a tree file
25
- (newick format).
24
+ Open an MSA file (FASTA, Stockholm, Clustal, A3M or EMF format) and/or
25
+ a tree file (Newick format).
26
26
  </Typography>
27
27
  <Typography color="error">
28
28
  Note: you can open up just an MSA or just a tree, both are not
@@ -9,7 +9,7 @@ import { load } from './util.ts'
9
9
  import type { MsaViewModel } from '../../model.ts'
10
10
 
11
11
  const BASE = 'https://jbrowse.org/genomes/multiple_sequence_alignments'
12
- const TREES = 'https://jbrowse.org/genomes/newicktrees'
12
+ const TREES = 'https://jbrowse.org/genomes/newick_trees'
13
13
 
14
14
  // each example either points at remote files (msa/tree/gff urls) or carries its
15
15
  // data inline, which is what the two bundled small ones do
@@ -35,11 +35,7 @@ const examples: Example[] = [
35
35
  inline: { msa: smallMSAOnly },
36
36
  },
37
37
  {
38
- label: 'PFAM SARS-CoV2 multi-stockholm',
39
- msa: `${BASE}/pfam-cov2.stock`,
40
- },
41
- {
42
- label: 'PFAM SARS-CoV2 multi-stockholm w/ domains loaded',
38
+ label: 'PFAM SARS-CoV2 multi-stockholm with domains',
43
39
  msa: `${BASE}/pfam-cov2.stock`,
44
40
  gff: `${BASE}/pfam-cov2-domains.gff`,
45
41
  },
@@ -16,5 +16,8 @@ export function load(
16
16
  }
17
17
  if (gffFile) {
18
18
  model.setGFFFilehandle(gffFile)
19
+ // asked for by hand, so drawn -- even if the reader hid the overlay
20
+ // earlier in this view
21
+ model.setShowDomains(true)
19
22
  }
20
23
  }
@@ -1,10 +1,10 @@
1
1
  import React from 'react'
2
2
 
3
3
  import BaseTooltip from '@jbrowse/core/ui/BaseTooltip'
4
+ import { useTheme } from '@mui/material'
4
5
  import { observer } from 'mobx-react'
5
6
 
6
7
  import { useCanvasAutorun } from '../../useCanvasAutorun.ts'
7
- import { useColorContrast } from '../../useColorContrast.ts'
8
8
  import ColumnStats from './ColumnStats.tsx'
9
9
  import { drawMsaRaster, rasterSupported } from './msaRaster.ts'
10
10
  import { renderBoxFeatureCanvasBlock } from './renderBoxFeatureCanvasBlock.ts'
@@ -22,8 +22,8 @@ const MSACanvasBlock = observer(function ({
22
22
  offsetX: number
23
23
  offsetY: number
24
24
  }) {
25
- const { colorScheme, blockSize, highResScaleFactor } = model
26
- const { theme, contrastScheme } = useColorContrast(colorScheme)
25
+ const { blockSize, highResScaleFactor } = model
26
+ const theme = useTheme()
27
27
  const { tooltipPoint, onMouseMove, onClick, onMouseLeave } = useMsaBlockMouse(
28
28
  {
29
29
  model,
@@ -56,14 +56,13 @@ const MSACanvasBlock = observer(function ({
56
56
  theme,
57
57
  offsetX,
58
58
  offsetY,
59
- contrastScheme,
60
59
  model,
61
60
  rasterTiles,
62
61
  })
63
62
  },
64
63
  width: canvasSize,
65
64
  height: canvasSize,
66
- deps: [model, offsetX, offsetY, theme, contrastScheme],
65
+ deps: [model, offsetX, offsetY, theme],
67
66
  })
68
67
 
69
68
  const { hoveredInsertion, mouseOverDomains, showColumnStats } = model