jbrowse-plugin-msaview 1.0.18 → 2.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/CHANGELOG.md +6 -4
- package/README.md +6 -4
- package/dist/AddHighlightModel/GenomeMouseoverHighlight.d.ts +6 -0
- package/dist/AddHighlightModel/GenomeMouseoverHighlight.js +30 -0
- package/dist/AddHighlightModel/GenomeMouseoverHighlight.js.map +1 -0
- package/dist/AddHighlightModel/HighlightComponents.d.ts +7 -0
- package/dist/AddHighlightModel/HighlightComponents.js +12 -0
- package/dist/AddHighlightModel/HighlightComponents.js.map +1 -0
- package/dist/AddHighlightModel/MsaToGenomeHighlight.d.ts +7 -0
- package/dist/AddHighlightModel/MsaToGenomeHighlight.js +26 -0
- package/dist/AddHighlightModel/MsaToGenomeHighlight.js.map +1 -0
- package/dist/AddHighlightModel/index.d.ts +2 -0
- package/dist/AddHighlightModel/index.js +14 -0
- package/dist/AddHighlightModel/index.js.map +1 -0
- package/dist/AddHighlightModel/util.d.ts +9 -0
- package/dist/AddHighlightModel/util.js +14 -0
- package/dist/AddHighlightModel/util.js.map +1 -0
- package/dist/LaunchMsaView/components/LaunchMsaViewDialog.d.ts +7 -0
- package/dist/LaunchMsaView/components/LaunchMsaViewDialog.js +20 -0
- package/dist/LaunchMsaView/components/LaunchMsaViewDialog.js.map +1 -0
- package/dist/LaunchMsaView/components/NewNCBIBlastQuery/NcbiBlastPanel.d.ts +8 -0
- package/dist/LaunchMsaView/components/NewNCBIBlastQuery/NcbiBlastPanel.js +76 -0
- package/dist/LaunchMsaView/components/NewNCBIBlastQuery/NcbiBlastPanel.js.map +1 -0
- package/dist/LaunchMsaView/components/NewNCBIBlastQuery/calculateProteinSequence.d.ts +26 -0
- package/dist/LaunchMsaView/components/NewNCBIBlastQuery/calculateProteinSequence.js +48 -0
- package/dist/LaunchMsaView/components/NewNCBIBlastQuery/calculateProteinSequence.js.map +1 -0
- package/dist/LaunchMsaView/components/NewNCBIBlastQuery/index.d.ts +2 -0
- package/dist/LaunchMsaView/components/NewNCBIBlastQuery/index.js +3 -0
- package/dist/LaunchMsaView/components/NewNCBIBlastQuery/index.js.map +1 -0
- package/dist/LaunchMsaView/components/NewNCBIBlastQuery/ncbiBlastLaunchView.d.ts +8 -0
- package/dist/LaunchMsaView/components/NewNCBIBlastQuery/ncbiBlastLaunchView.js +17 -0
- package/dist/LaunchMsaView/components/NewNCBIBlastQuery/ncbiBlastLaunchView.js.map +1 -0
- package/dist/LaunchMsaView/components/NewNCBIBlastQuery/useFeatureSequence.d.ts +20 -0
- package/dist/LaunchMsaView/components/NewNCBIBlastQuery/useFeatureSequence.js +64 -0
- package/dist/LaunchMsaView/components/NewNCBIBlastQuery/useFeatureSequence.js.map +1 -0
- package/dist/LaunchMsaView/components/NewNCBIBlastQuery/util.d.ts +5 -0
- package/dist/LaunchMsaView/components/NewNCBIBlastQuery/util.js +7 -0
- package/dist/LaunchMsaView/components/NewNCBIBlastQuery/util.js.map +1 -0
- package/dist/LaunchMsaView/components/PreLoadedMSA/PreLoadedMSADataPanel.d.ts +8 -0
- package/dist/LaunchMsaView/components/PreLoadedMSA/PreLoadedMSADataPanel.js +90 -0
- package/dist/LaunchMsaView/components/PreLoadedMSA/PreLoadedMSADataPanel.js.map +1 -0
- package/dist/LaunchMsaView/components/PreLoadedMSA/fetchGeneList.d.ts +1 -0
- package/dist/LaunchMsaView/components/PreLoadedMSA/fetchGeneList.js +12 -0
- package/dist/LaunchMsaView/components/PreLoadedMSA/fetchGeneList.js.map +1 -0
- package/dist/LaunchMsaView/components/PreLoadedMSA/preCalculatedLaunchView.d.ts +9 -0
- package/dist/LaunchMsaView/components/PreLoadedMSA/preCalculatedLaunchView.js +36 -0
- package/dist/LaunchMsaView/components/PreLoadedMSA/preCalculatedLaunchView.js.map +1 -0
- package/dist/LaunchMsaView/components/TabUtils.d.ts +12 -0
- package/dist/LaunchMsaView/components/TabUtils.js +13 -0
- package/dist/LaunchMsaView/components/TabUtils.js.map +1 -0
- package/dist/LaunchMsaView/index.d.ts +2 -0
- package/dist/LaunchMsaView/index.js +50 -0
- package/dist/LaunchMsaView/index.js.map +1 -0
- package/dist/LaunchMsaView/util.d.ts +5 -0
- package/dist/LaunchMsaView/util.js +25 -0
- package/dist/LaunchMsaView/util.js.map +1 -0
- package/dist/MsaViewPanel/components/LoadingBLAST.d.ts +6 -0
- package/dist/MsaViewPanel/components/LoadingBLAST.js +36 -0
- package/dist/MsaViewPanel/components/LoadingBLAST.js.map +1 -0
- package/dist/MsaViewPanel/components/MsaViewPanel.d.ts +6 -0
- package/dist/MsaViewPanel/components/MsaViewPanel.js +10 -0
- package/dist/MsaViewPanel/components/MsaViewPanel.js.map +1 -0
- package/dist/MsaViewPanel/components/RIDLink.d.ts +5 -0
- package/dist/MsaViewPanel/components/RIDLink.js +17 -0
- package/dist/MsaViewPanel/components/RIDLink.js.map +1 -0
- package/dist/MsaViewPanel/doLaunchBlast.d.ts +7 -0
- package/dist/MsaViewPanel/doLaunchBlast.js +27 -0
- package/dist/MsaViewPanel/doLaunchBlast.js.map +1 -0
- package/dist/MsaViewPanel/genomeToMSA.d.ts +4 -0
- package/dist/MsaViewPanel/genomeToMSA.js +18 -0
- package/dist/MsaViewPanel/genomeToMSA.js.map +1 -0
- package/dist/MsaViewPanel/index.d.ts +2 -0
- package/dist/MsaViewPanel/index.js +16 -0
- package/dist/MsaViewPanel/index.js.map +1 -0
- package/dist/MsaViewPanel/model.d.ts +596 -0
- package/dist/MsaViewPanel/model.js +235 -0
- package/dist/MsaViewPanel/model.js.map +1 -0
- package/dist/MsaViewPanel/msaCoordToGenomeCoord.d.ts +9 -0
- package/dist/MsaViewPanel/msaCoordToGenomeCoord.js +20 -0
- package/dist/MsaViewPanel/msaCoordToGenomeCoord.js.map +1 -0
- package/dist/MsaViewPanel/util.d.ts +8 -0
- package/dist/MsaViewPanel/util.js +7 -0
- package/dist/MsaViewPanel/util.js.map +1 -0
- package/dist/OpenInNewIcon.d.ts +3 -0
- package/dist/OpenInNewIcon.js +8 -0
- package/dist/OpenInNewIcon.js.map +1 -0
- package/dist/TextField2.d.ts +4 -0
- package/dist/TextField2.js +8 -0
- package/dist/TextField2.js.map +1 -0
- package/dist/index.d.ts +8 -8
- package/dist/index.js +32 -7
- package/dist/index.js.map +1 -0
- package/dist/jbrowse-plugin-msaview.umd.production.min.js +109 -1
- package/dist/jbrowse-plugin-msaview.umd.production.min.js.map +7 -1
- package/dist/utils/fetch.d.ts +4 -0
- package/dist/utils/fetch.js +19 -0
- package/dist/utils/fetch.js.map +1 -0
- package/dist/utils/msa.d.ts +8 -0
- package/dist/utils/msa.js +75 -0
- package/dist/utils/msa.js.map +1 -0
- package/dist/utils/ncbiBlast.d.ts +20 -0
- package/dist/utils/ncbiBlast.js +66 -0
- package/dist/utils/ncbiBlast.js.map +1 -0
- package/package.json +51 -40
- package/src/AddHighlightModel/GenomeMouseoverHighlight.tsx +44 -0
- package/src/AddHighlightModel/HighlightComponents.tsx +24 -0
- package/src/AddHighlightModel/MsaToGenomeHighlight.tsx +50 -0
- package/src/AddHighlightModel/index.tsx +27 -0
- package/src/AddHighlightModel/util.ts +14 -0
- package/src/LaunchMsaView/components/LaunchMsaViewDialog.tsx +52 -0
- package/src/LaunchMsaView/components/NewNCBIBlastQuery/NcbiBlastPanel.tsx +160 -0
- package/src/LaunchMsaView/components/NewNCBIBlastQuery/calculateProteinSequence.ts +92 -0
- package/src/LaunchMsaView/components/NewNCBIBlastQuery/index.tsx +2 -0
- package/src/LaunchMsaView/components/NewNCBIBlastQuery/ncbiBlastLaunchView.ts +28 -0
- package/src/LaunchMsaView/components/NewNCBIBlastQuery/useFeatureSequence.ts +85 -0
- package/src/LaunchMsaView/components/NewNCBIBlastQuery/util.ts +7 -0
- package/src/LaunchMsaView/components/PreLoadedMSA/PreLoadedMSADataPanel.tsx +149 -0
- package/src/LaunchMsaView/components/PreLoadedMSA/fetchGeneList.ts +13 -0
- package/src/LaunchMsaView/components/PreLoadedMSA/preCalculatedLaunchView.ts +54 -0
- package/src/LaunchMsaView/components/TabUtils.tsx +30 -0
- package/src/LaunchMsaView/index.ts +61 -0
- package/src/LaunchMsaView/util.ts +30 -0
- package/src/MsaViewPanel/components/LoadingBLAST.tsx +59 -0
- package/src/MsaViewPanel/components/MsaViewPanel.tsx +22 -0
- package/src/MsaViewPanel/components/RIDLink.tsx +18 -0
- package/src/MsaViewPanel/doLaunchBlast.ts +38 -0
- package/src/MsaViewPanel/genomeToMSA.ts +21 -0
- package/src/MsaViewPanel/index.ts +19 -0
- package/src/MsaViewPanel/model.ts +290 -0
- package/src/MsaViewPanel/msaCoordToGenomeCoord.ts +29 -0
- package/src/MsaViewPanel/util.ts +13 -0
- package/src/OpenInNewIcon.tsx +11 -0
- package/src/TextField2.tsx +12 -0
- package/src/index.ts +12 -14
- package/src/utils/fetch.ts +25 -0
- package/src/utils/msa.ts +111 -0
- package/src/utils/ncbiBlast.ts +105 -0
- package/dist/config.json +0 -8
- package/dist/jbrowse-plugin-msaview.cjs.development.js +0 -168
- package/dist/jbrowse-plugin-msaview.cjs.development.js.map +0 -1
- package/dist/jbrowse-plugin-msaview.cjs.production.min.js +0 -2
- package/dist/jbrowse-plugin-msaview.cjs.production.min.js.map +0 -1
- package/dist/jbrowse-plugin-msaview.esm.js +0 -163
- package/dist/jbrowse-plugin-msaview.esm.js.map +0 -1
- package/dist/jbrowse-plugin-msaview.umd.development.js +0 -8659
- package/dist/jbrowse-plugin-msaview.umd.development.js.map +0 -1
- package/src/index.test.ts +0 -3
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { Feature } from '@jbrowse/core/util'
|
|
2
|
+
|
|
3
|
+
export function getTranscriptFeatures(feature: Feature) {
|
|
4
|
+
// check if we are looking at a 'two-level' or 'three-level' feature by
|
|
5
|
+
// finding exon/CDS subfeatures. we want to select from transcript names
|
|
6
|
+
const subfeatures = feature.get('subfeatures') ?? []
|
|
7
|
+
return subfeatures.some(f => f.get('type') === 'CDS')
|
|
8
|
+
? [feature]
|
|
9
|
+
: // filter out non-coding by finding subfeatures with CDS subfeatures
|
|
10
|
+
subfeatures.filter(f =>
|
|
11
|
+
f.get('subfeatures')?.some(f => f.get('type') === 'CDS'),
|
|
12
|
+
)
|
|
13
|
+
}
|
|
14
|
+
export function getId(val?: Feature) {
|
|
15
|
+
return val === undefined ? '' : val.get('name') || val.get('id')
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function getTranscriptDisplayName(val?: Feature) {
|
|
19
|
+
return val === undefined
|
|
20
|
+
? ''
|
|
21
|
+
: [val.get('name'), val.get('id')].filter(f => !!f).join(' ')
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function getGeneDisplayName(val?: Feature) {
|
|
25
|
+
return val === undefined
|
|
26
|
+
? ''
|
|
27
|
+
: [val.get('gene_name') || val.get('name'), val.get('id')]
|
|
28
|
+
.filter(f => !!f)
|
|
29
|
+
.join(' ')
|
|
30
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { JBrowsePluginMsaViewModel } from '../model'
|
|
3
|
+
import { Typography } from '@mui/material'
|
|
4
|
+
import { observer } from 'mobx-react'
|
|
5
|
+
import { makeStyles } from 'tss-react/mui'
|
|
6
|
+
import { ErrorMessage, LoadingEllipses } from '@jbrowse/core/ui'
|
|
7
|
+
|
|
8
|
+
// locals
|
|
9
|
+
import RIDLink from './RIDLink'
|
|
10
|
+
|
|
11
|
+
const useStyles = makeStyles()(theme => ({
|
|
12
|
+
margin: {
|
|
13
|
+
padding: 20,
|
|
14
|
+
},
|
|
15
|
+
loading: {
|
|
16
|
+
background: theme.palette.background.paper,
|
|
17
|
+
},
|
|
18
|
+
}))
|
|
19
|
+
|
|
20
|
+
function RIDError({ rid, error }: { rid?: string; error: unknown }) {
|
|
21
|
+
return (
|
|
22
|
+
<div>
|
|
23
|
+
{rid ? <RIDLink rid={rid} /> : null}
|
|
24
|
+
<ErrorMessage error={error} />
|
|
25
|
+
</div>
|
|
26
|
+
)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function RIDProgress({ rid, progress }: { rid: string; progress: string }) {
|
|
30
|
+
const { classes } = useStyles()
|
|
31
|
+
return (
|
|
32
|
+
<div className={classes.loading}>
|
|
33
|
+
{rid ? <RIDLink rid={rid} /> : null}
|
|
34
|
+
<Typography>{progress}</Typography>
|
|
35
|
+
</div>
|
|
36
|
+
)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const LoadingBLAST = observer(function LoadingBLAST2({
|
|
40
|
+
model,
|
|
41
|
+
}: {
|
|
42
|
+
model: JBrowsePluginMsaViewModel
|
|
43
|
+
}) {
|
|
44
|
+
const { progress, rid, error, processing } = model
|
|
45
|
+
const { classes } = useStyles()
|
|
46
|
+
return (
|
|
47
|
+
<div className={classes.margin}>
|
|
48
|
+
<LoadingEllipses message="Running NCBI BLAST" variant="h5" />
|
|
49
|
+
{error ? (
|
|
50
|
+
<RIDError rid={rid} error={error} />
|
|
51
|
+
) : rid ? (
|
|
52
|
+
<RIDProgress rid={rid} progress={progress} />
|
|
53
|
+
) : null}
|
|
54
|
+
<Typography>{processing || 'Initializing BLAST query'}</Typography>
|
|
55
|
+
</div>
|
|
56
|
+
)
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
export default LoadingBLAST
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { observer } from 'mobx-react'
|
|
3
|
+
import { MSAView } from 'react-msaview'
|
|
4
|
+
|
|
5
|
+
// locals
|
|
6
|
+
import { JBrowsePluginMsaViewModel } from '../model'
|
|
7
|
+
import LoadingBLAST from './LoadingBLAST'
|
|
8
|
+
|
|
9
|
+
const MsaViewPanel = observer(function MsaViewPanel2({
|
|
10
|
+
model,
|
|
11
|
+
}: {
|
|
12
|
+
model: JBrowsePluginMsaViewModel
|
|
13
|
+
}) {
|
|
14
|
+
const { blastParams } = model
|
|
15
|
+
return (
|
|
16
|
+
<div>
|
|
17
|
+
{blastParams ? <LoadingBLAST model={model} /> : <MSAView model={model} />}
|
|
18
|
+
</div>
|
|
19
|
+
)
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
export default MsaViewPanel
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { Link, Typography } from '@mui/material'
|
|
3
|
+
// locals
|
|
4
|
+
import OpenInNewIcon from '../../OpenInNewIcon'
|
|
5
|
+
import { BLAST_URL } from '../../utils/ncbiBlast'
|
|
6
|
+
|
|
7
|
+
function RIDLink({ rid }: { rid: string }) {
|
|
8
|
+
return (
|
|
9
|
+
<Typography>
|
|
10
|
+
RID {rid}{' '}
|
|
11
|
+
<Link target="_black" href={`${BLAST_URL}?CMD=Get&RID=${rid}`}>
|
|
12
|
+
(see status at NCBI <OpenInNewIcon />)
|
|
13
|
+
</Link>
|
|
14
|
+
</Typography>
|
|
15
|
+
)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export default RIDLink
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { launchMSA } from '../utils/msa'
|
|
2
|
+
import { queryBlast } from '../utils/ncbiBlast'
|
|
3
|
+
import {
|
|
4
|
+
makeId,
|
|
5
|
+
strip,
|
|
6
|
+
} from '../LaunchMsaView/components/NewNCBIBlastQuery/util'
|
|
7
|
+
import { JBrowsePluginMsaViewModel } from './model'
|
|
8
|
+
|
|
9
|
+
export async function doLaunchBlast({
|
|
10
|
+
self,
|
|
11
|
+
}: {
|
|
12
|
+
self: JBrowsePluginMsaViewModel
|
|
13
|
+
}) {
|
|
14
|
+
const { blastDatabase, blastProgram, msaAlgorithm, proteinSequence } =
|
|
15
|
+
self.blastParams!
|
|
16
|
+
const query = proteinSequence.replaceAll('*', '').replaceAll('&', '')
|
|
17
|
+
const { hits } = await queryBlast({
|
|
18
|
+
query,
|
|
19
|
+
blastDatabase,
|
|
20
|
+
blastProgram,
|
|
21
|
+
onProgress: arg => self.setProgress(arg),
|
|
22
|
+
onRid: rid => self.setRid(rid),
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
const sequence = [
|
|
26
|
+
`>QUERY\n${query}`,
|
|
27
|
+
...hits
|
|
28
|
+
.map(h => [makeId(h.description[0]), strip(h.hsps[0].hseq)] as const)
|
|
29
|
+
.map(([id, seq]) => `>${id}\n${seq}`),
|
|
30
|
+
].join('\n')
|
|
31
|
+
|
|
32
|
+
const data = await launchMSA({
|
|
33
|
+
algorithm: msaAlgorithm,
|
|
34
|
+
sequence,
|
|
35
|
+
onProgress: arg => self.setProgress(arg),
|
|
36
|
+
})
|
|
37
|
+
return data
|
|
38
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { getSession } from '@jbrowse/core/util'
|
|
2
|
+
import { checkHovered } from './util'
|
|
3
|
+
import { JBrowsePluginMsaViewModel } from './model'
|
|
4
|
+
|
|
5
|
+
export function genomeToMSA({ model }: { model: JBrowsePluginMsaViewModel }) {
|
|
6
|
+
const { hovered } = getSession(model)
|
|
7
|
+
const { transcriptToMsaMap, connectedView } = model
|
|
8
|
+
if (
|
|
9
|
+
connectedView?.initialized &&
|
|
10
|
+
transcriptToMsaMap &&
|
|
11
|
+
checkHovered(hovered)
|
|
12
|
+
) {
|
|
13
|
+
const { coord: hoverCoord } = hovered.hoverPosition
|
|
14
|
+
const { g2p } = transcriptToMsaMap
|
|
15
|
+
const ret = g2p[hoverCoord]
|
|
16
|
+
if (ret !== undefined) {
|
|
17
|
+
return model.seqCoordToRowSpecificGlobalCoord('QUERY', ret + 1)
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return undefined
|
|
21
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { lazy } from 'react'
|
|
2
|
+
import PluginManager from '@jbrowse/core/PluginManager'
|
|
3
|
+
import ViewType from '@jbrowse/core/pluggableElementTypes/ViewType'
|
|
4
|
+
|
|
5
|
+
// locals
|
|
6
|
+
import stateModelFactory from './model'
|
|
7
|
+
|
|
8
|
+
// lazies
|
|
9
|
+
const MsaViewPanel = lazy(() => import('./components/MsaViewPanel'))
|
|
10
|
+
|
|
11
|
+
export default function MsaViewF(pluginManager: PluginManager) {
|
|
12
|
+
pluginManager.addViewType(() => {
|
|
13
|
+
return new ViewType({
|
|
14
|
+
name: 'MsaView',
|
|
15
|
+
stateModel: stateModelFactory(),
|
|
16
|
+
ReactComponent: MsaViewPanel,
|
|
17
|
+
})
|
|
18
|
+
})
|
|
19
|
+
}
|
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
import { Instance, addDisposer, cast, types } from 'mobx-state-tree'
|
|
2
|
+
import { autorun } from 'mobx'
|
|
3
|
+
import { MSAModelF } from 'react-msaview'
|
|
4
|
+
import { Feature, getSession, notEmpty } from '@jbrowse/core/util'
|
|
5
|
+
import { LinearGenomeViewModel } from '@jbrowse/plugin-linear-genome-view'
|
|
6
|
+
import { BaseViewModel } from '@jbrowse/core/pluggableElementTypes'
|
|
7
|
+
|
|
8
|
+
// locals
|
|
9
|
+
import { doLaunchBlast } from './doLaunchBlast'
|
|
10
|
+
import { genomeToMSA } from './genomeToMSA'
|
|
11
|
+
import { msaCoordToGenomeCoord } from './msaCoordToGenomeCoord'
|
|
12
|
+
import { genomeToTranscriptSeqMapping } from 'g2p_mapper'
|
|
13
|
+
|
|
14
|
+
type LGV = LinearGenomeViewModel
|
|
15
|
+
|
|
16
|
+
type MaybeLGV = LGV | undefined
|
|
17
|
+
|
|
18
|
+
export interface IRegion {
|
|
19
|
+
refName: string
|
|
20
|
+
start: number
|
|
21
|
+
end: number
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface BlastParams {
|
|
25
|
+
blastDatabase: string
|
|
26
|
+
msaAlgorithm: string
|
|
27
|
+
blastProgram: string
|
|
28
|
+
selectedTranscript: Feature
|
|
29
|
+
proteinSequence: string
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* #stateModel MsaViewPlugin
|
|
34
|
+
* extends
|
|
35
|
+
* - MSAModel from https://github.com/GMOD/react-msaview
|
|
36
|
+
*/
|
|
37
|
+
export default function stateModelFactory() {
|
|
38
|
+
return types
|
|
39
|
+
.compose(
|
|
40
|
+
BaseViewModel,
|
|
41
|
+
MSAModelF(),
|
|
42
|
+
types.model('MsaView', {
|
|
43
|
+
/**
|
|
44
|
+
* #property
|
|
45
|
+
*/
|
|
46
|
+
connectedViewId: types.maybe(types.string),
|
|
47
|
+
/**
|
|
48
|
+
* #property
|
|
49
|
+
*/
|
|
50
|
+
connectedFeature: types.frozen(),
|
|
51
|
+
/**
|
|
52
|
+
* #property
|
|
53
|
+
*/
|
|
54
|
+
connectedHighlights: types.array(
|
|
55
|
+
types.model({
|
|
56
|
+
refName: types.string,
|
|
57
|
+
start: types.number,
|
|
58
|
+
end: types.number,
|
|
59
|
+
}),
|
|
60
|
+
),
|
|
61
|
+
/**
|
|
62
|
+
* #property
|
|
63
|
+
*/
|
|
64
|
+
blastParams: types.frozen<BlastParams | undefined>(),
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* #property
|
|
68
|
+
*/
|
|
69
|
+
zoomToBaseLevel: false,
|
|
70
|
+
}),
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
.volatile(() => ({
|
|
74
|
+
rid: undefined as string | undefined,
|
|
75
|
+
progress: '',
|
|
76
|
+
error: undefined as unknown,
|
|
77
|
+
}))
|
|
78
|
+
|
|
79
|
+
.views(self => ({
|
|
80
|
+
/**
|
|
81
|
+
* #method
|
|
82
|
+
*/
|
|
83
|
+
ungappedCoordMap(rowName: string, position: number) {
|
|
84
|
+
const row = self.rows.find(f => f[0] === rowName)
|
|
85
|
+
const seq = row?.[1]
|
|
86
|
+
if (seq && position < seq.length) {
|
|
87
|
+
let i = 0
|
|
88
|
+
let j = 0
|
|
89
|
+
for (; j < position; j++, i++) {
|
|
90
|
+
while (seq[i] === '-') {
|
|
91
|
+
i++
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return i
|
|
95
|
+
}
|
|
96
|
+
return undefined
|
|
97
|
+
},
|
|
98
|
+
}))
|
|
99
|
+
.views(self => ({
|
|
100
|
+
/**
|
|
101
|
+
* #getter
|
|
102
|
+
*/
|
|
103
|
+
get transcriptToMsaMap() {
|
|
104
|
+
console.log(self.connectedFeature)
|
|
105
|
+
return self.connectedFeature
|
|
106
|
+
? genomeToTranscriptSeqMapping(self.connectedFeature)
|
|
107
|
+
: undefined
|
|
108
|
+
},
|
|
109
|
+
}))
|
|
110
|
+
.views(self => ({
|
|
111
|
+
/**
|
|
112
|
+
* #getter
|
|
113
|
+
*/
|
|
114
|
+
get mouseCol2(): number | undefined {
|
|
115
|
+
return genomeToMSA({ model: self as JBrowsePluginMsaViewModel })
|
|
116
|
+
},
|
|
117
|
+
/**
|
|
118
|
+
* #getter
|
|
119
|
+
*/
|
|
120
|
+
get clickCol2() {
|
|
121
|
+
return undefined
|
|
122
|
+
},
|
|
123
|
+
}))
|
|
124
|
+
|
|
125
|
+
.views(self => ({
|
|
126
|
+
/**
|
|
127
|
+
* #getter
|
|
128
|
+
*/
|
|
129
|
+
get processing() {
|
|
130
|
+
return !!self.progress
|
|
131
|
+
},
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* #getter
|
|
135
|
+
*/
|
|
136
|
+
get connectedView() {
|
|
137
|
+
const { views } = getSession(self)
|
|
138
|
+
return views.find(f => f.id === self.connectedViewId) as MaybeLGV
|
|
139
|
+
},
|
|
140
|
+
}))
|
|
141
|
+
.actions(self => ({
|
|
142
|
+
/**
|
|
143
|
+
* #action
|
|
144
|
+
*/
|
|
145
|
+
setZoomToBaseLevel(arg: boolean) {
|
|
146
|
+
self.zoomToBaseLevel = arg
|
|
147
|
+
},
|
|
148
|
+
/**
|
|
149
|
+
* #action
|
|
150
|
+
*/
|
|
151
|
+
setError(e: unknown) {
|
|
152
|
+
self.error = e
|
|
153
|
+
},
|
|
154
|
+
/**
|
|
155
|
+
* #action
|
|
156
|
+
*/
|
|
157
|
+
setProgress(arg: string) {
|
|
158
|
+
self.progress = arg
|
|
159
|
+
},
|
|
160
|
+
/**
|
|
161
|
+
* #action
|
|
162
|
+
*/
|
|
163
|
+
setRid(arg: string) {
|
|
164
|
+
self.rid = arg
|
|
165
|
+
},
|
|
166
|
+
/**
|
|
167
|
+
* #action
|
|
168
|
+
*/
|
|
169
|
+
setConnectedHighlights(r: IRegion[]) {
|
|
170
|
+
self.connectedHighlights = cast(r)
|
|
171
|
+
},
|
|
172
|
+
/**
|
|
173
|
+
* #action
|
|
174
|
+
*/
|
|
175
|
+
addToConnectedHighlights(r: IRegion) {
|
|
176
|
+
self.connectedHighlights.push(r)
|
|
177
|
+
},
|
|
178
|
+
/**
|
|
179
|
+
* #action
|
|
180
|
+
*/
|
|
181
|
+
clearConnectedHighlights() {
|
|
182
|
+
self.connectedHighlights = cast([])
|
|
183
|
+
},
|
|
184
|
+
/**
|
|
185
|
+
* #action
|
|
186
|
+
*/
|
|
187
|
+
setBlastParams(args?: BlastParams) {
|
|
188
|
+
self.blastParams = args
|
|
189
|
+
},
|
|
190
|
+
}))
|
|
191
|
+
|
|
192
|
+
.views(self => ({
|
|
193
|
+
/**
|
|
194
|
+
* #method
|
|
195
|
+
* overrides base
|
|
196
|
+
*/
|
|
197
|
+
extraViewMenuItems() {
|
|
198
|
+
return [
|
|
199
|
+
{
|
|
200
|
+
label: 'Zoom to base level on click?',
|
|
201
|
+
checked: self.zoomToBaseLevel,
|
|
202
|
+
type: 'checkbox',
|
|
203
|
+
onClick: () => self.setZoomToBaseLevel(!self.zoomToBaseLevel),
|
|
204
|
+
},
|
|
205
|
+
]
|
|
206
|
+
},
|
|
207
|
+
/**
|
|
208
|
+
* #getter
|
|
209
|
+
*/
|
|
210
|
+
get processing() {
|
|
211
|
+
return !!self.progress
|
|
212
|
+
},
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* #getter
|
|
216
|
+
*/
|
|
217
|
+
get connectedView() {
|
|
218
|
+
const { views } = getSession(self)
|
|
219
|
+
return views.find(f => f.id === self.connectedViewId) as MaybeLGV
|
|
220
|
+
},
|
|
221
|
+
}))
|
|
222
|
+
|
|
223
|
+
.actions(self => ({
|
|
224
|
+
afterCreate() {
|
|
225
|
+
addDisposer(
|
|
226
|
+
self,
|
|
227
|
+
autorun(async () => {
|
|
228
|
+
if (self.blastParams) {
|
|
229
|
+
self.setProgress('Submitting query')
|
|
230
|
+
const data = await doLaunchBlast({
|
|
231
|
+
self: self as JBrowsePluginMsaViewModel,
|
|
232
|
+
})
|
|
233
|
+
self.setData(data)
|
|
234
|
+
self.setBlastParams(undefined)
|
|
235
|
+
self.setProgress('')
|
|
236
|
+
}
|
|
237
|
+
}),
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
// this adds highlights to the genome view when mouse-ing over the MSA
|
|
241
|
+
addDisposer(
|
|
242
|
+
self,
|
|
243
|
+
autorun(() => {
|
|
244
|
+
const { mouseCol, mouseClickCol } = self
|
|
245
|
+
const r1 =
|
|
246
|
+
mouseCol !== undefined
|
|
247
|
+
? msaCoordToGenomeCoord({ model: self, coord: mouseCol })
|
|
248
|
+
: undefined
|
|
249
|
+
const r2 =
|
|
250
|
+
mouseClickCol !== undefined
|
|
251
|
+
? msaCoordToGenomeCoord({ model: self, coord: mouseClickCol })
|
|
252
|
+
: undefined
|
|
253
|
+
self.setConnectedHighlights([r1, r2].filter(notEmpty))
|
|
254
|
+
}),
|
|
255
|
+
)
|
|
256
|
+
|
|
257
|
+
// nav to genome position after click
|
|
258
|
+
addDisposer(
|
|
259
|
+
self,
|
|
260
|
+
autorun(() => {
|
|
261
|
+
const { connectedView, zoomToBaseLevel, mouseClickCol } = self
|
|
262
|
+
const { assemblyManager } = getSession(self)
|
|
263
|
+
const r2 =
|
|
264
|
+
mouseClickCol !== undefined
|
|
265
|
+
? msaCoordToGenomeCoord({ model: self, coord: mouseClickCol })
|
|
266
|
+
: undefined
|
|
267
|
+
|
|
268
|
+
if (!r2 || !connectedView) {
|
|
269
|
+
return
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
if (zoomToBaseLevel) {
|
|
273
|
+
connectedView.navTo(r2)
|
|
274
|
+
} else {
|
|
275
|
+
const r =
|
|
276
|
+
assemblyManager
|
|
277
|
+
.get(connectedView.assemblyNames[0])
|
|
278
|
+
?.getCanonicalRefName(r2.refName) ?? r2.refName
|
|
279
|
+
connectedView.centerAt(r2.start, r)
|
|
280
|
+
}
|
|
281
|
+
}),
|
|
282
|
+
)
|
|
283
|
+
},
|
|
284
|
+
}))
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
export type JBrowsePluginMsaViewStateModel = ReturnType<
|
|
288
|
+
typeof stateModelFactory
|
|
289
|
+
>
|
|
290
|
+
export type JBrowsePluginMsaViewModel = Instance<JBrowsePluginMsaViewStateModel>
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// locals
|
|
2
|
+
import { JBrowsePluginMsaViewModel } from './model'
|
|
3
|
+
|
|
4
|
+
export function msaCoordToGenomeCoord({
|
|
5
|
+
model,
|
|
6
|
+
coord: mouseCol,
|
|
7
|
+
}: {
|
|
8
|
+
model: JBrowsePluginMsaViewModel
|
|
9
|
+
coord: number
|
|
10
|
+
}) {
|
|
11
|
+
const { transcriptToMsaMap } = model
|
|
12
|
+
if (mouseCol === undefined || transcriptToMsaMap === undefined) {
|
|
13
|
+
return
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const c = mouseCol - 1
|
|
17
|
+
const k1 = model.globalCoordToRowSpecificSeqCoord('QUERY', c) || 0
|
|
18
|
+
const k2 = model.globalCoordToRowSpecificSeqCoord('QUERY', c + 1) || 0
|
|
19
|
+
const { refName, p2g } = transcriptToMsaMap
|
|
20
|
+
const s = p2g[k1]
|
|
21
|
+
const e = p2g[k2]
|
|
22
|
+
return s !== undefined && e !== undefined
|
|
23
|
+
? {
|
|
24
|
+
refName,
|
|
25
|
+
start: Math.min(s, e),
|
|
26
|
+
end: Math.max(s, e),
|
|
27
|
+
}
|
|
28
|
+
: undefined
|
|
29
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Feature } from '@jbrowse/core/util'
|
|
2
|
+
|
|
3
|
+
export function checkHovered(hovered: unknown): hovered is {
|
|
4
|
+
hoverFeature: Feature
|
|
5
|
+
hoverPosition: { coord: number; refName: string }
|
|
6
|
+
} {
|
|
7
|
+
return (
|
|
8
|
+
!!hovered &&
|
|
9
|
+
typeof hovered == 'object' &&
|
|
10
|
+
'hoverFeature' in hovered &&
|
|
11
|
+
'hoverPosition' in hovered
|
|
12
|
+
)
|
|
13
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { SvgIcon, SvgIconProps } from '@mui/material'
|
|
3
|
+
|
|
4
|
+
export default function OpenInNewIcon(props: SvgIconProps) {
|
|
5
|
+
return (
|
|
6
|
+
<SvgIcon {...props}>
|
|
7
|
+
<path d="M0 0h24v24H0z" fill="none" />
|
|
8
|
+
<path d="M19 19H5V5h7V3H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2v-7h-2v7zM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7z" />
|
|
9
|
+
</SvgIcon>
|
|
10
|
+
)
|
|
11
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { TextField, TextFieldProps } from '@mui/material'
|
|
3
|
+
|
|
4
|
+
function TextField2({ children, ...rest }: TextFieldProps) {
|
|
5
|
+
return (
|
|
6
|
+
<div>
|
|
7
|
+
<TextField {...rest}>{children}</TextField>
|
|
8
|
+
</div>
|
|
9
|
+
)
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export default TextField2
|
package/src/index.ts
CHANGED
|
@@ -1,26 +1,24 @@
|
|
|
1
1
|
import Plugin from '@jbrowse/core/Plugin'
|
|
2
2
|
import PluginManager from '@jbrowse/core/PluginManager'
|
|
3
|
-
import { version } from '../package.json'
|
|
4
3
|
import { AbstractSessionModel, isAbstractMenuManager } from '@jbrowse/core/util'
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
import GridOn from '@
|
|
4
|
+
|
|
5
|
+
// icons
|
|
6
|
+
import GridOn from '@mui/icons-material/GridOn'
|
|
7
|
+
|
|
8
|
+
// locals
|
|
9
|
+
import { version } from '../package.json'
|
|
10
|
+
import MsaViewF from './MsaViewPanel'
|
|
11
|
+
import LaunchMsaViewF from './LaunchMsaView'
|
|
12
|
+
import AddHighlightModelF from './AddHighlightModel'
|
|
8
13
|
|
|
9
14
|
export default class MsaViewPlugin extends Plugin {
|
|
10
15
|
name = 'MsaViewPlugin'
|
|
11
16
|
version = version
|
|
12
17
|
|
|
13
18
|
install(pluginManager: PluginManager) {
|
|
14
|
-
|
|
15
|
-
pluginManager
|
|
16
|
-
|
|
17
|
-
new ViewType({
|
|
18
|
-
name: 'MsaView',
|
|
19
|
-
//@ts-ignore
|
|
20
|
-
stateModel: MSA.MSAModel,
|
|
21
|
-
ReactComponent: MSA.MSAView,
|
|
22
|
-
}),
|
|
23
|
-
)
|
|
19
|
+
MsaViewF(pluginManager)
|
|
20
|
+
LaunchMsaViewF(pluginManager)
|
|
21
|
+
AddHighlightModelF(pluginManager)
|
|
24
22
|
}
|
|
25
23
|
|
|
26
24
|
configure(pluginManager: PluginManager) {
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export async function myfetch(url: string, args?: RequestInit) {
|
|
2
|
+
const response = await fetch(url, args)
|
|
3
|
+
|
|
4
|
+
if (!response.ok) {
|
|
5
|
+
throw new Error(
|
|
6
|
+
`HTTP ${response.status} fetching ${url} ${await response.text()}`,
|
|
7
|
+
)
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
return response
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export async function textfetch(url: string, args?: RequestInit) {
|
|
14
|
+
const response = await myfetch(url, args)
|
|
15
|
+
return response.text()
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export async function jsonfetch(url: string, args?: RequestInit) {
|
|
19
|
+
const response = await myfetch(url, args)
|
|
20
|
+
return response.json()
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function timeout(time: number) {
|
|
24
|
+
return new Promise(res => setTimeout(res, time))
|
|
25
|
+
}
|