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.
- package/README.md +6 -5
- package/bundle/index.js +100 -101
- package/bundle/index.js.map +4 -4
- package/dist/components/Loading.js +2 -2
- package/dist/components/Loading.js.map +1 -1
- package/dist/components/MSAViewer.d.ts +10 -2
- package/dist/components/MSAViewer.js +90 -2
- package/dist/components/MSAViewer.js.map +1 -1
- package/dist/components/ResizeHandles.d.ts +3 -3
- package/dist/components/ResizeHandles.js +28 -21
- package/dist/components/ResizeHandles.js.map +1 -1
- package/dist/components/dialogs/{InterProScanDialog.d.ts → AnnotationFileDialog.d.ts} +2 -2
- package/dist/components/dialogs/AnnotationFileDialog.js +92 -0
- package/dist/components/dialogs/AnnotationFileDialog.js.map +1 -0
- package/dist/components/dialogs/FeatureDialog.js +13 -9
- package/dist/components/dialogs/FeatureDialog.js.map +1 -1
- package/dist/components/header/Header.js +4 -0
- package/dist/components/header/Header.js.map +1 -1
- package/dist/components/header/HeaderStatusArea.js +0 -2
- package/dist/components/header/HeaderStatusArea.js.map +1 -1
- package/dist/components/header/LoadWarnings.d.ts +6 -0
- package/dist/components/header/LoadWarnings.js +36 -0
- package/dist/components/header/LoadWarnings.js.map +1 -0
- package/dist/components/header/MultiAlignmentSelector.js +0 -0
- package/dist/components/header/MultiAlignmentSelector.js.map +1 -1
- package/dist/components/header/TreeSettingsMenu.js +9 -1
- package/dist/components/header/TreeSettingsMenu.js.map +1 -1
- package/dist/components/header/UnshareableDataWarning.d.ts +6 -0
- package/dist/components/header/UnshareableDataWarning.js +42 -0
- package/dist/components/header/UnshareableDataWarning.js.map +1 -0
- package/dist/components/header/getAnnotationsMenu.js +14 -14
- package/dist/components/header/getAnnotationsMenu.js.map +1 -1
- package/dist/components/import/ImportForm.js +1 -1
- package/dist/components/import/ImportForm.js.map +1 -1
- package/dist/components/import/ImportFormExamples.js +2 -6
- package/dist/components/import/ImportFormExamples.js.map +1 -1
- package/dist/components/import/util.js +3 -0
- package/dist/components/import/util.js.map +1 -1
- package/dist/components/msa/MSACanvasBlock.js +4 -5
- package/dist/components/msa/MSACanvasBlock.js.map +1 -1
- package/dist/components/msa/msaRaster.d.ts +13 -3
- package/dist/components/msa/msaRaster.js +173 -61
- package/dist/components/msa/msaRaster.js.map +1 -1
- package/dist/components/msa/renderHighlights.d.ts +39 -1
- package/dist/components/msa/renderHighlights.js +71 -6
- package/dist/components/msa/renderHighlights.js.map +1 -1
- package/dist/components/msa/renderMSABlock.d.ts +1 -2
- package/dist/components/msa/renderMSABlock.js +24 -28
- package/dist/components/msa/renderMSABlock.js.map +1 -1
- package/dist/components/msa/renderMSAMouseover.js +9 -30
- package/dist/components/msa/renderMSAMouseover.js.map +1 -1
- package/dist/components/renderCtx.d.ts +1 -1
- package/dist/components/tracks/TrackBlocks.js +10 -11
- package/dist/components/tracks/TrackBlocks.js.map +1 -1
- package/dist/components/tracks/drawTracks.d.ts +26 -8
- package/dist/components/tracks/drawTracks.js +123 -13
- package/dist/components/tracks/drawTracks.js.map +1 -1
- package/dist/components/tree/TreeCanvas.js +2 -3
- package/dist/components/tree/TreeCanvas.js.map +1 -1
- package/dist/components/tree/TreeCanvasBlock.js +4 -2
- package/dist/components/tree/TreeCanvasBlock.js.map +1 -1
- package/dist/components/tree/TreeRuler.js +13 -3
- package/dist/components/tree/TreeRuler.js.map +1 -1
- package/dist/components/tree/renderTreeCanvas.d.ts +0 -1
- package/dist/components/tree/renderTreeCanvas.js +70 -30
- package/dist/components/tree/renderTreeCanvas.js.map +1 -1
- package/dist/components/tree/scaleBar.d.ts +10 -0
- package/dist/components/tree/scaleBar.js +24 -0
- package/dist/components/tree/scaleBar.js.map +1 -0
- package/dist/constants.d.ts +3 -1
- package/dist/constants.js +18 -1
- package/dist/constants.js.map +1 -1
- package/dist/fetchUtils.d.ts +7 -7
- package/dist/fetchUtils.js +4 -32
- package/dist/fetchUtils.js.map +1 -1
- package/dist/hierarchy.d.ts +2 -1
- package/dist/hierarchy.js +16 -10
- package/dist/hierarchy.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +8 -2
- package/dist/index.js.map +1 -1
- package/dist/model/DataModel.js +5 -2
- package/dist/model/DataModel.js.map +1 -1
- package/dist/model.d.ts +454 -177
- package/dist/model.js +1180 -511
- package/dist/model.js.map +1 -1
- package/dist/renderToStaticMarkup.d.ts +11 -0
- package/dist/renderToStaticMarkup.js +30 -7
- package/dist/renderToStaticMarkup.js.map +1 -1
- package/dist/renderToSvg.js +25 -19
- package/dist/renderToSvg.js.map +1 -1
- package/dist/rowCoordinateCalculations.d.ts +25 -25
- package/dist/rowCoordinateCalculations.js +12 -36
- package/dist/rowCoordinateCalculations.js.map +1 -1
- package/dist/seqPosToGlobalCol.js +3 -1
- package/dist/seqPosToGlobalCol.js.map +1 -1
- package/dist/svgTestUtil.d.ts +144 -82
- package/dist/types.d.ts +97 -3
- package/dist/util.d.ts +11 -4
- package/dist/util.js +30 -10
- package/dist/util.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/wuss.d.ts +14 -0
- package/dist/wuss.js +55 -0
- package/dist/wuss.js.map +1 -0
- package/package.json +10 -8
- package/src/components/Loading.tsx +12 -2
- package/src/components/MSAViewer.tsx +106 -2
- package/src/components/ResizeHandles.tsx +31 -31
- package/src/components/dialogs/AnnotationFileDialog.tsx +162 -0
- package/src/components/dialogs/FeatureDialog.tsx +13 -9
- package/src/components/header/Header.tsx +4 -0
- package/src/components/header/HeaderStatusArea.tsx +0 -5
- package/src/components/header/LoadWarnings.tsx +49 -0
- package/src/components/header/MultiAlignmentSelector.tsx +0 -0
- package/src/components/header/TreeSettingsMenu.tsx +9 -1
- package/src/components/header/UnshareableDataWarning.tsx +58 -0
- package/src/components/header/getAnnotationsMenu.ts +16 -17
- package/src/components/import/ImportForm.tsx +2 -2
- package/src/components/import/ImportFormExamples.tsx +2 -6
- package/src/components/import/util.ts +3 -0
- package/src/components/msa/MSACanvasBlock.tsx +4 -5
- package/src/components/msa/msaRaster.ts +216 -59
- package/src/components/msa/renderHighlights.ts +103 -5
- package/src/components/msa/renderMSABlock.ts +25 -36
- package/src/components/msa/renderMSAMouseover.ts +9 -39
- package/src/components/renderCtx.ts +1 -0
- package/src/components/tracks/TrackBlocks.tsx +10 -13
- package/src/components/tracks/drawTracks.ts +165 -17
- package/src/components/tree/TreeCanvas.tsx +2 -3
- package/src/components/tree/TreeCanvasBlock.tsx +4 -2
- package/src/components/tree/TreeRuler.tsx +38 -3
- package/src/components/tree/renderTreeCanvas.ts +79 -35
- package/src/components/tree/scaleBar.ts +23 -0
- package/src/constants.ts +20 -1
- package/src/fetchUtils.ts +7 -40
- package/src/hierarchy.ts +20 -11
- package/src/index.ts +18 -2
- package/src/model/DataModel.ts +6 -2
- package/src/model.ts +1352 -616
- package/src/rawImports.d.ts +9 -0
- package/src/renderToStaticMarkup.ts +34 -7
- package/src/renderToSvg.tsx +33 -27
- package/src/rowCoordinateCalculations.ts +15 -59
- package/src/seqPosToGlobalCol.ts +3 -1
- package/src/types.ts +131 -4
- package/src/util.ts +31 -14
- package/src/version.ts +1 -1
- package/src/wuss.ts +62 -0
- package/dist/components/dialogs/InterProScanDialog.js +0 -137
- package/dist/components/dialogs/InterProScanDialog.js.map +0 -1
- package/dist/components/dialogs/InterProScanFileDialog.d.ts +0 -7
- package/dist/components/dialogs/InterProScanFileDialog.js +0 -64
- package/dist/components/dialogs/InterProScanFileDialog.js.map +0 -1
- package/dist/launchInterProScan.d.ts +0 -13
- package/dist/launchInterProScan.js +0 -110
- package/dist/launchInterProScan.js.map +0 -1
- package/dist/useColorContrast.d.ts +0 -10
- package/dist/useColorContrast.js +0 -13
- package/dist/useColorContrast.js.map +0 -1
- package/src/clustalX.test.ts +0 -74
- package/src/colSpace.test.ts +0 -67
- package/src/collapseLogic.test.ts +0 -115
- package/src/colorSchemes.docs.test.ts +0 -57
- package/src/columnCounts.test.ts +0 -73
- package/src/columnStats.test.ts +0 -94
- package/src/columnTracks.test.ts +0 -97
- package/src/components/dialogs/InterProScanDialog.tsx +0 -214
- package/src/components/dialogs/InterProScanFileDialog.tsx +0 -138
- package/src/components/minimap/minimapLayout.test.ts +0 -71
- package/src/components/msa/msaRaster.test.ts +0 -145
- package/src/components/msa/renderBoxFeatureCanvasBlock.test.ts +0 -51
- package/src/components/tree/renderTreeCanvas.test.ts +0 -296
- package/src/dataInitialized.test.ts +0 -42
- package/src/domainBands.test.ts +0 -122
- package/src/domainLetterColor.test.ts +0 -80
- package/src/exportFileName.test.ts +0 -40
- package/src/fetchUtils.test.ts +0 -83
- package/src/flatToTree.test.ts +0 -51
- package/src/hierarchy.test.ts +0 -228
- package/src/highlights.test.ts +0 -70
- package/src/impgRender.test.tsx +0 -68
- package/src/largeTree.test.ts +0 -67
- package/src/launchInterProScan.ts +0 -167
- package/src/loadErrorDisplay.test.tsx +0 -102
- package/src/modelFilehandleLoaders.test.ts +0 -137
- package/src/modelReset.test.ts +0 -120
- package/src/neighborJoining.test.ts +0 -164
- package/src/parseAsn1.test.ts +0 -36
- package/src/propertyConservation.test.ts +0 -36
- package/src/renderColumnTracksSvg.test.tsx +0 -108
- package/src/renderDomainsSvg.test.tsx +0 -118
- package/src/renderHighlightsSvg.test.tsx +0 -85
- package/src/renderMinimapSvg.test.tsx +0 -62
- package/src/renderRasterSvg.test.tsx +0 -144
- package/src/renderSequenceLogoSvg.test.tsx +0 -165
- package/src/renderToSvg.test.tsx +0 -65
- package/src/reset.test.ts +0 -61
- package/src/rowCoordinateCalculations.test.ts +0 -211
- package/src/rowSpace.test.ts +0 -46
- package/src/segmentOverlay.test.ts +0 -70
- package/src/seqPosToGlobalCol.test.ts +0 -59
- package/src/sequenceLogo.test.ts +0 -88
- package/src/setFontSize.test.ts +0 -33
- package/src/stripDefault.test.ts +0 -48
- package/src/stripDefaultSnapshot.test.ts +0 -87
- package/src/treeMetadata.test.ts +0 -39
- package/src/useCanvasAutorun.test.tsx +0 -98
- package/src/useColorContrast.ts +0 -18
- package/src/viewChrome.test.tsx +0 -134
- 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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
75
|
+
checked={!model.turnedOffFeatures.get(accession)}
|
|
72
76
|
onChange={() => {
|
|
73
77
|
model.setFilter(
|
|
74
78
|
accession,
|
|
75
|
-
|
|
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
|
|
Binary file
|
|
@@ -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
|
|
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
|
|
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
|
|
13
|
-
() => import('../dialogs/
|
|
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
|
|
23
|
+
label: 'Open annotation file...',
|
|
25
24
|
icon: FolderOpen,
|
|
26
25
|
onClick: () => {
|
|
27
26
|
model.queueDialog(handleClose => [
|
|
28
|
-
|
|
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: '
|
|
38
|
-
icon:
|
|
42
|
+
label: 'How to get a domain file...',
|
|
43
|
+
icon: MenuBook,
|
|
39
44
|
onClick: () => {
|
|
40
|
-
|
|
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 (
|
|
25
|
-
(
|
|
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/
|
|
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
|
},
|
|
@@ -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 {
|
|
26
|
-
const
|
|
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
|
|
65
|
+
deps: [model, offsetX, offsetY, theme],
|
|
67
66
|
})
|
|
68
67
|
|
|
69
68
|
const { hoveredInsertion, mouseOverDomains, showColumnStats } = model
|