jbrowse-plugin-protein3d 0.11.2 → 0.12.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/dist/LaunchProteinView/codingFeature.js +33 -0
- package/dist/LaunchProteinView/components/AlphaFoldDBSearch.js +9 -18
- package/dist/LaunchProteinView/components/AlphaFoldDBSearchStatus.js +10 -13
- package/dist/LaunchProteinView/components/FoldseekActionMenu.js +1 -2
- package/dist/LaunchProteinView/components/FoldseekResultsTable.js +11 -5
- package/dist/LaunchProteinView/components/FoldseekSearch.js +40 -23
- package/dist/LaunchProteinView/components/HelpDialog.js +22 -11
- package/dist/LaunchProteinView/components/IdentifierSelector.js +1 -1
- package/dist/LaunchProteinView/components/LaunchOptionsMenu.js +11 -4
- package/dist/LaunchProteinView/components/LaunchProteinViewDialog.js +17 -7
- package/dist/LaunchProteinView/components/PartialFailureNotice.js +9 -0
- package/dist/LaunchProteinView/components/PdbResultsTable.js +5 -3
- package/dist/LaunchProteinView/components/PdbSearch.js +46 -23
- package/dist/LaunchProteinView/components/ProteinViewActions.js +7 -17
- package/dist/LaunchProteinView/components/SequenceMismatchNotice.js +5 -7
- package/dist/LaunchProteinView/components/StructureSourcePicker.js +4 -12
- package/dist/LaunchProteinView/components/TabPanel.js +11 -5
- package/dist/LaunchProteinView/components/TranscriptSelector.js +12 -9
- package/dist/LaunchProteinView/components/UniProtIdInput.js +3 -3
- package/dist/LaunchProteinView/components/UniProtLookupControls.js +32 -0
- package/dist/LaunchProteinView/components/UniProtResultsTable.js +1 -1
- package/dist/LaunchProteinView/components/UserProvidedStructure.js +17 -16
- package/dist/LaunchProteinView/components/proteinAssemblySetup.js +1 -1
- package/dist/LaunchProteinView/components/proteinTrackSetup.js +1 -2
- package/dist/LaunchProteinView/hooks/useAlphaFoldDBSearch.js +4 -5
- package/dist/LaunchProteinView/hooks/useAlphaFoldData.js +1 -1
- package/dist/LaunchProteinView/hooks/useFoldseekSearch.js +12 -5
- package/dist/LaunchProteinView/hooks/useIsoformProteinSequences.js +31 -27
- package/dist/LaunchProteinView/hooks/usePdbBestStructures.js +3 -3
- package/dist/LaunchProteinView/hooks/usePdbEntryMolecules.js +9 -0
- package/dist/LaunchProteinView/hooks/useStructureFileSequence.js +1 -1
- package/dist/LaunchProteinView/hooks/useTranscriptIsoformSelection.js +7 -5
- package/dist/LaunchProteinView/hooks/useTranscriptSelection.js +4 -4
- package/dist/LaunchProteinView/hooks/useUniProtIdLookup.js +38 -17
- package/dist/LaunchProteinView/hooks/useUniProtSearch.js +8 -3
- package/dist/LaunchProteinView/index.js +16 -7
- package/dist/LaunchProteinView/services/foldseekApi.js +14 -11
- package/dist/LaunchProteinView/utils/calculateProteinSequence.js +62 -12
- package/dist/LaunchProteinView/utils/launchHelpers.js +11 -3
- package/dist/LaunchProteinView/utils/util.js +19 -73
- package/dist/LaunchProteinViewExtensionPoint/index.js +6 -5
- package/dist/LaunchProteinViewExtensionPoint/resolveShortLaunch.js +10 -10
- package/dist/Protein1DLinkage/linkage.js +2 -1
- package/dist/ProteinView/attachStructureInteractions.js +70 -0
- package/dist/ProteinView/components/ChainSelect.js +2 -2
- package/dist/ProteinView/components/HeaderStructureRow.js +37 -0
- package/dist/ProteinView/components/ManualAlignmentDialog.js +1 -2
- package/dist/ProteinView/components/ProteinAlignment.js +15 -10
- package/dist/ProteinView/components/ProteinView.js +25 -5
- package/dist/ProteinView/components/ProteinViewHeader.js +10 -10
- package/dist/ProteinView/components/TrackLegend.js +33 -0
- package/dist/ProteinView/hooks/useProteinFeatureTrackData.js +1 -1
- package/dist/ProteinView/hooks/useStructureUniProt.js +1 -1
- package/dist/ProteinView/hooks/useUniProtFeatures.js +1 -2
- package/dist/ProteinView/loadStructureData.js +1 -2
- package/dist/ProteinView/model.js +129 -71
- package/dist/ProteinView/proteinToGenomeMapping.js +25 -1
- package/dist/ProteinView/proteinViewSpec.js +6 -2
- package/dist/ProteinView/removeStructure.js +15 -0
- package/dist/ProteinView/residueTracks.js +12 -0
- package/dist/ProteinView/showLoading.js +3 -1
- package/dist/ProteinView/storedSettings.js +9 -3
- package/dist/ProteinView/structureLoader.js +68 -6
- package/dist/ProteinView/structureModel.js +130 -117
- package/dist/ProteinView/structurePipeline.js +1 -1
- package/dist/ProteinView/util.js +8 -3
- package/dist/jbrowse-plugin-protein3d.umd.production.min.js +15 -18
- package/dist/jbrowse-plugin-protein3d.umd.production.min.js.map +4 -4
- package/dist/mappings.js +4 -179
- package/dist/version.js +1 -1
- package/package.json +2 -1
- package/src/LaunchProteinView/codingFeature.test.ts +95 -0
- package/src/LaunchProteinView/codingFeature.ts +47 -0
- package/src/LaunchProteinView/components/AlphaFoldDBSearch.tsx +16 -55
- package/src/LaunchProteinView/components/AlphaFoldDBSearchStatus.tsx +12 -26
- package/src/LaunchProteinView/components/FoldseekActionMenu.tsx +6 -5
- package/src/LaunchProteinView/components/FoldseekResultsTable.tsx +17 -4
- package/src/LaunchProteinView/components/FoldseekSearch.tsx +78 -42
- package/src/LaunchProteinView/components/HelpDialog.tsx +38 -32
- package/src/LaunchProteinView/components/IdentifierSelector.tsx +1 -2
- package/src/LaunchProteinView/components/LaunchOptionsMenu.tsx +20 -1
- package/src/LaunchProteinView/components/LaunchProteinViewDialog.tsx +20 -15
- package/src/LaunchProteinView/components/PartialFailureNotice.tsx +19 -0
- package/src/LaunchProteinView/components/PdbResultsTable.tsx +6 -3
- package/src/LaunchProteinView/components/PdbSearch.tsx +77 -56
- package/src/LaunchProteinView/components/ProteinViewActions.tsx +23 -42
- package/src/LaunchProteinView/components/SequenceMismatchNotice.tsx +5 -20
- package/src/LaunchProteinView/components/StructureSourcePicker.tsx +2 -22
- package/src/LaunchProteinView/components/TabPanel.tsx +12 -6
- package/src/LaunchProteinView/components/TranscriptSelector.tsx +14 -11
- package/src/LaunchProteinView/components/UniProtIdInput.tsx +11 -5
- package/src/LaunchProteinView/components/UniProtLookupControls.tsx +87 -0
- package/src/LaunchProteinView/components/UniProtResultsTable.tsx +2 -2
- package/src/LaunchProteinView/components/UserProvidedStructure.tsx +30 -40
- package/src/LaunchProteinView/components/proteinAssemblySetup.ts +1 -1
- package/src/LaunchProteinView/components/proteinTrackSetup.ts +1 -2
- package/src/LaunchProteinView/hooks/uniProtLookupStatus.test.ts +37 -0
- package/src/LaunchProteinView/hooks/useAlphaFoldDBSearch.ts +7 -3
- package/src/LaunchProteinView/hooks/useAlphaFoldData.ts +1 -4
- package/src/LaunchProteinView/hooks/useFoldseekSearch.ts +13 -5
- package/src/LaunchProteinView/hooks/useIsoformProteinSequences.ts +42 -32
- package/src/LaunchProteinView/hooks/usePdbBestStructures.ts +10 -9
- package/src/LaunchProteinView/hooks/usePdbEntryMolecules.ts +19 -0
- package/src/LaunchProteinView/hooks/useStructureFileSequence.ts +1 -1
- package/src/LaunchProteinView/hooks/useTranscriptIsoformSelection.ts +16 -8
- package/src/LaunchProteinView/hooks/useTranscriptSelection.ts +5 -4
- package/src/LaunchProteinView/hooks/useUniProtIdLookup.ts +49 -18
- package/src/LaunchProteinView/hooks/useUniProtSearch.ts +15 -7
- package/src/LaunchProteinView/index.ts +18 -8
- package/src/LaunchProteinView/services/foldseekApi.ts +14 -17
- package/src/LaunchProteinView/utils/calculateProteinSequence.ts +113 -15
- package/src/LaunchProteinView/utils/launchHelpers.test.ts +48 -0
- package/src/LaunchProteinView/utils/launchHelpers.ts +12 -2
- package/src/LaunchProteinView/utils/launchViewUtils.ts +1 -1
- package/src/LaunchProteinView/utils/translateTranscripts.test.ts +63 -0
- package/src/LaunchProteinView/utils/util.ts +26 -92
- package/src/LaunchProteinViewExtensionPoint/index.ts +9 -5
- package/src/LaunchProteinViewExtensionPoint/resolveShortLaunch.ts +9 -16
- package/src/Protein1DLinkage/linkage.ts +2 -1
- package/src/ProteinView/attachStructureInteractions.test.ts +126 -0
- package/src/ProteinView/attachStructureInteractions.ts +102 -0
- package/src/ProteinView/components/ChainSelect.tsx +2 -3
- package/src/ProteinView/components/HeaderStructureRow.tsx +115 -0
- package/src/ProteinView/components/ManualAlignmentDialog.tsx +2 -3
- package/src/ProteinView/components/ProteinAlignment.tsx +31 -33
- package/src/ProteinView/components/ProteinView.tsx +48 -12
- package/src/ProteinView/components/ProteinViewHeader.tsx +16 -24
- package/src/ProteinView/components/TrackLegend.tsx +68 -0
- package/src/ProteinView/geneExplorerLinkage.test.ts +1 -2
- package/src/ProteinView/hooks/layoutFeature.test.ts +1 -4
- package/src/ProteinView/hooks/useProteinFeatureTrackData.ts +3 -2
- package/src/ProteinView/hooks/useStructureUniProt.ts +7 -4
- package/src/ProteinView/hooks/useUniProtFeatures.ts +1 -2
- package/src/ProteinView/labelSeqIdIntegration.test.ts +4 -5
- package/src/ProteinView/loadStructureData.ts +4 -5
- package/src/ProteinView/model.test.ts +165 -0
- package/src/ProteinView/model.ts +143 -78
- package/src/ProteinView/proteinToGenomeMapping.ts +38 -4
- package/src/ProteinView/proteinViewSpec.ts +7 -7
- package/src/ProteinView/removeStructure.ts +37 -0
- package/src/ProteinView/residueTracks.test.ts +11 -0
- package/src/ProteinView/residueTracks.ts +13 -0
- package/src/ProteinView/showLoading.ts +3 -1
- package/src/ProteinView/storedSettings.ts +9 -3
- package/src/ProteinView/structureLoader.test.ts +211 -11
- package/src/ProteinView/structureLoader.ts +88 -7
- package/src/ProteinView/structureModel.test.ts +48 -6
- package/src/ProteinView/structureModel.ts +161 -165
- package/src/ProteinView/structurePipeline.ts +1 -1
- package/src/ProteinView/util.test.ts +1 -39
- package/src/ProteinView/util.ts +9 -4
- package/src/ProteinView/withStoredSettings.test.ts +23 -5
- package/src/mappings.test.ts +2 -182
- package/src/mappings.ts +4 -230
- package/src/version.ts +1 -1
- package/dist/LaunchProteinView/components/AlignmentSettingsButton.js +0 -41
- package/dist/LaunchProteinView/components/Checkbox2.js +0 -12
- package/dist/LaunchProteinView/components/IsoformSequencesToggle.js +0 -13
- package/dist/LaunchProteinView/components/LaunchSettingsDialog.js +0 -23
- package/dist/LaunchProteinView/components/MSATable.js +0 -53
- package/dist/LaunchProteinView/hooks/useLocalStorage.js +0 -10
- package/dist/LaunchProteinView/services/alphaFoldModels.js +0 -49
- package/dist/LaunchProteinView/services/lookupMethods.js +0 -76
- package/dist/LaunchProteinView/services/pdbeBestStructures.js +0 -66
- package/dist/LaunchProteinView/utils/caCoordsToPdb.js +0 -90
- package/dist/LaunchProteinView/utils/geneticCodes.js +0 -227
- package/dist/LaunchProteinView/utils/isoformRanking.js +0 -85
- package/dist/LaunchProteinView/utils/structureUrls.js +0 -122
- package/dist/ProteinView/__fixtures__/structureFixtures.js +0 -34
- package/dist/ProteinView/alignmentQuality.js +0 -75
- package/dist/ProteinView/chooseMappedEntity.js +0 -133
- package/dist/ProteinView/coordinates.js +0 -57
- package/dist/ProteinView/extractPerResidueConfidence.js +0 -37
- package/dist/ProteinView/extractStructureSequences.js +0 -204
- package/dist/ProteinView/pairwiseAlignment.js +0 -327
- package/dist/ProteinView/pdbUniProtMapping.js +0 -217
- package/dist/ProteinView/structureFormat.js +0 -60
- package/dist/ProteinView/types.js +0 -22
- package/dist/fetchUtils.js +0 -36
- package/src/LaunchProteinView/components/AlignmentSettingsButton.tsx +0 -126
- package/src/LaunchProteinView/components/Checkbox2.tsx +0 -32
- package/src/LaunchProteinView/components/IsoformSequencesToggle.tsx +0 -41
- package/src/LaunchProteinView/components/LaunchSettingsDialog.tsx +0 -63
- package/src/LaunchProteinView/components/MSATable.tsx +0 -97
- package/src/LaunchProteinView/hooks/useLocalStorage.ts +0 -19
- package/src/LaunchProteinView/services/alphaFoldModels.test.ts +0 -76
- package/src/LaunchProteinView/services/alphaFoldModels.ts +0 -87
- package/src/LaunchProteinView/services/lookupMethods.ts +0 -144
- package/src/LaunchProteinView/services/pdbeBestStructures.test.ts +0 -72
- package/src/LaunchProteinView/services/pdbeBestStructures.ts +0 -87
- package/src/LaunchProteinView/utils/caCoordsToPdb.ts +0 -111
- package/src/LaunchProteinView/utils/geneticCodes.ts +0 -298
- package/src/LaunchProteinView/utils/isoformRanking.ts +0 -138
- package/src/LaunchProteinView/utils/pickStructureSequence.test.ts +0 -68
- package/src/LaunchProteinView/utils/selectBestTranscript.test.ts +0 -235
- package/src/LaunchProteinView/utils/stripStopCodon.test.ts +0 -23
- package/src/LaunchProteinView/utils/structureUrls.test.ts +0 -120
- package/src/LaunchProteinView/utils/structureUrls.ts +0 -158
- package/src/ProteinView/__fixtures__/structureFixtures.ts +0 -64
- package/src/ProteinView/alignmentQuality.test.ts +0 -101
- package/src/ProteinView/alignmentQuality.ts +0 -101
- package/src/ProteinView/chooseMappedEntity.test.ts +0 -224
- package/src/ProteinView/chooseMappedEntity.ts +0 -198
- package/src/ProteinView/coordinates.test.ts +0 -90
- package/src/ProteinView/coordinates.ts +0 -119
- package/src/ProteinView/extractPerResidueConfidence.test.ts +0 -83
- package/src/ProteinView/extractPerResidueConfidence.ts +0 -88
- package/src/ProteinView/extractStructureSequences.test.ts +0 -294
- package/src/ProteinView/extractStructureSequences.ts +0 -342
- package/src/ProteinView/pairwiseAlignment.test.ts +0 -101
- package/src/ProteinView/pairwiseAlignment.ts +0 -423
- package/src/ProteinView/pdbUniProtMapping.test.ts +0 -327
- package/src/ProteinView/pdbUniProtMapping.ts +0 -296
- package/src/ProteinView/structureFormat.test.ts +0 -73
- package/src/ProteinView/structureFormat.ts +0 -71
- package/src/ProteinView/types.ts +0 -30
- package/src/fetchUtils.test.ts +0 -27
- package/src/fetchUtils.ts +0 -49
- package/src/mappings.test.characterization.test.ts +0 -92
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// Copied from @jbrowse/core's featureTypes: the barrel export reads undefined
|
|
2
|
+
// on a v4 host, and the deep path is not in core's exports map.
|
|
3
|
+
const GENE_LIKE_TYPE = /gene(_segment)?$|rna$|transcript/;
|
|
4
|
+
export function isGeneLikeType(type) {
|
|
5
|
+
return type !== undefined && GENE_LIKE_TYPE.test(type.toLowerCase());
|
|
6
|
+
}
|
|
7
|
+
export function isCDS(feature) {
|
|
8
|
+
return feature.get('type')?.toLowerCase() === 'cds';
|
|
9
|
+
}
|
|
10
|
+
function hasDirectCDS(feature) {
|
|
11
|
+
return !!feature.get('subfeatures')?.some(isCDS);
|
|
12
|
+
}
|
|
13
|
+
// The transcripts the translator can read: the feature itself when its CDS
|
|
14
|
+
// records hang directly off it, else each gene-like child that carries them.
|
|
15
|
+
// One definition serves the menu gate, the isoform picker and the translator,
|
|
16
|
+
// so the menu never promises a protein the dialog cannot compute.
|
|
17
|
+
export function codingTranscripts(feature) {
|
|
18
|
+
return hasDirectCDS(feature)
|
|
19
|
+
? [feature]
|
|
20
|
+
: (feature.get('subfeatures') ?? []).filter(f => isGeneLikeType(f.get('type')) && hasDirectCDS(f));
|
|
21
|
+
}
|
|
22
|
+
export function isCodingFeature(feature) {
|
|
23
|
+
return codingTranscripts(feature).length > 0;
|
|
24
|
+
}
|
|
25
|
+
// The outermost gene-like ancestor, so a click on an isoform opens the dialog
|
|
26
|
+
// on the gene with every transcript to choose from, as the canvas host does.
|
|
27
|
+
export function geneLikeRoot(feature) {
|
|
28
|
+
let root = feature;
|
|
29
|
+
for (let parent = root.parent?.(); parent && isGeneLikeType(parent.get('type')); parent = parent.parent?.()) {
|
|
30
|
+
root = parent;
|
|
31
|
+
}
|
|
32
|
+
return root;
|
|
33
|
+
}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { ErrorMessage, LoadingEllipses } from '@jbrowse/core/ui';
|
|
3
|
-
import { DialogActions, DialogContent,
|
|
3
|
+
import { DialogActions, DialogContent, Typography } from '@mui/material';
|
|
4
4
|
import { observer } from 'mobx-react';
|
|
5
5
|
import { makeStyles } from 'tss-react/mui';
|
|
6
6
|
import AlphaFoldDBSearchStatus from './AlphaFoldDBSearchStatus';
|
|
7
|
-
import
|
|
7
|
+
import PartialFailureNotice from './PartialFailureNotice';
|
|
8
8
|
import ProteinViewActions from './ProteinViewActions';
|
|
9
9
|
import TranscriptSelector from './TranscriptSelector';
|
|
10
|
-
import
|
|
10
|
+
import UniProtLookupControls from './UniProtLookupControls';
|
|
11
11
|
import UniProtResultsTable from './UniProtResultsTable';
|
|
12
12
|
import ExternalLink from '../../components/ExternalLink';
|
|
13
13
|
import useAlphaFoldDBSearch from '../hooks/useAlphaFoldDBSearch';
|
|
@@ -27,16 +27,10 @@ const useStyles = makeStyles()({
|
|
|
27
27
|
gap: 20,
|
|
28
28
|
alignItems: 'flex-start',
|
|
29
29
|
},
|
|
30
|
-
endRow: {
|
|
31
|
-
display: 'flex',
|
|
32
|
-
flexDirection: 'row',
|
|
33
|
-
gap: 12,
|
|
34
|
-
alignItems: 'flex-start',
|
|
35
|
-
},
|
|
36
30
|
});
|
|
37
|
-
const AlphaFoldDBSearch = observer(function AlphaFoldDBSearch({ feature, session, view, handleClose,
|
|
31
|
+
const AlphaFoldDBSearch = observer(function AlphaFoldDBSearch({ feature, session, view, handleClose, lookup, sideBySide, onSideBySideChange, }) {
|
|
38
32
|
const { classes } = useStyles();
|
|
39
|
-
const state = useAlphaFoldDBSearch({ feature, view });
|
|
33
|
+
const state = useAlphaFoldDBSearch({ feature, view, lookup });
|
|
40
34
|
return (React.createElement(React.Fragment, null,
|
|
41
35
|
React.createElement(DialogContent, { className: classes.dialogContent },
|
|
42
36
|
state.error ? React.createElement(ErrorMessage, { error: state.error }) : null,
|
|
@@ -44,12 +38,9 @@ const AlphaFoldDBSearch = observer(function AlphaFoldDBSearch({ feature, session
|
|
|
44
38
|
"AlphaFold DB has no model for ",
|
|
45
39
|
state.uniprotId,
|
|
46
40
|
". The PDB and Foldseek tabs may have a structure.")) : null,
|
|
47
|
-
React.createElement(
|
|
48
|
-
React.createElement(IdentifierSelector, { recognizedIds: state.recognizedIds, geneName: state.geneName, selectedId: state.selectedQueryId, onSelectedIdChange: state.setSelectedQueryId }),
|
|
49
|
-
React.createElement(TextField, { size: "small", label: "Organism (NCBI taxon)", helperText: "Scopes the gene-name search", value: state.taxonId, onChange: event => {
|
|
50
|
-
state.setTaxonId(event.target.value);
|
|
51
|
-
}, placeholder: String(state.effectiveTaxonId), slotProps: { inputLabel: { shrink: true } }, sx: { width: 180 } }))) : null }),
|
|
41
|
+
React.createElement(UniProtLookupControls, { lookup: lookup }),
|
|
52
42
|
state.loadingStatuses.map(status => (React.createElement(LoadingEllipses, { key: status, variant: "subtitle2", message: status }))),
|
|
43
|
+
React.createElement(PartialFailureNotice, { message: state.isoformPartialFailure }),
|
|
53
44
|
state.showUniprotResults && (React.createElement(React.Fragment, null,
|
|
54
45
|
React.createElement(Typography, { variant: "body2", color: "textSecondary" },
|
|
55
46
|
"Searched UniProt by ",
|
|
@@ -75,8 +66,8 @@ const AlphaFoldDBSearch = observer(function AlphaFoldDBSearch({ feature, session
|
|
|
75
66
|
state.uniprotId ? (React.createElement(React.Fragment, null,
|
|
76
67
|
React.createElement("div", { className: classes.selectorsRow },
|
|
77
68
|
React.createElement(TranscriptSelector, { val: state.userSelection, setVal: state.setUserSelection, structureSequence: state.structureSequence, feature: feature, isoforms: state.transcriptOptions, isoformSequences: state.isoformSequences })),
|
|
78
|
-
React.createElement(AlphaFoldDBSearchStatus, { uniprotId: state.modelAccession ?? state.uniprotId,
|
|
69
|
+
React.createElement(AlphaFoldDBSearchStatus, { uniprotId: state.modelAccession ?? state.uniprotId, url: state.url }))) : null),
|
|
79
70
|
React.createElement(DialogActions, null,
|
|
80
|
-
React.createElement(ProteinViewActions, { handleClose: handleClose, uniprotId: state.modelAccession ?? state.uniprotId, userSelectedProteinSequence: state.userSelectedProteinSequence, selectedTranscript: state.selectedTranscript, url: state.url, confidenceUrl: state.confidenceUrl, feature: feature, view: view, session: session,
|
|
71
|
+
React.createElement(ProteinViewActions, { handleClose: handleClose, uniprotId: state.modelAccession ?? state.uniprotId, userSelectedProteinSequence: state.userSelectedProteinSequence, selectedTranscript: state.selectedTranscript, url: state.url, confidenceUrl: state.confidenceUrl, feature: feature, view: view, session: session, sideBySide: sideBySide, onSideBySideChange: onSideBySideChange, sequencesMatch: state.sequencesMatch, isLoading: state.isLoading, error: state.error }))));
|
|
81
72
|
});
|
|
82
73
|
export default AlphaFoldDBSearch;
|
|
@@ -1,17 +1,14 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { Typography } from '@mui/material';
|
|
3
|
-
import
|
|
3
|
+
import { uniprotEntryUrl } from 'p2s_mapper';
|
|
4
4
|
import ExternalLink from '../../components/ExternalLink';
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
React.createElement(
|
|
14
|
-
"AlphaFoldDB link: ",
|
|
15
|
-
React.createElement(ExternalLink, { href: url }, url))),
|
|
16
|
-
React.createElement(IsoformSequencesToggle, { structureSequence: structureSequence, structureName: uniprotId, isoformSequences: isoformSequences })));
|
|
5
|
+
export default function AlphaFoldDBSearchStatus({ uniprotId, url, }) {
|
|
6
|
+
return (React.createElement("div", null,
|
|
7
|
+
React.createElement(Typography, null,
|
|
8
|
+
"UniProt link:",
|
|
9
|
+
' ',
|
|
10
|
+
React.createElement(ExternalLink, { href: uniprotEntryUrl(uniprotId) }, uniprotId)),
|
|
11
|
+
React.createElement(Typography, null,
|
|
12
|
+
"AlphaFoldDB link: ",
|
|
13
|
+
React.createElement(ExternalLink, { href: url }, url))));
|
|
17
14
|
}
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import React, { useState } from 'react';
|
|
2
2
|
import { ErrorMessage } from '@jbrowse/core/ui';
|
|
3
3
|
import { Button, Menu, MenuItem } from '@mui/material';
|
|
4
|
+
import { caCoordsToPdb, getConfidenceUrlFromTarget, getUniprotIdFromAlphaFoldTarget, hasValidCaCoords, } from 'p2s_mapper';
|
|
4
5
|
import { useSafeLaunch } from '../hooks/useSafeLaunch';
|
|
5
|
-
import { caCoordsToPdb, hasValidCaCoords } from '../utils/caCoordsToPdb';
|
|
6
6
|
import { PROTEIN_LAUNCH_LABELS, getConditionalProteinLaunches, launch3DProteinView, } from '../utils/launchViewUtils';
|
|
7
|
-
import { getConfidenceUrlFromTarget, getUniprotIdFromAlphaFoldTarget, } from '../utils/structureUrls';
|
|
8
7
|
export default function FoldseekActionMenu({ hit, session, view, feature, selectedTranscript, userProvidedTranscriptSequence, onClose, }) {
|
|
9
8
|
const [anchorEl, setAnchorEl] = useState(null);
|
|
10
9
|
const open = Boolean(anchorEl);
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { Paper, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, Typography, } from '@mui/material';
|
|
2
|
+
import { Paper, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, Tooltip, Typography, } from '@mui/material';
|
|
3
|
+
import { getStructureUrlFromTarget } from 'p2s_mapper';
|
|
3
4
|
import { makeStyles } from 'tss-react/mui';
|
|
4
5
|
import FoldseekActionMenu from './FoldseekActionMenu';
|
|
5
|
-
import { getStructureUrlFromTarget } from '../utils/structureUrls';
|
|
6
6
|
const useStyles = makeStyles()(theme => ({
|
|
7
7
|
root: {
|
|
8
8
|
display: 'flex',
|
|
@@ -56,10 +56,16 @@ export default function FoldseekResultsTable({ results, session, view, feature,
|
|
|
56
56
|
React.createElement(TableCell, { className: classes.headerCell }, "Database"),
|
|
57
57
|
React.createElement(TableCell, { className: classes.headerCell }, "Target"),
|
|
58
58
|
React.createElement(TableCell, { className: classes.headerCell }, "Organism"),
|
|
59
|
-
React.createElement(TableCell, { className: classes.headerCell },
|
|
60
|
-
|
|
59
|
+
React.createElement(TableCell, { className: classes.headerCell },
|
|
60
|
+
React.createElement(Tooltip, { title: "Foldseek's estimated probability that the hit is a true structural homolog" },
|
|
61
|
+
React.createElement("span", null, "Prob"))),
|
|
62
|
+
React.createElement(TableCell, { className: classes.headerCell },
|
|
63
|
+
React.createElement(Tooltip, { title: "Percentage of aligned residues whose amino acid is identical; structural hits are often low here" },
|
|
64
|
+
React.createElement("span", null, "Seq. Id."))),
|
|
61
65
|
React.createElement(TableCell, { className: classes.headerCell }, "Coverage"),
|
|
62
|
-
React.createElement(TableCell, { className: classes.headerCell },
|
|
66
|
+
React.createElement(TableCell, { className: classes.headerCell },
|
|
67
|
+
React.createElement(Tooltip, { title: "Hits of this score expected by chance in a database this size; smaller is stronger" },
|
|
68
|
+
React.createElement("span", null, "E-value"))),
|
|
63
69
|
React.createElement(TableCell, { className: classes.headerCell }, "Actions"))),
|
|
64
70
|
React.createElement(TableBody, null, flatHits.map((hit, idx) => (React.createElement(TableRow, { key: `${hit.db}-${hit.target}-${idx}` },
|
|
65
71
|
React.createElement(TableCell, null, hit.db),
|
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
import React, { useState } from 'react';
|
|
2
2
|
import { ErrorMessage, LoadingEllipses } from '@jbrowse/core/ui';
|
|
3
|
-
import { Button, DialogActions, DialogContent, TextField, Typography, } from '@mui/material';
|
|
3
|
+
import { Button, DialogActions, DialogContent, Link, TextField, Typography, } from '@mui/material';
|
|
4
4
|
import { observer } from 'mobx-react';
|
|
5
|
+
import { stripAllStopCodons } from 'p2s_mapper';
|
|
5
6
|
import { makeStyles } from 'tss-react/mui';
|
|
6
7
|
import FoldseekDatabaseSelector from './FoldseekDatabaseSelector';
|
|
7
8
|
import FoldseekResultsTable from './FoldseekResultsTable';
|
|
9
|
+
import PartialFailureNotice from './PartialFailureNotice';
|
|
8
10
|
import TranscriptSelector from './TranscriptSelector';
|
|
9
11
|
import useFoldseekSearch from '../hooks/useFoldseekSearch';
|
|
10
12
|
import useTranscriptIsoformSelection from '../hooks/useTranscriptIsoformSelection';
|
|
11
13
|
import { DEFAULT_DATABASES } from '../services/foldseekApi';
|
|
12
|
-
import { stripAllStopCodons } from '../utils/util';
|
|
13
14
|
const useStyles = makeStyles()({
|
|
14
15
|
dialogContent: {
|
|
15
16
|
width: '80em',
|
|
@@ -24,22 +25,23 @@ const useStyles = makeStyles()({
|
|
|
24
25
|
display: 'flex',
|
|
25
26
|
flexDirection: 'column',
|
|
26
27
|
gap: 8,
|
|
28
|
+
alignItems: 'flex-start',
|
|
27
29
|
},
|
|
28
30
|
});
|
|
29
31
|
const FoldseekSearch = observer(function FoldseekSearch({ feature, session, view, handleClose, }) {
|
|
30
32
|
const { classes } = useStyles();
|
|
31
33
|
const [userEditedSequence, setUserEditedSequence] = useState();
|
|
32
34
|
const [selectedDatabases, setSelectedDatabases] = useState(DEFAULT_DATABASES);
|
|
33
|
-
const
|
|
34
|
-
const {
|
|
35
|
+
const [show3Di, setShow3Di] = useState(false);
|
|
36
|
+
const { results, cleanedAaSequence, di3Sequence, isLoading, isPredicting, error, statusMessage, predictStructure, search, cancel, reset, } = useFoldseekSearch();
|
|
37
|
+
const { transcripts, isoformSequences, isLoading: isLoadingIsoforms, error: isoformError, partialFailure: isoformPartialFailure, selectedTranscriptId: effectiveSelectedTranscriptId, setSelectedTranscriptId: setUserSelection, selectedTranscript, selectedIsoform: selectedIsoformData, } = useTranscriptIsoformSelection({ feature, view });
|
|
35
38
|
const cleanedSequence = selectedIsoformData
|
|
36
39
|
? stripAllStopCodons(selectedIsoformData.seq)
|
|
37
40
|
: '';
|
|
38
41
|
const sequence = userEditedSequence ?? cleanedSequence;
|
|
39
42
|
// Any change to the input sequence makes an existing 3Di prediction (and any
|
|
40
|
-
// results derived from it) stale
|
|
41
|
-
//
|
|
42
|
-
// sequence after the user switches transcript or edits the residues.
|
|
43
|
+
// results derived from it) stale, so it goes back to being predicted on the
|
|
44
|
+
// next search rather than a search running against the old residues.
|
|
43
45
|
const invalidatePrediction = () => {
|
|
44
46
|
if (di3Sequence !== undefined || results !== undefined) {
|
|
45
47
|
reset();
|
|
@@ -50,17 +52,24 @@ const FoldseekSearch = observer(function FoldseekSearch({ feature, session, view
|
|
|
50
52
|
setUserEditedSequence(undefined);
|
|
51
53
|
invalidatePrediction();
|
|
52
54
|
};
|
|
53
|
-
const canPredict = sequence.trim().length > 0 && !isPredicting && !isLoading;
|
|
54
|
-
const canSearch = !!cleanedAaSequence &&
|
|
55
|
-
!!di3Sequence &&
|
|
56
|
-
selectedDatabases.length > 0 &&
|
|
57
|
-
!isLoading;
|
|
58
|
-
const combinedError = error ?? isoformError;
|
|
59
55
|
const isBusy = isLoading || isPredicting;
|
|
56
|
+
const canSearch = sequence.trim().length > 0 && selectedDatabases.length > 0 && !isBusy;
|
|
57
|
+
// One button: predicting the 3Di alphabet is a step of the search, not a
|
|
58
|
+
// decision, and making the user click twice only invited a stale prediction.
|
|
59
|
+
const runSearch = async () => {
|
|
60
|
+
const predicted = cleanedAaSequence && di3Sequence
|
|
61
|
+
? { aaSequence: cleanedAaSequence, di3Sequence }
|
|
62
|
+
: await predictStructure(sequence.trim());
|
|
63
|
+
if (predicted) {
|
|
64
|
+
await search(predicted.aaSequence, predicted.di3Sequence, selectedDatabases);
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
const combinedError = error ?? isoformError;
|
|
60
68
|
return (React.createElement(React.Fragment, null,
|
|
61
69
|
React.createElement(DialogContent, { className: classes.dialogContent },
|
|
62
70
|
combinedError && !isLoadingIsoforms ? (React.createElement(ErrorMessage, { error: combinedError })) : null,
|
|
63
71
|
isLoadingIsoforms ? (React.createElement(LoadingEllipses, { variant: "subtitle2", message: "Loading transcript sequences" })) : null,
|
|
72
|
+
React.createElement(PartialFailureNotice, { message: isoformPartialFailure }),
|
|
64
73
|
isoformSequences ? (React.createElement(React.Fragment, null,
|
|
65
74
|
React.createElement(TranscriptSelector, { val: effectiveSelectedTranscriptId, setVal: setUserSelectionWithReset, isoforms: transcripts, isoformSequences: isoformSequences, feature: feature, disabled: isBusy }),
|
|
66
75
|
React.createElement(TextField, { label: "Protein sequence (amino acids)", multiline: true, rows: 4, value: sequence, onChange: e => {
|
|
@@ -70,24 +79,32 @@ const FoldseekSearch = observer(function FoldseekSearch({ feature, session, view
|
|
|
70
79
|
input: { className: classes.sequenceInput },
|
|
71
80
|
} }))) : null,
|
|
72
81
|
di3Sequence ? (React.createElement("div", { className: classes.di3Section },
|
|
73
|
-
React.createElement(
|
|
74
|
-
|
|
82
|
+
React.createElement(Link, { component: "button", type: "button", variant: "body2", onClick: () => {
|
|
83
|
+
setShow3Di(!show3Di);
|
|
84
|
+
} }, show3Di ? 'Hide 3Di' : 'Show 3Di'),
|
|
85
|
+
show3Di ? (React.createElement(TextField, { label: "3Di structural alphabet (what the search runs on)", multiline: true, rows: 4, fullWidth: true, value: di3Sequence, slotProps: {
|
|
75
86
|
input: { className: classes.sequenceInput, readOnly: true },
|
|
76
|
-
} }))) : null,
|
|
87
|
+
} })) : null)) : null,
|
|
77
88
|
React.createElement(FoldseekDatabaseSelector, { selected: selectedDatabases, onChange: setSelectedDatabases, disabled: isBusy }),
|
|
78
89
|
statusMessage ? (React.createElement(LoadingEllipses, { variant: "subtitle2", message: statusMessage })) : null,
|
|
79
|
-
results ? (React.createElement(FoldseekResultsTable, { results: results, session: session, view: view, feature: feature, selectedTranscript: selectedTranscript, userProvidedTranscriptSequence: selectedIsoformData?.seq, onClose: handleClose })) : null
|
|
90
|
+
results ? (React.createElement(FoldseekResultsTable, { results: results, session: session, view: view, feature: feature, selectedTranscript: selectedTranscript, userProvidedTranscriptSequence: selectedIsoformData?.seq, onClose: handleClose })) : null,
|
|
91
|
+
React.createElement(Typography, { variant: "body2", color: "textSecondary" }, "Searching sends the protein sequence above to the foldseek.com servers, which predict its 3Di alphabet and run the structure search.")),
|
|
80
92
|
React.createElement(DialogActions, null,
|
|
81
93
|
React.createElement(Button, { variant: "contained", color: "secondary", onClick: () => {
|
|
82
94
|
handleClose();
|
|
83
|
-
} }, "
|
|
95
|
+
} }, "Close"),
|
|
96
|
+
isBusy ? (React.createElement(Button, { variant: "outlined", onClick: () => {
|
|
97
|
+
cancel();
|
|
98
|
+
} }, "Cancel search")) : null,
|
|
84
99
|
results ? (React.createElement(Button, { variant: "outlined", onClick: () => {
|
|
85
100
|
reset();
|
|
86
101
|
} }, "New search")) : null,
|
|
87
|
-
|
|
88
|
-
void
|
|
89
|
-
} }, isPredicting
|
|
90
|
-
|
|
91
|
-
|
|
102
|
+
React.createElement(Button, { variant: "contained", color: "primary", disabled: !canSearch, onClick: () => {
|
|
103
|
+
void runSearch();
|
|
104
|
+
} }, isPredicting
|
|
105
|
+
? 'Predicting 3Di...'
|
|
106
|
+
: isLoading
|
|
107
|
+
? 'Searching...'
|
|
108
|
+
: 'Search Foldseek'))));
|
|
92
109
|
});
|
|
93
110
|
export default FoldseekSearch;
|
|
@@ -1,21 +1,32 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { Dialog } from '@jbrowse/core/ui';
|
|
3
3
|
import { Button, DialogActions, DialogContent, Divider, Typography, } from '@mui/material';
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
}
|
|
4
|
+
import ExternalLink from '../../components/ExternalLink';
|
|
5
|
+
const ISSUES_URL = 'https://github.com/GMOD/jbrowse-plugin-protein3d/issues';
|
|
7
6
|
export default function HelpDialog({ handleClose, }) {
|
|
8
7
|
return (React.createElement(Dialog, { open: true, maxWidth: "lg", onClose: handleClose, title: "Help" },
|
|
9
8
|
React.createElement(DialogContent, null,
|
|
10
|
-
React.createElement(
|
|
11
|
-
|
|
9
|
+
React.createElement(Typography, { sx: { mb: 2 } }, "Each tab finds a structure a different way. All of them end in the same place: the plugin aligns the structure's residues to the protein sequence it translates from the transcript you pick, and that alignment maps genome coordinates onto positions in the 3D view."),
|
|
10
|
+
React.createElement(Typography, { component: "div" },
|
|
12
11
|
React.createElement("ul", null,
|
|
13
|
-
React.createElement("li", null,
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
React.createElement("li", null,
|
|
17
|
-
|
|
18
|
-
|
|
12
|
+
React.createElement("li", null,
|
|
13
|
+
React.createElement("b", null, "AlphaFoldDB search"),
|
|
14
|
+
" resolves the feature to a UniProt accession \u2014 from its own identifiers, or one you type \u2014 and opens AlphaFold's predicted model for it."),
|
|
15
|
+
React.createElement("li", null,
|
|
16
|
+
React.createElement("b", null, "PDB search"),
|
|
17
|
+
" lists the experimental structures PDBe maps to that accession, ranked on coverage and resolution. A crystal is usually one domain, often with binding partners, so the view picks the chain the transcript belongs to once the structure loads."),
|
|
18
|
+
React.createElement("li", null,
|
|
19
|
+
React.createElement("b", null, "Foldseek search"),
|
|
20
|
+
" sends the protein sequence to the foldseek.com servers and lists structures similar in shape, including ones with little sequence similarity."),
|
|
21
|
+
React.createElement("li", null,
|
|
22
|
+
React.createElement("b", null, "File or URL"),
|
|
23
|
+
" opens a structure you already have \u2014 the output of ColabFold or another modelling tool, or any PDB/mmCIF file reachable by URL."))),
|
|
24
|
+
React.createElement(Typography, { sx: { mb: 2 } }, "The isoform list marks which transcripts match the structure's residues exactly, and counts identical residues for the rest. You do not need an exact match; the alignment absorbs the differences between the two representations."),
|
|
25
|
+
React.createElement(Typography, null,
|
|
26
|
+
"If a gene will not resolve, or something looks wrong, please open an issue at",
|
|
27
|
+
' ',
|
|
28
|
+
React.createElement(ExternalLink, { href: ISSUES_URL }, "github.com/GMOD/jbrowse-plugin-protein3d"),
|
|
29
|
+
".")),
|
|
19
30
|
React.createElement(Divider, null),
|
|
20
31
|
React.createElement(DialogActions, null,
|
|
21
32
|
React.createElement(Button, { onClick: handleClose, color: "primary" }, "Close"))));
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React, { useState } from 'react';
|
|
2
2
|
import { Button, FormControl, InputLabel, MenuItem, Select, } from '@mui/material';
|
|
3
|
-
import { getDbIdLabel } from '
|
|
3
|
+
import { getDbIdLabel } from 'p2s_mapper';
|
|
4
4
|
export default function IdentifierSelector({ recognizedIds, geneName, selectedId, onSelectedIdChange, }) {
|
|
5
5
|
const [expanded, setExpanded] = useState(false);
|
|
6
6
|
// Build list of selectable options
|
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { ListItemText, Menu, MenuItem } from '@mui/material';
|
|
3
|
-
export default function LaunchOptionsMenu({ anchorEl, onClose, options, }) {
|
|
4
|
-
return (React.createElement(Menu, { anchorEl: anchorEl, open: Boolean(anchorEl), onClose: onClose, "data-testid": "protein-launch-options-menu" },
|
|
5
|
-
React.createElement(
|
|
2
|
+
import { Checkbox, Divider, ListItemText, Menu, MenuItem } from '@mui/material';
|
|
3
|
+
export default function LaunchOptionsMenu({ anchorEl, onClose, options, sideBySide, onSideBySideChange, }) {
|
|
4
|
+
return (React.createElement(Menu, { anchorEl: anchorEl, open: Boolean(anchorEl), onClose: onClose, "data-testid": "protein-launch-options-menu" },
|
|
5
|
+
options.map(opt => (React.createElement(MenuItem, { key: opt.key, "data-testid": `protein-launch-option-${opt.key}`, onClick: opt.onClick },
|
|
6
|
+
React.createElement(ListItemText, { primary: opt.title, secondary: opt.description })))),
|
|
7
|
+
React.createElement(Divider, null),
|
|
8
|
+
React.createElement(MenuItem, { "data-testid": "protein-launch-side-by-side", onClick: () => {
|
|
9
|
+
onSideBySideChange(!sideBySide);
|
|
10
|
+
} },
|
|
11
|
+
React.createElement(Checkbox, { checked: sideBySide, size: "small" }),
|
|
12
|
+
React.createElement(ListItemText, { primary: "Open side by side", secondary: "Place the protein view right of the genome view instead of below it" }))));
|
|
6
13
|
}
|
|
@@ -8,13 +8,23 @@ import HelpButton from './HelpButton';
|
|
|
8
8
|
import PdbSearch from './PdbSearch';
|
|
9
9
|
import TabPanel from './TabPanel';
|
|
10
10
|
import UserProvidedStructure from './UserProvidedStructure';
|
|
11
|
-
import
|
|
12
|
-
import {
|
|
11
|
+
import useUniProtIdLookup from '../hooks/useUniProtIdLookup';
|
|
12
|
+
import { getLaunchSideBySide, setLaunchSideBySide } from '../utils/sideBySide';
|
|
13
13
|
export default function LaunchProteinViewDialog({ handleClose, feature, model, }) {
|
|
14
14
|
const [choice, setChoice] = useState(0);
|
|
15
|
-
const [alignmentAlgorithm, setAlignmentAlgorithm] = useLocalStorage('jbrowse-protein3d-alignment-algorithm', DEFAULT_ALIGNMENT_ALGORITHM);
|
|
16
15
|
const session = getSession(model);
|
|
17
16
|
const view = getContainingView(model);
|
|
17
|
+
// One lookup for the whole dialog: the tabs stay mounted once visited, so a
|
|
18
|
+
// lookup per tab meant the same UniProt search ran twice and a row picked on
|
|
19
|
+
// one tab left the other pointing at a different gene.
|
|
20
|
+
const lookup = useUniProtIdLookup({ feature, view });
|
|
21
|
+
// Also the dialog's, for the same reason: a tab that has been mounted since
|
|
22
|
+
// before the user changed this would otherwise launch with its own stale copy.
|
|
23
|
+
const [sideBySide, setSideBySide] = useState(() => getLaunchSideBySide());
|
|
24
|
+
const changeSideBySide = (value) => {
|
|
25
|
+
setSideBySide(value);
|
|
26
|
+
setLaunchSideBySide(value);
|
|
27
|
+
};
|
|
18
28
|
return (React.createElement(Dialog, { "data-testid": "launch-protein-view-dialog", maxWidth: "xl", title: "Launch protein view", titleNode: React.createElement(React.Fragment, null,
|
|
19
29
|
"Launch protein view ",
|
|
20
30
|
React.createElement(HelpButton, null)), open: true, onClose: handleClose },
|
|
@@ -24,13 +34,13 @@ export default function LaunchProteinViewDialog({ handleClose, feature, model, }
|
|
|
24
34
|
React.createElement(Tab, { value: 0, label: "AlphaFoldDB search" }),
|
|
25
35
|
React.createElement(Tab, { value: 1, label: "PDB search" }),
|
|
26
36
|
React.createElement(Tab, { value: 2, label: "Foldseek search" }),
|
|
27
|
-
React.createElement(Tab, { value: 3, label: "
|
|
37
|
+
React.createElement(Tab, { value: 3, label: "File or URL" })),
|
|
28
38
|
React.createElement(TabPanel, { value: choice, index: 0 },
|
|
29
|
-
React.createElement(AlphaFoldDBSearch, { session: session, view: view, feature: feature, handleClose: handleClose,
|
|
39
|
+
React.createElement(AlphaFoldDBSearch, { session: session, view: view, feature: feature, handleClose: handleClose, lookup: lookup, sideBySide: sideBySide, onSideBySideChange: changeSideBySide })),
|
|
30
40
|
React.createElement(TabPanel, { value: choice, index: 1 },
|
|
31
|
-
React.createElement(PdbSearch, { session: session, view: view, feature: feature, handleClose: handleClose,
|
|
41
|
+
React.createElement(PdbSearch, { session: session, view: view, feature: feature, handleClose: handleClose, lookup: lookup, sideBySide: sideBySide, onSideBySideChange: changeSideBySide })),
|
|
32
42
|
React.createElement(TabPanel, { value: choice, index: 2 },
|
|
33
43
|
React.createElement(FoldseekSearch, { session: session, view: view, feature: feature, handleClose: handleClose })),
|
|
34
44
|
React.createElement(TabPanel, { value: choice, index: 3 },
|
|
35
|
-
React.createElement(UserProvidedStructure, { session: session, view: view, feature: feature, handleClose: handleClose
|
|
45
|
+
React.createElement(UserProvidedStructure, { session: session, view: view, feature: feature, handleClose: handleClose }))));
|
|
36
46
|
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Typography } from '@mui/material';
|
|
3
|
+
// What came back short of what was asked for: identifiers a UniProt outage
|
|
4
|
+
// swallowed, transcripts with no protein sequence. Not an error — the dialog
|
|
5
|
+
// still works — but not silence either, which is what makes 18 isoforms of a
|
|
6
|
+
// 20-isoform gene read as the whole gene.
|
|
7
|
+
export default function PartialFailureNotice({ message, }) {
|
|
8
|
+
return message ? (React.createElement(Typography, { variant: "body2", color: "warning.main" }, message)) : null;
|
|
9
|
+
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { Paper, Radio, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, Typography, } from '@mui/material';
|
|
2
|
+
import { Paper, Radio, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, Tooltip, Typography, } from '@mui/material';
|
|
3
|
+
import { rcsbEntryUrl } from 'p2s_mapper';
|
|
3
4
|
import { makeStyles } from 'tss-react/mui';
|
|
4
5
|
import ExternalLink from '../../components/ExternalLink';
|
|
5
|
-
import { rcsbEntryUrl } from '../services/pdbeBestStructures';
|
|
6
6
|
const useStyles = makeStyles()(theme => ({
|
|
7
7
|
tableContainer: {
|
|
8
8
|
maxHeight: 300,
|
|
@@ -42,7 +42,9 @@ export default function PdbResultsTable({ entries, selectedPdbId, onSelect, }) {
|
|
|
42
42
|
React.createElement(TableCell, { className: classes.headerCell }, "PDB ID"),
|
|
43
43
|
React.createElement(TableCell, { className: classes.headerCell }, "Method"),
|
|
44
44
|
React.createElement(TableCell, { className: classes.headerCell }, "Resolution"),
|
|
45
|
-
React.createElement(TableCell, { className: classes.headerCell },
|
|
45
|
+
React.createElement(TableCell, { className: classes.headerCell },
|
|
46
|
+
React.createElement(Tooltip, { title: "The span of the UniProt sequence this entry resolves, by SIFTS. A crystal is usually one domain rather than the whole protein." },
|
|
47
|
+
React.createElement("span", null, "UniProt residues"))),
|
|
46
48
|
React.createElement(TableCell, { className: classes.headerCell }, "Coverage"),
|
|
47
49
|
React.createElement(TableCell, { className: classes.headerCell }, "Chains"))),
|
|
48
50
|
React.createElement(TableBody, null, shown.map(entry => {
|
|
@@ -2,18 +2,19 @@ import React, { useState } from 'react';
|
|
|
2
2
|
import { ErrorMessage, LoadingEllipses } from '@jbrowse/core/ui';
|
|
3
3
|
import { DialogActions, DialogContent, TextField, Typography, } from '@mui/material';
|
|
4
4
|
import { observer } from 'mobx-react';
|
|
5
|
+
import { getPdbStructureUrl, isPdbId, uniprotEntryUrl } from 'p2s_mapper';
|
|
5
6
|
import { makeStyles } from 'tss-react/mui';
|
|
6
|
-
import
|
|
7
|
+
import PartialFailureNotice from './PartialFailureNotice';
|
|
7
8
|
import PdbResultsTable from './PdbResultsTable';
|
|
8
9
|
import ProteinViewActions from './ProteinViewActions';
|
|
9
10
|
import TranscriptSelector from './TranscriptSelector';
|
|
10
|
-
import
|
|
11
|
+
import UniProtLookupControls from './UniProtLookupControls';
|
|
11
12
|
import UniProtResultsTable from './UniProtResultsTable';
|
|
12
13
|
import ExternalLink from '../../components/ExternalLink';
|
|
14
|
+
import useDebouncedValue from '../hooks/useDebouncedValue';
|
|
13
15
|
import usePdbBestStructures from '../hooks/usePdbBestStructures';
|
|
16
|
+
import usePdbEntryMolecules from '../hooks/usePdbEntryMolecules';
|
|
14
17
|
import useTranscriptIsoformSelection from '../hooks/useTranscriptIsoformSelection';
|
|
15
|
-
import useUniProtIdLookup from '../hooks/useUniProtIdLookup';
|
|
16
|
-
import { getPdbStructureUrl, uniprotEntryUrl } from '../utils/structureUrls';
|
|
17
18
|
const useStyles = makeStyles()({
|
|
18
19
|
dialogContent: {
|
|
19
20
|
width: '80em',
|
|
@@ -24,12 +25,6 @@ const useStyles = makeStyles()({
|
|
|
24
25
|
marginBottom: 0,
|
|
25
26
|
},
|
|
26
27
|
},
|
|
27
|
-
endRow: {
|
|
28
|
-
display: 'flex',
|
|
29
|
-
flexDirection: 'row',
|
|
30
|
-
gap: 12,
|
|
31
|
-
alignItems: 'flex-start',
|
|
32
|
-
},
|
|
33
28
|
});
|
|
34
29
|
// Experimental structures of the gene's protein, found through SIFTS: PDBe
|
|
35
30
|
// lists every entry mapped to the UniProt accession, ranked on coverage and
|
|
@@ -37,16 +32,42 @@ const useStyles = makeStyles()({
|
|
|
37
32
|
// A crystal is usually a fragment, often with partners, so the view aligns
|
|
38
33
|
// the transcript to it after launch rather than expecting a sequence match
|
|
39
34
|
// here.
|
|
40
|
-
const PdbSearch = observer(function PdbSearch({ feature, session, view, handleClose,
|
|
35
|
+
const PdbSearch = observer(function PdbSearch({ feature, session, view, handleClose, lookup, sideBySide, onSideBySideChange, }) {
|
|
41
36
|
const { classes } = useStyles();
|
|
42
|
-
const lookup = useUniProtIdLookup({ feature, view });
|
|
43
37
|
const { uniprotId, isAutoMode, isLookupLoading } = lookup;
|
|
44
38
|
const { entries, error: pdbError, isLoading: isPdbLoading, } = usePdbBestStructures(uniprotId);
|
|
45
39
|
const [userPdbId, setUserPdbId] = useState();
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
40
|
+
// A typed id reaches entries PDBe's SIFTS listing never offers: a structure
|
|
41
|
+
// of a complex filed under a partner, anything a paper names. Debounced, so
|
|
42
|
+
// the three characters on the way to four are not three fetches.
|
|
43
|
+
const [typedPdbId, setTypedPdbId] = useState('');
|
|
44
|
+
const trimmedTypedPdbId = typedPdbId.trim();
|
|
45
|
+
const debouncedTypedPdbId = useDebouncedValue(trimmedTypedPdbId, 400);
|
|
46
|
+
const typedPdbIdInvalid = trimmedTypedPdbId !== '' && !isPdbId(trimmedTypedPdbId);
|
|
47
|
+
const selectedPdbId = isPdbId(debouncedTypedPdbId)
|
|
48
|
+
? debouncedTypedPdbId.toLowerCase()
|
|
49
|
+
: userPdbId && entries?.some(e => e.pdbId === userPdbId)
|
|
50
|
+
? userPdbId
|
|
51
|
+
: entries?.[0]?.pdbId;
|
|
52
|
+
const structureUrl = selectedPdbId
|
|
53
|
+
? getPdbStructureUrl(selectedPdbId)
|
|
54
|
+
: undefined;
|
|
55
|
+
// The chosen entry's residues, so the isoform picker can say which transcript
|
|
56
|
+
// matches it — the same annotation the AlphaFold tab shows. It is a label and
|
|
57
|
+
// nothing more: Launch never waits on it, and its failure costs the label
|
|
58
|
+
// rather than the launch, which reads the structure file itself.
|
|
59
|
+
//
|
|
60
|
+
// While another entry's answer is in flight keepPreviousData still holds the
|
|
61
|
+
// last one, so isValidating withholds it rather than labelling these rows
|
|
62
|
+
// with the previous entry's chains.
|
|
63
|
+
const { sequences, isValidating: isMoleculesValidating } = usePdbEntryMolecules(selectedPdbId);
|
|
64
|
+
const structureSequences = isMoleculesValidating ? undefined : sequences;
|
|
65
|
+
const { transcripts, isoformSequences, structureSequence, isLoading: isIsoformLoading, error: isoformError, partialFailure: isoformPartialFailure, selectedTranscriptId, setSelectedTranscriptId, selectedTranscript, selectedIsoform, } = useTranscriptIsoformSelection({
|
|
66
|
+
feature,
|
|
67
|
+
view,
|
|
68
|
+
structureSequences,
|
|
69
|
+
resetKey: uniprotId,
|
|
70
|
+
});
|
|
50
71
|
const loadingStatuses = [
|
|
51
72
|
isLookupLoading && 'Looking up UniProt ID',
|
|
52
73
|
isIsoformLoading && 'Loading protein sequences from transcript isoforms',
|
|
@@ -59,12 +80,9 @@ const PdbSearch = observer(function PdbSearch({ feature, session, view, handleCl
|
|
|
59
80
|
return (React.createElement(React.Fragment, null,
|
|
60
81
|
React.createElement(DialogContent, { className: classes.dialogContent },
|
|
61
82
|
error ? React.createElement(ErrorMessage, { error: error }) : null,
|
|
62
|
-
React.createElement(
|
|
63
|
-
React.createElement(IdentifierSelector, { recognizedIds: lookup.recognizedIds, geneName: lookup.geneName, selectedId: lookup.selectedQueryId, onSelectedIdChange: lookup.setSelectedQueryId }),
|
|
64
|
-
React.createElement(TextField, { size: "small", label: "Organism (NCBI taxon)", helperText: "Scopes the gene-name search", value: lookup.taxonId, onChange: event => {
|
|
65
|
-
lookup.setTaxonId(event.target.value);
|
|
66
|
-
}, placeholder: String(lookup.effectiveTaxonId), slotProps: { inputLabel: { shrink: true } }, sx: { width: 180 } }))) : null }),
|
|
83
|
+
React.createElement(UniProtLookupControls, { lookup: lookup }),
|
|
67
84
|
loadingStatuses.map(status => (React.createElement(LoadingEllipses, { key: status, variant: "subtitle2", message: status }))),
|
|
85
|
+
React.createElement(PartialFailureNotice, { message: isoformPartialFailure }),
|
|
68
86
|
isAutoMode && lookup.uniprotEntries.length > 0 ? (React.createElement(React.Fragment, null,
|
|
69
87
|
React.createElement(Typography, { variant: "body2", color: "textSecondary" },
|
|
70
88
|
"Searched UniProt by ",
|
|
@@ -80,13 +98,18 @@ const PdbSearch = observer(function PdbSearch({ feature, session, view, handleCl
|
|
|
80
98
|
React.createElement(ExternalLink, { href: "https://www.uniprot.org/" }, "UniProt"),
|
|
81
99
|
' ',
|
|
82
100
|
"directly and use \"Enter manually\".")) : null,
|
|
101
|
+
React.createElement(TextField, { size: "small", label: "PDB ID", placeholder: "e.g. 1TUP", helperText: typedPdbIdInvalid
|
|
102
|
+
? 'A PDB ID is four characters beginning with a digit'
|
|
103
|
+
: 'Overrides the selection below', error: typedPdbIdInvalid, value: typedPdbId, onChange: event => {
|
|
104
|
+
setTypedPdbId(event.target.value);
|
|
105
|
+
}, slotProps: { inputLabel: { shrink: true } }, sx: { width: 240 } }),
|
|
83
106
|
uniprotId && entries && !isPdbLoading ? (entries.length > 0 ? (React.createElement(PdbResultsTable, { entries: entries, selectedPdbId: selectedPdbId, onSelect: setUserPdbId })) : (React.createElement(Typography, null,
|
|
84
107
|
"PDBe lists no experimental structure for",
|
|
85
108
|
' ',
|
|
86
109
|
React.createElement(ExternalLink, { href: uniprotEntryUrl(uniprotId) }, uniprotId),
|
|
87
110
|
". The AlphaFoldDB tab has a predicted one."))) : null,
|
|
88
|
-
isoformSequences && selectedTranscript ? (React.createElement(TranscriptSelector, { val: selectedTranscriptId, setVal: setSelectedTranscriptId, feature: feature, isoforms: transcripts, isoformSequences: isoformSequences })) : null),
|
|
111
|
+
isoformSequences && selectedTranscript ? (React.createElement(TranscriptSelector, { val: selectedTranscriptId, setVal: setSelectedTranscriptId, structureSequence: structureSequence, feature: feature, isoforms: transcripts, isoformSequences: isoformSequences })) : null),
|
|
89
112
|
React.createElement(DialogActions, null,
|
|
90
|
-
React.createElement(ProteinViewActions, { handleClose: handleClose, uniprotId: uniprotId, userSelectedProteinSequence: selectedIsoform, selectedTranscript: selectedTranscript, url:
|
|
113
|
+
React.createElement(ProteinViewActions, { handleClose: handleClose, uniprotId: uniprotId, userSelectedProteinSequence: selectedIsoform, selectedTranscript: selectedTranscript, url: structureUrl, pdbId: selectedPdbId, feature: feature, view: view, session: session, sideBySide: sideBySide, onSideBySideChange: onSideBySideChange, isLoading: isLoading, error: error }))));
|
|
91
114
|
});
|
|
92
115
|
export default PdbSearch;
|