jbrowse-plugin-msaview 2.2.3 → 2.2.5
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 +1 -1
- package/README.md +229 -0
- package/dist/AddHighlightModel/GenomeMouseoverHighlight.js +23 -18
- package/dist/AddHighlightModel/GenomeMouseoverHighlight.js.map +1 -1
- package/dist/AddHighlightModel/MsaToGenomeHighlight.js +23 -13
- package/dist/AddHighlightModel/MsaToGenomeHighlight.js.map +1 -1
- package/dist/AddHighlightModel/index.js +8 -1
- package/dist/AddHighlightModel/index.js.map +1 -1
- package/dist/AddHighlightModel/util.d.ts +2 -2
- package/dist/BgzipFastaMsaAdapter/configSchema.d.ts +2 -2
- package/dist/LaunchMsaView/components/EnsemblGeneTree/EnsemblGeneTree.js +5 -11
- package/dist/LaunchMsaView/components/EnsemblGeneTree/EnsemblGeneTree.js.map +1 -1
- package/dist/LaunchMsaView/components/EnsemblGeneTree/useGeneTree.js +5 -1
- package/dist/LaunchMsaView/components/EnsemblGeneTree/useGeneTree.js.map +1 -1
- package/dist/LaunchMsaView/components/LaunchMsaViewDialog.js +16 -16
- package/dist/LaunchMsaView/components/LaunchMsaViewDialog.js.map +1 -1
- package/dist/LaunchMsaView/components/ManualMSALoader/ManualMSALoader.js +38 -46
- package/dist/LaunchMsaView/components/ManualMSALoader/ManualMSALoader.js.map +1 -1
- package/dist/LaunchMsaView/components/ManualMSALoader/launchView.d.ts +4 -3
- package/dist/LaunchMsaView/components/ManualMSALoader/launchView.js +4 -3
- package/dist/LaunchMsaView/components/ManualMSALoader/launchView.js.map +1 -1
- package/dist/LaunchMsaView/components/NCBIBlastQuery/CachedBlastResults.d.ts +9 -0
- package/dist/LaunchMsaView/components/NCBIBlastQuery/CachedBlastResults.js +76 -0
- package/dist/LaunchMsaView/components/NCBIBlastQuery/CachedBlastResults.js.map +1 -0
- package/dist/LaunchMsaView/components/NCBIBlastQuery/NCBIBlastAutomaticPanel.js +35 -13
- package/dist/LaunchMsaView/components/NCBIBlastQuery/NCBIBlastAutomaticPanel.js.map +1 -1
- package/dist/LaunchMsaView/components/NCBIBlastQuery/NCBIBlastManualPanel.js +6 -12
- package/dist/LaunchMsaView/components/NCBIBlastQuery/NCBIBlastManualPanel.js.map +1 -1
- package/dist/LaunchMsaView/components/NCBIBlastQuery/blastLaunchView.d.ts +6 -0
- package/dist/LaunchMsaView/components/NCBIBlastQuery/blastLaunchView.js +15 -0
- package/dist/LaunchMsaView/components/NCBIBlastQuery/blastLaunchView.js.map +1 -1
- package/dist/LaunchMsaView/components/PreLoadedMSA/PreLoadedMSADataPanel.js +12 -34
- package/dist/LaunchMsaView/components/PreLoadedMSA/PreLoadedMSADataPanel.js.map +1 -1
- package/dist/LaunchMsaView/components/PreLoadedMSA/consts.d.ts +1 -0
- package/dist/LaunchMsaView/components/PreLoadedMSA/consts.js +1 -0
- package/dist/LaunchMsaView/components/PreLoadedMSA/consts.js.map +1 -1
- package/dist/LaunchMsaView/components/TabPanel.d.ts +2 -2
- package/dist/LaunchMsaView/components/TranscriptSelector.d.ts +2 -2
- package/dist/LaunchMsaView/components/TranscriptSelector.js +3 -6
- package/dist/LaunchMsaView/components/TranscriptSelector.js.map +1 -1
- package/dist/LaunchMsaView/components/useSWRFeatureSequence.js +6 -4
- package/dist/LaunchMsaView/components/useSWRFeatureSequence.js.map +1 -1
- package/dist/LaunchMsaView/components/useTranscriptSelection.d.ts +16 -0
- package/dist/LaunchMsaView/components/useTranscriptSelection.js +31 -0
- package/dist/LaunchMsaView/components/useTranscriptSelection.js.map +1 -0
- package/dist/LaunchMsaView/components/util.d.ts +3 -1
- package/dist/LaunchMsaView/components/util.js +12 -2
- package/dist/LaunchMsaView/components/util.js.map +1 -1
- package/dist/LaunchMsaView/util.d.ts +2 -0
- package/dist/LaunchMsaView/util.js +16 -4
- package/dist/LaunchMsaView/util.js.map +1 -1
- package/dist/LaunchMsaViewExtensionPoint/index.d.ts +2 -0
- package/dist/LaunchMsaViewExtensionPoint/index.js +31 -0
- package/dist/LaunchMsaViewExtensionPoint/index.js.map +1 -0
- package/dist/MsaViewPanel/components/ConnectStructureDialog.d.ts +7 -0
- package/dist/MsaViewPanel/components/ConnectStructureDialog.js +56 -0
- package/dist/MsaViewPanel/components/ConnectStructureDialog.js.map +1 -0
- package/dist/MsaViewPanel/components/MsaViewPanel.js +4 -2
- package/dist/MsaViewPanel/components/MsaViewPanel.js.map +1 -1
- package/dist/MsaViewPanel/doLaunchBlast.d.ts +1 -0
- package/dist/MsaViewPanel/doLaunchBlast.js +65 -19
- package/dist/MsaViewPanel/doLaunchBlast.js.map +1 -1
- package/dist/MsaViewPanel/genomeToMSA.d.ts +6 -0
- package/dist/MsaViewPanel/genomeToMSA.js +38 -8
- package/dist/MsaViewPanel/genomeToMSA.js.map +1 -1
- package/dist/MsaViewPanel/genomeToMSA.test.d.ts +1 -0
- package/dist/MsaViewPanel/genomeToMSA.test.js +244 -0
- package/dist/MsaViewPanel/genomeToMSA.test.js.map +1 -0
- package/dist/MsaViewPanel/model.d.ts +719 -226
- package/dist/MsaViewPanel/model.js +467 -39
- package/dist/MsaViewPanel/model.js.map +1 -1
- package/dist/MsaViewPanel/msaCoordToGenomeCoord.d.ts +7 -2
- package/dist/MsaViewPanel/msaCoordToGenomeCoord.js +26 -27
- package/dist/MsaViewPanel/msaCoordToGenomeCoord.js.map +1 -1
- package/dist/MsaViewPanel/msaCoordToGenomeCoord.test.d.ts +1 -0
- package/dist/MsaViewPanel/msaCoordToGenomeCoord.test.js +240 -0
- package/dist/MsaViewPanel/msaCoordToGenomeCoord.test.js.map +1 -0
- package/dist/MsaViewPanel/msaDataStore.d.ts +14 -0
- package/dist/MsaViewPanel/msaDataStore.js +197 -0
- package/dist/MsaViewPanel/msaDataStore.js.map +1 -0
- package/dist/MsaViewPanel/pairwiseAlignment.d.ts +27 -0
- package/dist/MsaViewPanel/pairwiseAlignment.js +776 -0
- package/dist/MsaViewPanel/pairwiseAlignment.js.map +1 -0
- package/dist/MsaViewPanel/pairwiseAlignment.test.d.ts +1 -0
- package/dist/MsaViewPanel/pairwiseAlignment.test.js +112 -0
- package/dist/MsaViewPanel/pairwiseAlignment.test.js.map +1 -0
- package/dist/MsaViewPanel/structureConnection.d.ts +27 -0
- package/dist/MsaViewPanel/structureConnection.js +46 -0
- package/dist/MsaViewPanel/structureConnection.js.map +1 -0
- package/dist/MsaViewPanel/structureConnection.test.d.ts +1 -0
- package/dist/MsaViewPanel/structureConnection.test.js +122 -0
- package/dist/MsaViewPanel/structureConnection.test.js.map +1 -0
- package/dist/MsaViewPanel/types.d.ts +13 -0
- package/dist/MsaViewPanel/types.js +2 -0
- package/dist/MsaViewPanel/types.js.map +1 -0
- package/dist/MsaViewPanel/util.d.ts +7 -0
- package/dist/MsaViewPanel/util.js +10 -0
- package/dist/MsaViewPanel/util.js.map +1 -1
- package/dist/index.d.ts +5 -5
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/jbrowse-plugin-msaview.umd.production.min.js +39 -90
- package/dist/jbrowse-plugin-msaview.umd.production.min.js.map +4 -4
- package/dist/utils/blastCache.d.ts +34 -0
- package/dist/utils/blastCache.js +58 -0
- package/dist/utils/blastCache.js.map +1 -0
- package/dist/utils/fetch.d.ts +1 -1
- package/dist/utils/fetch.js +1 -1
- package/dist/utils/fetch.js.map +1 -1
- package/dist/utils/ncbiBlast.d.ts +1 -5
- package/dist/utils/taxonomyNames.d.ts +5 -0
- package/dist/utils/taxonomyNames.js +79 -0
- package/dist/utils/taxonomyNames.js.map +1 -0
- package/dist/utils/types.d.ts +8 -5
- package/package.json +50 -54
- package/src/AddHighlightModel/GenomeMouseoverHighlight.tsx +37 -21
- package/src/AddHighlightModel/MsaToGenomeHighlight.tsx +38 -17
- package/src/AddHighlightModel/index.tsx +9 -4
- package/src/LaunchMsaView/components/EnsemblGeneTree/EnsemblGeneTree.tsx +13 -13
- package/src/LaunchMsaView/components/EnsemblGeneTree/useGeneTree.ts +6 -0
- package/src/LaunchMsaView/components/LaunchMsaViewDialog.tsx +30 -23
- package/src/LaunchMsaView/components/ManualMSALoader/ManualMSALoader.tsx +64 -51
- package/src/LaunchMsaView/components/ManualMSALoader/launchView.ts +9 -6
- package/src/LaunchMsaView/components/NCBIBlastQuery/CachedBlastResults.tsx +146 -0
- package/src/LaunchMsaView/components/NCBIBlastQuery/NCBIBlastAutomaticPanel.tsx +53 -22
- package/src/LaunchMsaView/components/NCBIBlastQuery/NCBIBlastManualPanel.tsx +8 -13
- package/src/LaunchMsaView/components/NCBIBlastQuery/blastLaunchView.ts +25 -0
- package/src/LaunchMsaView/components/PreLoadedMSA/PreLoadedMSADataPanel.tsx +27 -47
- package/src/LaunchMsaView/components/PreLoadedMSA/consts.ts +1 -0
- package/src/LaunchMsaView/components/TabPanel.tsx +2 -2
- package/src/LaunchMsaView/components/TranscriptSelector.tsx +13 -20
- package/src/LaunchMsaView/components/useSWRFeatureSequence.ts +5 -5
- package/src/LaunchMsaView/components/useTranscriptSelection.ts +48 -0
- package/src/LaunchMsaView/components/util.ts +17 -2
- package/src/LaunchMsaView/index.ts +1 -1
- package/src/LaunchMsaView/util.ts +25 -6
- package/src/LaunchMsaViewExtensionPoint/index.ts +74 -0
- package/src/MsaViewPanel/components/ConnectStructureDialog.tsx +156 -0
- package/src/MsaViewPanel/components/MsaViewPanel.tsx +6 -1
- package/src/MsaViewPanel/doLaunchBlast.ts +83 -23
- package/src/MsaViewPanel/genomeToMSA.test.ts +281 -0
- package/src/MsaViewPanel/genomeToMSA.ts +43 -10
- package/src/MsaViewPanel/model.ts +590 -43
- package/src/MsaViewPanel/msaCoordToGenomeCoord.test.ts +256 -0
- package/src/MsaViewPanel/msaCoordToGenomeCoord.ts +43 -29
- package/src/MsaViewPanel/msaDataStore.ts +236 -0
- package/src/MsaViewPanel/pairwiseAlignment.test.ts +140 -0
- package/src/MsaViewPanel/pairwiseAlignment.ts +818 -0
- package/src/MsaViewPanel/structureConnection.test.ts +143 -0
- package/src/MsaViewPanel/structureConnection.ts +72 -0
- package/src/MsaViewPanel/types.ts +14 -0
- package/src/MsaViewPanel/util.ts +11 -0
- package/src/index.ts +3 -1
- package/src/utils/blastCache.ts +114 -0
- package/src/utils/fetch.ts +1 -1
- package/src/utils/taxonomyNames.ts +111 -0
- package/src/utils/types.ts +9 -1
- package/dist/LaunchMsaView/components/PreLoadedMSA/findValidTranscriptId.d.ts +0 -5
- package/dist/LaunchMsaView/components/PreLoadedMSA/findValidTranscriptId.js +0 -16
- package/dist/LaunchMsaView/components/PreLoadedMSA/findValidTranscriptId.js.map +0 -1
- package/dist/out.js +0 -55381
- package/src/LaunchMsaView/components/PreLoadedMSA/findValidTranscriptId.ts +0 -25
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, {
|
|
1
|
+
import React, { useState } from 'react'
|
|
2
2
|
|
|
3
3
|
import { readConfObject } from '@jbrowse/core/configuration'
|
|
4
4
|
import { ErrorMessage, LoadingEllipses, SanitizedHTML } from '@jbrowse/core/ui'
|
|
@@ -15,12 +15,11 @@ import useSWR from 'swr'
|
|
|
15
15
|
import { makeStyles } from 'tss-react/mui'
|
|
16
16
|
|
|
17
17
|
import TextField2 from '../../../components/TextField2'
|
|
18
|
-
import { getGeneDisplayName
|
|
18
|
+
import { getGeneDisplayName } from '../../util'
|
|
19
19
|
import TranscriptSelector from '../TranscriptSelector'
|
|
20
|
-
import {
|
|
20
|
+
import { useTranscriptSelection } from '../useTranscriptSelection'
|
|
21
21
|
import { swrFlags } from './consts'
|
|
22
22
|
import { fetchMSA, fetchMSAList } from './fetchMSAData'
|
|
23
|
-
import { findValidTranscriptId } from './findValidTranscriptId'
|
|
24
23
|
import { preCalculatedLaunchView } from './preCalculatedLaunchView'
|
|
25
24
|
import { Dataset } from './types'
|
|
26
25
|
|
|
@@ -46,18 +45,7 @@ const PreLoadedMSA = observer(function PreLoadedMSA2({
|
|
|
46
45
|
const { classes } = useStyles()
|
|
47
46
|
const { pluginManager } = getEnv(model)
|
|
48
47
|
const { assemblyNames } = view
|
|
49
|
-
const transcripts = getTranscriptFeatures(feature)
|
|
50
|
-
const [selectedTranscriptId, setSelectedTranscriptId] = useState(
|
|
51
|
-
getId(transcripts[0]),
|
|
52
|
-
)
|
|
53
48
|
const [viewError, setViewError] = useState<unknown>()
|
|
54
|
-
const selectedTranscript = selectedTranscriptId
|
|
55
|
-
? transcripts.find(val => getId(val) === selectedTranscriptId)
|
|
56
|
-
: undefined
|
|
57
|
-
const { proteinSequence, error: proteinSequenceError } = useFeatureSequence({
|
|
58
|
-
view,
|
|
59
|
-
feature: selectedTranscript,
|
|
60
|
-
})
|
|
61
49
|
|
|
62
50
|
const { jbrowse } = session
|
|
63
51
|
const datasets = readConfObject(jbrowse, ['msa', 'datasets']) as
|
|
@@ -82,18 +70,29 @@ const PreLoadedMSA = observer(function PreLoadedMSA2({
|
|
|
82
70
|
: undefined,
|
|
83
71
|
swrFlags,
|
|
84
72
|
)
|
|
73
|
+
|
|
74
|
+
const {
|
|
75
|
+
options: transcripts,
|
|
76
|
+
selectedId,
|
|
77
|
+
setSelectedId,
|
|
78
|
+
selectedTranscript,
|
|
79
|
+
proteinSequence,
|
|
80
|
+
error: proteinSequenceError,
|
|
81
|
+
validSet,
|
|
82
|
+
} = useTranscriptSelection({ feature, view, validIds: msaList })
|
|
83
|
+
|
|
85
84
|
const {
|
|
86
85
|
data: msaData,
|
|
87
86
|
isLoading: msaDataLoading,
|
|
88
87
|
error: msaDataFetchError,
|
|
89
88
|
} = useSWR(
|
|
90
|
-
|
|
91
|
-
? `${
|
|
89
|
+
selectedId && selectedDatasetId
|
|
90
|
+
? `${selectedId}-${selectedId}-${msaList?.length}-msa`
|
|
92
91
|
: 'none-msa',
|
|
93
92
|
() =>
|
|
94
|
-
|
|
93
|
+
selectedId && selectedDataset && msaList
|
|
95
94
|
? fetchMSA({
|
|
96
|
-
msaId:
|
|
95
|
+
msaId: selectedId,
|
|
97
96
|
config: selectedDataset.adapter,
|
|
98
97
|
pluginManager,
|
|
99
98
|
})
|
|
@@ -101,21 +100,6 @@ const PreLoadedMSA = observer(function PreLoadedMSA2({
|
|
|
101
100
|
swrFlags,
|
|
102
101
|
)
|
|
103
102
|
|
|
104
|
-
const validSet = useMemo(() => new Set(msaList), [msaList])
|
|
105
|
-
|
|
106
|
-
// Update selectedTranscriptId when msaList changes
|
|
107
|
-
useEffect(() => {
|
|
108
|
-
if (msaList && msaList.length > 0) {
|
|
109
|
-
const validId = findValidTranscriptId({
|
|
110
|
-
transcriptsList: transcripts,
|
|
111
|
-
validMsaList: msaList,
|
|
112
|
-
})
|
|
113
|
-
if (validId && validId !== selectedTranscriptId) {
|
|
114
|
-
setSelectedTranscriptId(validId)
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
}, [msaList, transcripts, selectedTranscriptId])
|
|
118
|
-
|
|
119
103
|
const e =
|
|
120
104
|
msaListFetchError ?? msaDataFetchError ?? proteinSequenceError ?? viewError
|
|
121
105
|
if (e) {
|
|
@@ -134,15 +118,11 @@ const PreLoadedMSA = observer(function PreLoadedMSA2({
|
|
|
134
118
|
setSelectedDatasetId(event.target.value)
|
|
135
119
|
}}
|
|
136
120
|
>
|
|
137
|
-
{datasets
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
))
|
|
143
|
-
) : (
|
|
144
|
-
<MenuItem>No MSA datasets found</MenuItem>
|
|
145
|
-
)}
|
|
121
|
+
{datasets?.map(d => (
|
|
122
|
+
<MenuItem key={d.datasetId} value={d.datasetId}>
|
|
123
|
+
{d.name}
|
|
124
|
+
</MenuItem>
|
|
125
|
+
))}
|
|
146
126
|
</TextField2>
|
|
147
127
|
|
|
148
128
|
{selectedDataset ? (
|
|
@@ -150,7 +130,7 @@ const PreLoadedMSA = observer(function PreLoadedMSA2({
|
|
|
150
130
|
{!msaListLoading && msaDataLoading ? (
|
|
151
131
|
<LoadingEllipses
|
|
152
132
|
variant="h6"
|
|
153
|
-
message={`Loading MSA for (${
|
|
133
|
+
message={`Loading MSA for (${selectedId})`}
|
|
154
134
|
/>
|
|
155
135
|
) : null}
|
|
156
136
|
{msaListLoading ? (
|
|
@@ -166,8 +146,8 @@ const PreLoadedMSA = observer(function PreLoadedMSA2({
|
|
|
166
146
|
<TranscriptSelector
|
|
167
147
|
feature={feature}
|
|
168
148
|
options={transcripts}
|
|
169
|
-
|
|
170
|
-
onTranscriptChange={
|
|
149
|
+
selectedTranscript={selectedTranscript}
|
|
150
|
+
onTranscriptChange={setSelectedId}
|
|
171
151
|
proteinSequence={proteinSequence}
|
|
172
152
|
validSet={validSet}
|
|
173
153
|
/>
|
|
@@ -187,7 +167,7 @@ const PreLoadedMSA = observer(function PreLoadedMSA2({
|
|
|
187
167
|
if (!selectedTranscript || !msaData) {
|
|
188
168
|
return
|
|
189
169
|
}
|
|
190
|
-
const querySeqName = `${
|
|
170
|
+
const querySeqName = `${selectedId}_${assemblyNames[0]}`
|
|
191
171
|
preCalculatedLaunchView({
|
|
192
172
|
session,
|
|
193
173
|
newViewTitle: getGeneDisplayName(selectedTranscript),
|
|
@@ -25,23 +25,20 @@ const useStyles = makeStyles()({
|
|
|
25
25
|
export default function TranscriptSelector({
|
|
26
26
|
feature,
|
|
27
27
|
options,
|
|
28
|
-
|
|
28
|
+
selectedTranscript,
|
|
29
29
|
onTranscriptChange,
|
|
30
30
|
proteinSequence,
|
|
31
31
|
validSet,
|
|
32
32
|
}: {
|
|
33
33
|
feature: Feature
|
|
34
34
|
options: Feature[]
|
|
35
|
-
|
|
35
|
+
selectedTranscript: Feature | undefined
|
|
36
36
|
onTranscriptChange: (transcriptId: string) => void
|
|
37
37
|
proteinSequence: string | undefined
|
|
38
38
|
validSet?: Set<string>
|
|
39
39
|
}) {
|
|
40
40
|
const { classes } = useStyles()
|
|
41
41
|
const [showSequence, setShowSequence] = useState(false)
|
|
42
|
-
const selectedTranscript = options.find(
|
|
43
|
-
val => getId(val) === selectedTranscriptId,
|
|
44
|
-
)!
|
|
45
42
|
|
|
46
43
|
return (
|
|
47
44
|
<>
|
|
@@ -51,26 +48,22 @@ export default function TranscriptSelector({
|
|
|
51
48
|
label={`Choose isoform of ${getGeneDisplayName(feature)}`}
|
|
52
49
|
select
|
|
53
50
|
className={classes.minWidth}
|
|
54
|
-
value={
|
|
51
|
+
value={getId(selectedTranscript)}
|
|
55
52
|
onChange={event => {
|
|
56
53
|
onTranscriptChange(event.target.value)
|
|
57
54
|
}}
|
|
58
55
|
>
|
|
59
|
-
{options
|
|
60
|
-
.
|
|
61
|
-
|
|
56
|
+
{options.map(val => {
|
|
57
|
+
const inSet = validSet ? validSet.has(getId(val)) : true
|
|
58
|
+
const { len, mod } = getTranscriptLength(val)
|
|
59
|
+
return (
|
|
60
|
+
<MenuItem value={getId(val)} key={val.id()} disabled={!inSet}>
|
|
61
|
+
{getTranscriptDisplayName(val)} ({len} aa){' '}
|
|
62
|
+
{mod ? ` (possible fragment)` : ''}
|
|
63
|
+
{validSet ? (inSet ? ' (has data)' : ' (no data)') : ''}
|
|
64
|
+
</MenuItem>
|
|
62
65
|
)
|
|
63
|
-
|
|
64
|
-
const inSet = validSet ? validSet.has(getId(val)) : true
|
|
65
|
-
const { len, mod } = getTranscriptLength(val)
|
|
66
|
-
return (
|
|
67
|
-
<MenuItem value={getId(val)} key={val.id()} disabled={!inSet}>
|
|
68
|
-
{getTranscriptDisplayName(val)} ({len} aa){' '}
|
|
69
|
-
{mod ? ` (possible fragment)` : ''}
|
|
70
|
-
{validSet ? (inSet ? ' (has data)' : ' (no data)') : ''}
|
|
71
|
-
</MenuItem>
|
|
72
|
-
)
|
|
73
|
-
})}
|
|
66
|
+
})}
|
|
74
67
|
</TextField2>
|
|
75
68
|
<div style={{ alignContent: 'center', marginLeft: 20 }}>
|
|
76
69
|
<Button
|
|
@@ -6,20 +6,16 @@ import { fetchSeq } from './fetchSeq'
|
|
|
6
6
|
import type { SeqState } from './types'
|
|
7
7
|
import type { Feature } from '@jbrowse/core/util'
|
|
8
8
|
|
|
9
|
-
const BPLIMIT = 500_000
|
|
10
|
-
|
|
11
9
|
async function featureSequenceFetcher({
|
|
12
10
|
feature,
|
|
13
11
|
assemblyName,
|
|
14
12
|
upDownBp,
|
|
15
13
|
view,
|
|
16
|
-
forceLoad,
|
|
17
14
|
}: {
|
|
18
15
|
feature: Feature
|
|
19
16
|
assemblyName: string
|
|
20
17
|
upDownBp: number
|
|
21
18
|
view: { assemblyNames?: string[] }
|
|
22
|
-
forceLoad: boolean
|
|
23
19
|
}): Promise<SeqState | undefined> {
|
|
24
20
|
const session = getSession(view)
|
|
25
21
|
const { start, end, refName } = feature.toJSON() as {
|
|
@@ -79,8 +75,12 @@ export function useSWRFeatureSequence({
|
|
|
79
75
|
assemblyName: assemblyName!,
|
|
80
76
|
upDownBp,
|
|
81
77
|
view: view!,
|
|
82
|
-
forceLoad,
|
|
83
78
|
}),
|
|
79
|
+
{
|
|
80
|
+
revalidateOnFocus: false,
|
|
81
|
+
revalidateOnReconnect: false,
|
|
82
|
+
revalidateIfStale: false,
|
|
83
|
+
},
|
|
84
84
|
)
|
|
85
85
|
|
|
86
86
|
return {
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { useEffect, useMemo, useState } from 'react'
|
|
2
|
+
|
|
3
|
+
import { getId, getSortedTranscriptFeatures } from '../util'
|
|
4
|
+
import { useFeatureSequence } from './useFeatureSequence'
|
|
5
|
+
|
|
6
|
+
import type { Feature } from '@jbrowse/core/util'
|
|
7
|
+
|
|
8
|
+
export function useTranscriptSelection({
|
|
9
|
+
feature,
|
|
10
|
+
view,
|
|
11
|
+
validIds,
|
|
12
|
+
}: {
|
|
13
|
+
feature: Feature
|
|
14
|
+
view: { assemblyNames?: string[] } | undefined
|
|
15
|
+
validIds?: string[]
|
|
16
|
+
}) {
|
|
17
|
+
const options = getSortedTranscriptFeatures(feature)
|
|
18
|
+
const [selectedId, setSelectedId] = useState(getId(options[0]))
|
|
19
|
+
const selectedTranscript = options.find(val => getId(val) === selectedId)
|
|
20
|
+
const { proteinSequence, error } = useFeatureSequence({
|
|
21
|
+
view,
|
|
22
|
+
feature: selectedTranscript,
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
const validSet = useMemo(
|
|
26
|
+
() => (validIds ? new Set(validIds) : undefined),
|
|
27
|
+
[validIds],
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
useEffect(() => {
|
|
31
|
+
if (validIds && validIds.length > 0 && !validIds.includes(selectedId)) {
|
|
32
|
+
const validOption = options.find(opt => validIds.includes(getId(opt)))
|
|
33
|
+
if (validOption) {
|
|
34
|
+
setSelectedId(getId(validOption))
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}, [validIds, options, selectedId])
|
|
38
|
+
|
|
39
|
+
return {
|
|
40
|
+
options,
|
|
41
|
+
selectedId,
|
|
42
|
+
setSelectedId,
|
|
43
|
+
selectedTranscript,
|
|
44
|
+
proteinSequence,
|
|
45
|
+
error,
|
|
46
|
+
validSet,
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -1,5 +1,20 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import type { TaxonomyInfo } from '../../utils/taxonomyNames'
|
|
2
|
+
|
|
3
|
+
export function makeId(
|
|
4
|
+
h: { accession: string; sciname: string; taxid?: number },
|
|
5
|
+
taxonomyInfo?: Map<number, TaxonomyInfo>,
|
|
6
|
+
) {
|
|
7
|
+
let speciesName = h.sciname.replaceAll(' ', '_')
|
|
8
|
+
if (h.taxid && taxonomyInfo?.has(h.taxid)) {
|
|
9
|
+
const info = taxonomyInfo.get(h.taxid)!
|
|
10
|
+
if (info.commonName) {
|
|
11
|
+
speciesName = info.commonName
|
|
12
|
+
.split(' ')
|
|
13
|
+
.map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
|
|
14
|
+
.join('_')
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
return `${h.accession}-${speciesName}`
|
|
3
18
|
}
|
|
4
19
|
|
|
5
20
|
export function strip(s: string) {
|
|
@@ -7,7 +7,7 @@ import AddIcon from '@mui/icons-material/Add'
|
|
|
7
7
|
|
|
8
8
|
import LaunchMsaViewDialog from './components/LaunchMsaViewDialog'
|
|
9
9
|
|
|
10
|
-
import type { IAnyModelType } from 'mobx-state-tree'
|
|
10
|
+
import type { IAnyModelType } from '@jbrowse/mobx-state-tree'
|
|
11
11
|
|
|
12
12
|
function isDisplay(elt: { name: string }): elt is DisplayType {
|
|
13
13
|
return elt.name === 'LinearBasicDisplay'
|
|
@@ -6,12 +6,21 @@ export function getTranscriptFeatures(feature: Feature) {
|
|
|
6
6
|
// check if we are looking at a 'two-level' or 'three-level' feature by
|
|
7
7
|
// finding exon/CDS subfeatures. we want to select from transcript names
|
|
8
8
|
const subfeatures = feature.get('subfeatures') ?? []
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
)
|
|
9
|
+
|
|
10
|
+
// Check for mRNA/transcript subfeatures (three-level: gene → mRNA → CDS)
|
|
11
|
+
// Filter to only those that have CDS subfeatures (i.e. are coding)
|
|
12
|
+
const transcripts = subfeatures.filter(
|
|
13
|
+
(f: Feature) =>
|
|
14
|
+
(f.get('type') === 'mRNA' || f.get('type') === 'transcript') &&
|
|
15
|
+
f.get('subfeatures')?.some((s: Feature) => s.get('type') === 'CDS'),
|
|
16
|
+
)
|
|
17
|
+
if (transcripts.length > 0) {
|
|
18
|
+
return transcripts
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Has direct CDS children, treat feature itself as the transcript
|
|
22
|
+
// (two-level: gene → CDS or mRNA → CDS)
|
|
23
|
+
return [feature]
|
|
15
24
|
}
|
|
16
25
|
|
|
17
26
|
export function getTranscriptLength(feature: Feature) {
|
|
@@ -48,3 +57,13 @@ export function getGeneDisplayName(val?: Feature) {
|
|
|
48
57
|
.filter(f => !!f)
|
|
49
58
|
.join(' ')
|
|
50
59
|
}
|
|
60
|
+
|
|
61
|
+
export function getSortedTranscriptFeatures(feature: Feature) {
|
|
62
|
+
return getTranscriptFeatures(feature).toSorted(
|
|
63
|
+
(a, b) => getTranscriptLength(b).len - getTranscriptLength(a).len,
|
|
64
|
+
)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function cleanProteinSequence(seq: string) {
|
|
68
|
+
return seq.replaceAll('*', '').replaceAll('&', '')
|
|
69
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import type PluginManager from '@jbrowse/core/PluginManager'
|
|
2
|
+
import type { AbstractSessionModel } from '@jbrowse/core/util'
|
|
3
|
+
|
|
4
|
+
export default function LaunchMsaViewExtensionPointF(
|
|
5
|
+
pluginManager: PluginManager,
|
|
6
|
+
) {
|
|
7
|
+
pluginManager.addToExtensionPoint(
|
|
8
|
+
'LaunchView-MsaView',
|
|
9
|
+
// @ts-expect-error
|
|
10
|
+
({
|
|
11
|
+
session,
|
|
12
|
+
data,
|
|
13
|
+
msaFileLocation,
|
|
14
|
+
treeFileLocation,
|
|
15
|
+
connectedViewId,
|
|
16
|
+
connectedFeature,
|
|
17
|
+
displayName,
|
|
18
|
+
colorSchemeName,
|
|
19
|
+
colWidth,
|
|
20
|
+
rowHeight,
|
|
21
|
+
treeAreaWidth,
|
|
22
|
+
treeWidth,
|
|
23
|
+
drawNodeBubbles,
|
|
24
|
+
labelsAlignRight,
|
|
25
|
+
showBranchLen,
|
|
26
|
+
querySeqName,
|
|
27
|
+
}: {
|
|
28
|
+
session: AbstractSessionModel
|
|
29
|
+
data?: { msa: string; tree?: string }
|
|
30
|
+
msaFileLocation?: { uri: string }
|
|
31
|
+
treeFileLocation?: { uri: string }
|
|
32
|
+
connectedViewId?: string
|
|
33
|
+
connectedFeature?: Record<string, unknown>
|
|
34
|
+
displayName?: string
|
|
35
|
+
colorSchemeName?: string
|
|
36
|
+
colWidth?: number
|
|
37
|
+
rowHeight?: number
|
|
38
|
+
treeAreaWidth?: number
|
|
39
|
+
treeWidth?: number
|
|
40
|
+
drawNodeBubbles?: boolean
|
|
41
|
+
labelsAlignRight?: boolean
|
|
42
|
+
showBranchLen?: boolean
|
|
43
|
+
querySeqName?: string
|
|
44
|
+
}) => {
|
|
45
|
+
if (!data && !msaFileLocation) {
|
|
46
|
+
throw new Error(
|
|
47
|
+
'No MSA data or file location provided when launching MSA view',
|
|
48
|
+
)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
session.addView('MsaView', {
|
|
52
|
+
type: 'MsaView',
|
|
53
|
+
displayName,
|
|
54
|
+
connectedViewId,
|
|
55
|
+
connectedFeature,
|
|
56
|
+
colorSchemeName,
|
|
57
|
+
colWidth,
|
|
58
|
+
rowHeight,
|
|
59
|
+
treeAreaWidth,
|
|
60
|
+
treeWidth,
|
|
61
|
+
drawNodeBubbles,
|
|
62
|
+
labelsAlignRight,
|
|
63
|
+
showBranchLen,
|
|
64
|
+
init: {
|
|
65
|
+
msaData: data?.msa,
|
|
66
|
+
treeData: data?.tree,
|
|
67
|
+
msaUrl: msaFileLocation?.uri,
|
|
68
|
+
treeUrl: treeFileLocation?.uri,
|
|
69
|
+
querySeqName,
|
|
70
|
+
},
|
|
71
|
+
})
|
|
72
|
+
},
|
|
73
|
+
)
|
|
74
|
+
}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import React, { useState } from 'react'
|
|
2
|
+
|
|
3
|
+
import { Dialog } from '@jbrowse/core/ui'
|
|
4
|
+
import { getSession } from '@jbrowse/core/util'
|
|
5
|
+
import {
|
|
6
|
+
Button,
|
|
7
|
+
DialogActions,
|
|
8
|
+
DialogContent,
|
|
9
|
+
FormControl,
|
|
10
|
+
InputLabel,
|
|
11
|
+
MenuItem,
|
|
12
|
+
Select,
|
|
13
|
+
Typography,
|
|
14
|
+
} from '@mui/material'
|
|
15
|
+
import { observer } from 'mobx-react'
|
|
16
|
+
|
|
17
|
+
import type { JBrowsePluginMsaViewModel } from '../model'
|
|
18
|
+
|
|
19
|
+
const ConnectStructureDialog = observer(function ConnectStructureDialog({
|
|
20
|
+
model,
|
|
21
|
+
handleClose,
|
|
22
|
+
}: {
|
|
23
|
+
model: JBrowsePluginMsaViewModel
|
|
24
|
+
handleClose: () => void
|
|
25
|
+
}) {
|
|
26
|
+
const session = getSession(model)
|
|
27
|
+
const [selectedViewId, setSelectedViewId] = useState('')
|
|
28
|
+
const [selectedStructureIdx, setSelectedStructureIdx] = useState(0)
|
|
29
|
+
const [selectedMsaRow, setSelectedMsaRow] = useState(model.querySeqName)
|
|
30
|
+
const [error, setError] = useState<string>()
|
|
31
|
+
|
|
32
|
+
// Find all ProteinViews in the session
|
|
33
|
+
|
|
34
|
+
const proteinViews = session.views.filter(
|
|
35
|
+
(v: any) => v.type === 'ProteinView',
|
|
36
|
+
) as any[]
|
|
37
|
+
|
|
38
|
+
// Get structures for the selected view
|
|
39
|
+
const selectedView = proteinViews.find(v => v.id === selectedViewId)
|
|
40
|
+
const structures = selectedView?.structures ?? []
|
|
41
|
+
|
|
42
|
+
// Get MSA row names
|
|
43
|
+
const msaRowNames = model.rows.map(r => r[0])
|
|
44
|
+
|
|
45
|
+
const handleConnect = () => {
|
|
46
|
+
if (!selectedViewId) {
|
|
47
|
+
setError('Please select a protein view')
|
|
48
|
+
return
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
try {
|
|
52
|
+
model.connectToStructure(
|
|
53
|
+
selectedViewId,
|
|
54
|
+
selectedStructureIdx,
|
|
55
|
+
selectedMsaRow,
|
|
56
|
+
)
|
|
57
|
+
handleClose()
|
|
58
|
+
} catch (e) {
|
|
59
|
+
setError(e instanceof Error ? e.message : String(e))
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return (
|
|
64
|
+
<Dialog
|
|
65
|
+
maxWidth="sm"
|
|
66
|
+
title="Connect to Protein Structure"
|
|
67
|
+
open
|
|
68
|
+
onClose={handleClose}
|
|
69
|
+
>
|
|
70
|
+
<DialogContent>
|
|
71
|
+
{proteinViews.length === 0 ? (
|
|
72
|
+
<Typography color="textSecondary">
|
|
73
|
+
No protein views are currently open. Please open a protein structure
|
|
74
|
+
view first.
|
|
75
|
+
</Typography>
|
|
76
|
+
) : (
|
|
77
|
+
<>
|
|
78
|
+
<FormControl fullWidth sx={{ mb: 2 }}>
|
|
79
|
+
<InputLabel>Protein View</InputLabel>
|
|
80
|
+
<Select
|
|
81
|
+
value={selectedViewId}
|
|
82
|
+
label="Protein View"
|
|
83
|
+
onChange={e => {
|
|
84
|
+
setSelectedViewId(e.target.value)
|
|
85
|
+
setSelectedStructureIdx(0)
|
|
86
|
+
}}
|
|
87
|
+
>
|
|
88
|
+
{proteinViews.map(view => (
|
|
89
|
+
<MenuItem key={view.id} value={view.id}>
|
|
90
|
+
{view.displayName ?? `ProteinView ${view.id}`}
|
|
91
|
+
</MenuItem>
|
|
92
|
+
))}
|
|
93
|
+
</Select>
|
|
94
|
+
</FormControl>
|
|
95
|
+
|
|
96
|
+
{structures.length > 1 && (
|
|
97
|
+
<FormControl fullWidth sx={{ mb: 2 }}>
|
|
98
|
+
<InputLabel>Structure</InputLabel>
|
|
99
|
+
<Select
|
|
100
|
+
value={selectedStructureIdx}
|
|
101
|
+
label="Structure"
|
|
102
|
+
onChange={e => {
|
|
103
|
+
setSelectedStructureIdx(e.target.value)
|
|
104
|
+
}}
|
|
105
|
+
>
|
|
106
|
+
{structures.map(
|
|
107
|
+
(structure: { url?: string }, idx: number) => (
|
|
108
|
+
<MenuItem key={idx} value={idx}>
|
|
109
|
+
{structure.url ?? `Structure ${idx + 1}`}
|
|
110
|
+
</MenuItem>
|
|
111
|
+
),
|
|
112
|
+
)}
|
|
113
|
+
</Select>
|
|
114
|
+
</FormControl>
|
|
115
|
+
)}
|
|
116
|
+
|
|
117
|
+
<FormControl fullWidth sx={{ mb: 2 }}>
|
|
118
|
+
<InputLabel>MSA Row</InputLabel>
|
|
119
|
+
<Select
|
|
120
|
+
value={selectedMsaRow}
|
|
121
|
+
label="MSA Row"
|
|
122
|
+
onChange={e => {
|
|
123
|
+
setSelectedMsaRow(e.target.value)
|
|
124
|
+
}}
|
|
125
|
+
>
|
|
126
|
+
{msaRowNames.map(name => (
|
|
127
|
+
<MenuItem key={name} value={name}>
|
|
128
|
+
{name}
|
|
129
|
+
</MenuItem>
|
|
130
|
+
))}
|
|
131
|
+
</Select>
|
|
132
|
+
</FormControl>
|
|
133
|
+
|
|
134
|
+
{error && (
|
|
135
|
+
<Typography color="error" sx={{ mt: 1 }}>
|
|
136
|
+
{error}
|
|
137
|
+
</Typography>
|
|
138
|
+
)}
|
|
139
|
+
</>
|
|
140
|
+
)}
|
|
141
|
+
</DialogContent>
|
|
142
|
+
<DialogActions>
|
|
143
|
+
<Button onClick={handleClose}>Cancel</Button>
|
|
144
|
+
<Button
|
|
145
|
+
onClick={handleConnect}
|
|
146
|
+
variant="contained"
|
|
147
|
+
disabled={proteinViews.length === 0 || !selectedViewId}
|
|
148
|
+
>
|
|
149
|
+
Connect
|
|
150
|
+
</Button>
|
|
151
|
+
</DialogActions>
|
|
152
|
+
</Dialog>
|
|
153
|
+
)
|
|
154
|
+
})
|
|
155
|
+
|
|
156
|
+
export default ConnectStructureDialog
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
2
|
|
|
3
|
+
import { LoadingEllipses } from '@jbrowse/core/ui'
|
|
3
4
|
import { observer } from 'mobx-react'
|
|
4
5
|
import { MSAView } from 'react-msaview'
|
|
5
6
|
|
|
@@ -11,11 +12,15 @@ const MsaViewPanel = observer(function MsaViewPanel2({
|
|
|
11
12
|
}: {
|
|
12
13
|
model: JBrowsePluginMsaViewModel
|
|
13
14
|
}) {
|
|
14
|
-
const { blastParams } = model
|
|
15
|
+
const { blastParams, loadingStoredData } = model
|
|
15
16
|
return (
|
|
16
17
|
<div>
|
|
17
18
|
{blastParams ? (
|
|
18
19
|
<LoadingBLAST model={model} baseUrl={blastParams.baseUrl} />
|
|
20
|
+
) : loadingStoredData ? (
|
|
21
|
+
<div style={{ padding: 20 }}>
|
|
22
|
+
<LoadingEllipses message="Loading MSA data" variant="h6" />
|
|
23
|
+
</div>
|
|
19
24
|
) : (
|
|
20
25
|
<MSAView model={model} />
|
|
21
26
|
)}
|