jbrowse-plugin-msaview 3.4.1 → 3.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -0
- package/dist/AddHighlightModel/index.js +3 -3
- package/dist/BgzipFastaMsaAdapter/BgzipFastaMsaAdapter.d.ts +8 -2
- package/dist/BgzipFastaMsaAdapter/configSchema.d.ts +3 -3
- package/dist/BgzipFastaMsaAdapter/configSchema.js +1 -1
- package/dist/LaunchMsaView/codingFeature.d.ts +14 -0
- package/dist/LaunchMsaView/codingFeature.js +38 -0
- package/dist/LaunchMsaView/codingFeature.test.js +75 -0
- package/dist/LaunchMsaView/components/BlastQuery/BlastAutomaticPanel.d.ts +3 -1
- package/dist/LaunchMsaView/components/BlastQuery/BlastAutomaticPanel.js +29 -26
- package/dist/LaunchMsaView/components/BlastQuery/BlastManualPanel.d.ts +3 -1
- package/dist/LaunchMsaView/components/BlastQuery/BlastManualPanel.js +7 -2
- package/dist/LaunchMsaView/components/BlastQuery/BlastPanel.d.ts +7 -3
- package/dist/LaunchMsaView/components/BlastQuery/BlastPanel.js +8 -7
- package/dist/LaunchMsaView/components/BlastQuery/BlastSettingsDialog.js +2 -2
- package/dist/LaunchMsaView/components/BlastQuery/MsaAlgorithmSelect.js +5 -3
- package/dist/LaunchMsaView/components/BlastQuery/consts.d.ts +26 -4
- package/dist/LaunchMsaView/components/BlastQuery/consts.js +68 -2
- package/dist/LaunchMsaView/components/BlastQuery/searchChoiceStorage.d.ts +1 -1
- package/dist/LaunchMsaView/components/HelpButton.d.ts +2 -0
- package/dist/LaunchMsaView/components/HelpButton.js +17 -0
- package/dist/LaunchMsaView/components/HelpDialog.d.ts +4 -0
- package/dist/LaunchMsaView/components/HelpDialog.js +18 -0
- package/dist/LaunchMsaView/components/LaunchMsaViewDialog.d.ts +3 -1
- package/dist/LaunchMsaView/components/LaunchMsaViewDialog.js +21 -16
- package/dist/LaunchMsaView/components/ManualMSALoader/ManualMSALoader.d.ts +3 -1
- package/dist/LaunchMsaView/components/ManualMSALoader/ManualMSALoader.js +10 -4
- package/dist/LaunchMsaView/components/ManualMSALoader/launchView.d.ts +3 -1
- package/dist/LaunchMsaView/components/ManualMSALoader/launchView.js +2 -1
- package/dist/LaunchMsaView/components/OrthologQuery/OrthologPanel.d.ts +3 -1
- package/dist/LaunchMsaView/components/OrthologQuery/OrthologPanel.js +21 -7
- package/dist/LaunchMsaView/components/OrthologQuery/OrthologSourceSelect.js +4 -1
- package/dist/LaunchMsaView/components/PreLoadedMSA/PreLoadedMSADataPanel.d.ts +3 -1
- package/dist/LaunchMsaView/components/PreLoadedMSA/PreLoadedMSADataPanel.js +33 -15
- package/dist/LaunchMsaView/components/PreLoadedMSA/preCalculatedLaunchView.d.ts +3 -1
- package/dist/LaunchMsaView/components/PreLoadedMSA/preCalculatedLaunchView.js +2 -1
- package/dist/LaunchMsaView/components/QueryRowSelector.js +21 -5
- package/dist/LaunchMsaView/components/SequenceStatus.d.ts +14 -0
- package/dist/LaunchMsaView/components/SequenceStatus.js +15 -0
- package/dist/LaunchMsaView/components/SubmitCancelActions.d.ts +3 -1
- package/dist/LaunchMsaView/components/SubmitCancelActions.js +25 -13
- package/dist/LaunchMsaView/components/SubmitCancelActions.test.js +33 -3
- package/dist/LaunchMsaView/components/TabPanel.js +15 -3
- package/dist/LaunchMsaView/components/TabPanel.test.d.ts +1 -0
- package/dist/LaunchMsaView/components/TabPanel.test.js +36 -0
- package/dist/LaunchMsaView/components/TranscriptSelector.d.ts +3 -1
- package/dist/LaunchMsaView/components/TranscriptSelector.js +4 -2
- package/dist/LaunchMsaView/components/calculateProteinSequence.d.ts +4 -1
- package/dist/LaunchMsaView/components/calculateProteinSequence.js +20 -10
- package/dist/LaunchMsaView/components/fetchSeq.d.ts +4 -1
- package/dist/LaunchMsaView/components/fetchSeq.js +14 -4
- package/dist/LaunchMsaView/components/launchPlacement.d.ts +8 -0
- package/dist/LaunchMsaView/components/launchPlacement.js +25 -0
- package/dist/LaunchMsaView/components/useFeatureSequence.d.ts +2 -0
- package/dist/LaunchMsaView/components/useFeatureSequence.js +14 -11
- package/dist/LaunchMsaView/components/useTranscriptSelection.d.ts +10 -1
- package/dist/LaunchMsaView/components/useTranscriptSelection.js +21 -3
- package/dist/LaunchMsaView/components/useTranscriptSelection.test.d.ts +1 -0
- package/dist/LaunchMsaView/components/useTranscriptSelection.test.js +57 -0
- package/dist/LaunchMsaView/detectQueryRow.d.ts +11 -0
- package/dist/LaunchMsaView/detectQueryRow.js +11 -4
- package/dist/LaunchMsaView/detectQueryRow.test.js +30 -0
- package/dist/LaunchMsaView/index.js +28 -9
- package/dist/LaunchMsaView/launchTarget.d.ts +36 -8
- package/dist/LaunchMsaView/launchTarget.js +23 -13
- package/dist/LaunchMsaView/launchTarget.test.js +80 -15
- package/dist/LaunchMsaView/useQueryRowName.d.ts +13 -0
- package/dist/LaunchMsaView/useQueryRowName.js +16 -1
- package/dist/LaunchMsaView/useQueryRowName.test.d.ts +1 -0
- package/dist/LaunchMsaView/useQueryRowName.test.js +40 -0
- package/dist/LaunchMsaView/util.js +5 -2
- package/dist/LaunchMsaViewExtensionPoint/index.js +8 -6
- package/dist/LaunchMsaViewExtensionPoint/index.test.js +21 -0
- package/dist/MsaViewPanel/afterCreateAutoruns.d.ts +9 -6
- package/dist/MsaViewPanel/afterCreateAutoruns.js +72 -28
- package/dist/MsaViewPanel/components/LaunchProgress.d.ts +4 -3
- package/dist/MsaViewPanel/components/LaunchProgress.js +22 -10
- package/dist/MsaViewPanel/components/MsaViewPanel.js +10 -2
- package/dist/MsaViewPanel/components/MsaViewPanel.test.js +47 -2
- package/dist/MsaViewPanel/doLaunchBlast.d.ts +8 -0
- package/dist/MsaViewPanel/doLaunchBlast.js +78 -93
- package/dist/MsaViewPanel/doLaunchBlast.test.d.ts +1 -0
- package/dist/MsaViewPanel/doLaunchBlast.test.js +158 -0
- package/dist/MsaViewPanel/doLaunchOrthologs.js +40 -5
- package/dist/MsaViewPanel/doLaunchOrthologs.test.js +10 -0
- package/dist/MsaViewPanel/genomeToMSA.js +25 -7
- package/dist/MsaViewPanel/genomeToMSA.test.js +214 -19
- package/dist/MsaViewPanel/loadProteinDomains.d.ts +8 -2
- package/dist/MsaViewPanel/loadProteinDomains.js +18 -9
- package/dist/MsaViewPanel/loadProteinDomains.test.d.ts +1 -0
- package/dist/MsaViewPanel/loadProteinDomains.test.js +41 -0
- package/dist/MsaViewPanel/model.d.ts +265 -103
- package/dist/MsaViewPanel/model.js +174 -48
- package/dist/MsaViewPanel/model.test.d.ts +1 -0
- package/dist/MsaViewPanel/model.test.js +166 -0
- package/dist/MsaViewPanel/msaCoordToGenomeCoord.d.ts +12 -1
- package/dist/MsaViewPanel/msaCoordToGenomeCoord.js +8 -8
- package/dist/MsaViewPanel/msaCoordToGenomeCoord.test.js +133 -32
- package/dist/MsaViewPanel/msaDataStore.d.ts +2 -0
- package/dist/MsaViewPanel/msaDataStore.js +10 -0
- package/dist/MsaViewPanel/observeProteinHighlights.test.js +20 -3
- package/dist/MsaViewPanel/processInit.test.d.ts +1 -0
- package/dist/MsaViewPanel/processInit.test.js +72 -0
- package/dist/MsaViewPanel/resolveConnectedTranscript.d.ts +9 -0
- package/dist/MsaViewPanel/resolveConnectedTranscript.js +128 -0
- package/dist/MsaViewPanel/runLaunch.d.ts +1 -0
- package/dist/MsaViewPanel/runLaunch.js +35 -0
- package/dist/MsaViewPanel/runLaunch.test.js +46 -0
- package/dist/MsaViewPanel/storedData.test.js +20 -0
- package/dist/MsaViewPanel/structureConnection.d.ts +0 -4
- package/dist/MsaViewPanel/structureConnection.js +0 -19
- package/dist/MsaViewPanel/syncGenomeHoverToMsaColumn.test.js +12 -5
- package/dist/MsaViewPanel/util.d.ts +39 -0
- package/dist/MsaViewPanel/util.js +44 -0
- package/dist/index.d.ts +11 -11
- package/dist/jbrowse-plugin-msaview.umd.production.min.js +56 -68
- package/dist/jbrowse-plugin-msaview.umd.production.min.js.map +4 -4
- package/dist/utils/browserAlign.d.ts +42 -0
- package/dist/utils/browserAlign.js +297 -0
- package/dist/utils/browserAlign.test.d.ts +1 -0
- package/dist/utils/browserAlign.test.js +85 -0
- package/dist/utils/ebiBlast.d.ts +10 -1
- package/dist/utils/ebiBlast.js +27 -1
- package/dist/utils/homologSearch.d.ts +31 -0
- package/dist/utils/homologSearch.js +6 -0
- package/dist/utils/msa.d.ts +3 -11
- package/dist/utils/msa.js +14 -29
- package/dist/utils/msaRows.d.ts +11 -8
- package/dist/utils/msaRows.js +14 -12
- package/dist/utils/phmmer.d.ts +11 -1
- package/dist/utils/phmmer.js +49 -12
- package/dist/utils/unirefHomologs.d.ts +94 -0
- package/dist/utils/unirefHomologs.js +193 -0
- package/dist/utils/unirefHomologs.test.d.ts +1 -0
- package/dist/utils/unirefHomologs.test.js +118 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +23 -22
- package/src/AddHighlightModel/index.tsx +1 -1
- package/src/BgzipFastaMsaAdapter/configSchema.ts +1 -1
- package/src/LaunchMsaView/codingFeature.test.ts +96 -0
- package/src/LaunchMsaView/codingFeature.ts +49 -0
- package/src/LaunchMsaView/components/BlastQuery/BlastAutomaticPanel.tsx +52 -26
- package/src/LaunchMsaView/components/BlastQuery/BlastManualPanel.tsx +9 -1
- package/src/LaunchMsaView/components/BlastQuery/BlastPanel.tsx +24 -11
- package/src/LaunchMsaView/components/BlastQuery/BlastSettingsDialog.tsx +4 -4
- package/src/LaunchMsaView/components/BlastQuery/MsaAlgorithmSelect.tsx +7 -2
- package/src/LaunchMsaView/components/BlastQuery/consts.ts +77 -2
- package/src/LaunchMsaView/components/HelpButton.tsx +33 -0
- package/src/LaunchMsaView/components/HelpDialog.tsx +78 -0
- package/src/LaunchMsaView/components/LaunchMsaViewDialog.tsx +54 -34
- package/src/LaunchMsaView/components/ManualMSALoader/ManualMSALoader.tsx +13 -2
- package/src/LaunchMsaView/components/ManualMSALoader/launchView.ts +4 -0
- package/src/LaunchMsaView/components/OrthologQuery/OrthologPanel.tsx +28 -5
- package/src/LaunchMsaView/components/OrthologQuery/OrthologSourceSelect.tsx +4 -1
- package/src/LaunchMsaView/components/PreLoadedMSA/PreLoadedMSADataPanel.tsx +63 -13
- package/src/LaunchMsaView/components/PreLoadedMSA/preCalculatedLaunchView.ts +4 -0
- package/src/LaunchMsaView/components/QueryRowSelector.tsx +35 -7
- package/src/LaunchMsaView/components/SequenceStatus.tsx +29 -0
- package/src/LaunchMsaView/components/SubmitCancelActions.test.tsx +62 -3
- package/src/LaunchMsaView/components/SubmitCancelActions.tsx +37 -15
- package/src/LaunchMsaView/components/TabPanel.test.tsx +51 -0
- package/src/LaunchMsaView/components/TabPanel.tsx +16 -4
- package/src/LaunchMsaView/components/TranscriptSelector.tsx +6 -1
- package/src/LaunchMsaView/components/calculateProteinSequence.ts +29 -10
- package/src/LaunchMsaView/components/fetchSeq.ts +25 -5
- package/src/LaunchMsaView/components/launchPlacement.tsx +41 -0
- package/src/LaunchMsaView/components/useFeatureSequence.ts +18 -11
- package/src/LaunchMsaView/components/useTranscriptSelection.test.tsx +66 -0
- package/src/LaunchMsaView/components/useTranscriptSelection.ts +41 -2
- package/src/LaunchMsaView/detectQueryRow.test.ts +34 -0
- package/src/LaunchMsaView/detectQueryRow.ts +22 -4
- package/src/LaunchMsaView/index.ts +32 -13
- package/src/LaunchMsaView/launchTarget.test.ts +88 -15
- package/src/LaunchMsaView/launchTarget.ts +49 -14
- package/src/LaunchMsaView/useQueryRowName.test.ts +54 -0
- package/src/LaunchMsaView/useQueryRowName.ts +22 -1
- package/src/LaunchMsaView/util.ts +8 -4
- package/src/LaunchMsaViewExtensionPoint/index.test.ts +23 -0
- package/src/LaunchMsaViewExtensionPoint/index.ts +32 -6
- package/src/MsaViewPanel/afterCreateAutoruns.ts +85 -27
- package/src/MsaViewPanel/components/LaunchProgress.tsx +38 -7
- package/src/MsaViewPanel/components/MsaViewPanel.test.tsx +50 -2
- package/src/MsaViewPanel/components/MsaViewPanel.tsx +10 -2
- package/src/MsaViewPanel/doLaunchBlast.test.ts +207 -0
- package/src/MsaViewPanel/doLaunchBlast.ts +92 -142
- package/src/MsaViewPanel/doLaunchOrthologs.test.ts +13 -0
- package/src/MsaViewPanel/doLaunchOrthologs.ts +55 -5
- package/src/MsaViewPanel/genomeToMSA.test.ts +251 -31
- package/src/MsaViewPanel/genomeToMSA.ts +35 -7
- package/src/MsaViewPanel/loadProteinDomains.test.ts +52 -0
- package/src/MsaViewPanel/loadProteinDomains.ts +24 -11
- package/src/MsaViewPanel/model.test.ts +196 -0
- package/src/MsaViewPanel/model.ts +204 -55
- package/src/MsaViewPanel/msaCoordToGenomeCoord.test.ts +152 -32
- package/src/MsaViewPanel/msaCoordToGenomeCoord.ts +21 -11
- package/src/MsaViewPanel/msaDataStore.ts +10 -0
- package/src/MsaViewPanel/observeProteinHighlights.test.ts +27 -3
- package/src/MsaViewPanel/processInit.test.ts +84 -0
- package/src/MsaViewPanel/resolveConnectedTranscript.ts +160 -0
- package/src/MsaViewPanel/runLaunch.test.ts +54 -0
- package/src/MsaViewPanel/runLaunch.ts +39 -0
- package/src/MsaViewPanel/storedData.test.ts +26 -0
- package/src/MsaViewPanel/structureConnection.ts +0 -26
- package/src/MsaViewPanel/syncGenomeHoverToMsaColumn.test.ts +14 -5
- package/src/MsaViewPanel/util.ts +75 -0
- package/src/utils/browserAlign.test.ts +102 -0
- package/src/utils/browserAlign.ts +352 -0
- package/src/utils/ebiBlast.ts +34 -0
- package/src/utils/homologSearch.ts +51 -0
- package/src/utils/msa.ts +19 -41
- package/src/utils/msaRows.ts +20 -16
- package/src/utils/phmmer.ts +57 -11
- package/src/utils/unirefHomologs.test.ts +149 -0
- package/src/utils/unirefHomologs.ts +323 -0
- package/src/version.ts +1 -1
- package/dist/LaunchMsaView/components/geneticCodes.d.ts +0 -15
- package/dist/LaunchMsaView/components/geneticCodes.js +0 -227
- package/dist/MsaViewPanel/structureConnection.test.js +0 -53
- package/src/LaunchMsaView/components/geneticCodes.ts +0 -298
- package/src/MsaViewPanel/structureConnection.test.ts +0 -62
- /package/dist/{MsaViewPanel/structureConnection.test.d.ts → LaunchMsaView/codingFeature.test.d.ts} +0 -0
|
@@ -4,4 +4,4 @@ export declare const MSA_ALGORITHM_STORAGE_KEY = "msaView-msaAlgorithm";
|
|
|
4
4
|
export declare function validSearchChoice(stored: unknown): SearchChoice;
|
|
5
5
|
export declare function validMsaAlgorithm(stored: unknown): MsaAlgorithm;
|
|
6
6
|
export declare function useStoredSearchChoice(): readonly [SearchChoice, (choice: SearchChoice) => void];
|
|
7
|
-
export declare function useStoredMsaAlgorithm(): readonly ["clustalo" | "muscle" | "kalign" | "mafft", (algorithm: MsaAlgorithm) => void];
|
|
7
|
+
export declare function useStoredMsaAlgorithm(): readonly ["clustalo" | "muscle" | "kalign" | "mafft" | "browser", (algorithm: MsaAlgorithm) => void];
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import React, { Suspense, lazy, useState } from 'react';
|
|
2
|
+
import Help from '@mui/icons-material/Help';
|
|
3
|
+
import { IconButton, Tooltip } from '@mui/material';
|
|
4
|
+
const HelpDialog = lazy(() => import('./HelpDialog'));
|
|
5
|
+
export default function HelpButton() {
|
|
6
|
+
const [show, setShow] = useState(false);
|
|
7
|
+
return (React.createElement(React.Fragment, null,
|
|
8
|
+
React.createElement(Tooltip, { title: "What each tab does" },
|
|
9
|
+
React.createElement(IconButton, { "aria-label": "Help", onClick: () => {
|
|
10
|
+
setShow(true);
|
|
11
|
+
} },
|
|
12
|
+
React.createElement(Help, null))),
|
|
13
|
+
show ? (React.createElement(Suspense, { fallback: null },
|
|
14
|
+
React.createElement(HelpDialog, { handleClose: () => {
|
|
15
|
+
setShow(false);
|
|
16
|
+
} }))) : null));
|
|
17
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Dialog } from '@jbrowse/core/ui';
|
|
3
|
+
import { Button, DialogActions, DialogContent, Divider, Typography, } from '@mui/material';
|
|
4
|
+
export default function HelpDialog({ handleClose, }) {
|
|
5
|
+
return (React.createElement(Dialog, { open: true, maxWidth: "md", onClose: handleClose, title: "Launching an MSA" },
|
|
6
|
+
React.createElement(DialogContent, null,
|
|
7
|
+
React.createElement(Typography, { gutterBottom: true }, "Every tab aligns the same thing: the protein the selected transcript translates to. That transcript is the query row, which is what ties alignment columns back to codons in the genome view \u2014 hovering one highlights the other, and clicking navigates."),
|
|
8
|
+
React.createElement(Typography, { variant: "h6", gutterBottom: true }, "Orthologs"),
|
|
9
|
+
React.createElement(Typography, { gutterBottom: true }, "Precomputed sets, looked up rather than searched for: NCBI and PANTHER give one gene per species, and a UniRef cluster gives every UniProtKB entry within 50% identity of the query, one per species, from any organism. Nothing is queued, so this is the quickest route to \"this gene across species\"."),
|
|
10
|
+
React.createElement(Typography, { variant: "h6", gutterBottom: true }, "BLAST query"),
|
|
11
|
+
React.createElement(Typography, { gutterBottom: true }, "Searches run at EBI's Job Dispatcher, which searches UniProtKB. Swiss-Prot returns curated sequences that align more cleanly than the many near-identical entries a TrEMBL search brings back. blastp finds the hits and the chosen aligner then aligns them \u2014 \"in browser\" needs no second EBI job. phmmer instead searches with a profile HMM built from the query and aligns as it goes, so its output is the alignment and nothing is realigned; a hit matching the query in more than one place appears once per matched region. The Representative Proteomes (15% to 75%) spread the hits across all of life."),
|
|
12
|
+
React.createElement(Typography, { gutterBottom: true }, "The EBI queue is the wait, and it runs from seconds to many minutes. Searching NCBI's nr needs the Manual option, which links out to NCBI's own site: NCBI no longer lets a browser read responses from Blast.cgi."),
|
|
13
|
+
React.createElement(Typography, { variant: "h6", gutterBottom: true }, "Pre-loaded MSA datasets and Manual upload"),
|
|
14
|
+
React.createElement(Typography, { gutterBottom: true }, "Both take an alignment that already exists \u2014 one a dataset the site configured, the other a file or pasted text. In each case the row matching the selected transcript is found by comparing residues, not by name, because aligners rename the query on the way through.")),
|
|
15
|
+
React.createElement(Divider, null),
|
|
16
|
+
React.createElement(DialogActions, null,
|
|
17
|
+
React.createElement(Button, { onClick: handleClose, color: "primary" }, "Close"))));
|
|
18
|
+
}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import type { AbstractTrackModel, Feature } from '@jbrowse/core/util';
|
|
3
|
-
export default function LaunchMsaViewDialog({ handleClose, feature, model, }: {
|
|
3
|
+
export default function LaunchMsaViewDialog({ handleClose, feature, model, preferredTranscriptId, }: {
|
|
4
4
|
handleClose: () => void;
|
|
5
5
|
feature: Feature;
|
|
6
6
|
model: AbstractTrackModel;
|
|
7
|
+
/** the isoform the right-click landed on, when this opened on its gene */
|
|
8
|
+
preferredTranscriptId?: string;
|
|
7
9
|
}): React.JSX.Element;
|
|
@@ -3,12 +3,14 @@ import { Dialog } from '@jbrowse/core/ui';
|
|
|
3
3
|
import { getSession } from '@jbrowse/core/util';
|
|
4
4
|
import { Tab, Tabs } from '@mui/material';
|
|
5
5
|
import BlastPanel from './BlastQuery/BlastPanel';
|
|
6
|
+
import HelpButton from './HelpButton';
|
|
6
7
|
import ManualMSALoader from './ManualMSALoader/ManualMSALoader';
|
|
7
8
|
import OrthologPanel from './OrthologQuery/OrthologPanel';
|
|
8
9
|
import PreLoadedMSA from './PreLoadedMSA/PreLoadedMSADataPanel';
|
|
9
10
|
import { readMsaDatasets } from './PreLoadedMSA/types';
|
|
10
11
|
import TabPanel from './TabPanel';
|
|
11
|
-
|
|
12
|
+
import { LaunchPlacementProvider } from './launchPlacement';
|
|
13
|
+
export default function LaunchMsaViewDialog({ handleClose, feature, model, preferredTranscriptId, }) {
|
|
12
14
|
const session = getSession(model);
|
|
13
15
|
const datasets = readMsaDatasets(session.jbrowse);
|
|
14
16
|
const hasPreloadedDatasets = !!datasets?.length;
|
|
@@ -16,19 +18,22 @@ export default function LaunchMsaViewDialog({ handleClose, feature, model, }) {
|
|
|
16
18
|
// that BLAST takes 10+ minutes to answer worse (see utils/ncbiOrthologs.ts)
|
|
17
19
|
const [value, setValue] = useState('orthologs');
|
|
18
20
|
return (React.createElement(Dialog, { maxWidth: "xl", title: "Launch MSA view", open: true, onClose: handleClose },
|
|
19
|
-
React.createElement(
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
React.createElement(
|
|
30
|
-
|
|
31
|
-
React.createElement(
|
|
32
|
-
|
|
33
|
-
React.createElement(
|
|
21
|
+
React.createElement(LaunchPlacementProvider, null,
|
|
22
|
+
React.createElement("div", { style: { display: 'flex', alignItems: 'center' } },
|
|
23
|
+
React.createElement(Tabs, { value: value, onChange: (_event, newValue) => {
|
|
24
|
+
setValue(newValue);
|
|
25
|
+
} },
|
|
26
|
+
React.createElement(Tab, { label: "Orthologs", value: "orthologs" }),
|
|
27
|
+
React.createElement(Tab, { label: "BLAST query", value: "ncbi_blast" }),
|
|
28
|
+
hasPreloadedDatasets ? (React.createElement(Tab, { label: "Pre-loaded MSA datasets", value: "preloaded_msa" })) : null,
|
|
29
|
+
React.createElement(Tab, { label: "Manual upload", value: "manual_msa" })),
|
|
30
|
+
React.createElement(HelpButton, null)),
|
|
31
|
+
React.createElement(TabPanel, { value: value, index: "orthologs" },
|
|
32
|
+
React.createElement(OrthologPanel, { handleClose: handleClose, feature: feature, model: model, preferredTranscriptId: preferredTranscriptId })),
|
|
33
|
+
React.createElement(TabPanel, { value: value, index: "ncbi_blast" },
|
|
34
|
+
React.createElement(BlastPanel, { handleClose: handleClose, feature: feature, model: model, preferredTranscriptId: preferredTranscriptId })),
|
|
35
|
+
hasPreloadedDatasets ? (React.createElement(TabPanel, { value: value, index: "preloaded_msa" },
|
|
36
|
+
React.createElement(PreLoadedMSA, { model: model, feature: feature, handleClose: handleClose, preferredTranscriptId: preferredTranscriptId }))) : null,
|
|
37
|
+
React.createElement(TabPanel, { value: value, index: "manual_msa" },
|
|
38
|
+
React.createElement(ManualMSALoader, { model: model, feature: feature, handleClose: handleClose, preferredTranscriptId: preferredTranscriptId })))));
|
|
34
39
|
}
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import type { AbstractTrackModel, Feature } from '@jbrowse/core/util';
|
|
3
|
-
declare const ManualMSALoader: ({ model, feature, handleClose, }: {
|
|
3
|
+
declare const ManualMSALoader: ({ model, feature, handleClose, preferredTranscriptId, }: {
|
|
4
4
|
model: AbstractTrackModel;
|
|
5
5
|
feature: Feature;
|
|
6
6
|
handleClose: () => void;
|
|
7
|
+
/** the isoform the user right-clicked, preselected in the picker */
|
|
8
|
+
preferredTranscriptId?: string;
|
|
7
9
|
}) => React.JSX.Element;
|
|
8
10
|
export default ManualMSALoader;
|
|
@@ -8,6 +8,7 @@ import { useQueryRowName } from '../../useQueryRowName';
|
|
|
8
8
|
import { getGeneDisplayName, getLinearGenomeView } from '../../util';
|
|
9
9
|
import LaunchPanelContent from '../LaunchPanelContent';
|
|
10
10
|
import QueryRowSelector from '../QueryRowSelector';
|
|
11
|
+
import SequenceStatusMessage from '../SequenceStatus';
|
|
11
12
|
import SubmitCancelActions from '../SubmitCancelActions';
|
|
12
13
|
import TranscriptSelector from '../TranscriptSelector';
|
|
13
14
|
import { useTranscriptSelection } from '../useTranscriptSelection';
|
|
@@ -26,7 +27,7 @@ const useStyles = makeStyles()({
|
|
|
26
27
|
marginBottom: 20,
|
|
27
28
|
},
|
|
28
29
|
});
|
|
29
|
-
const ManualMSALoader = observer(function PreLoadedMSA2({ model, feature, handleClose, }) {
|
|
30
|
+
const ManualMSALoader = observer(function PreLoadedMSA2({ model, feature, handleClose, preferredTranscriptId, }) {
|
|
30
31
|
const view = getLinearGenomeView(model);
|
|
31
32
|
const { classes } = useStyles();
|
|
32
33
|
const [launchViewError, setLaunchViewError] = useState();
|
|
@@ -35,8 +36,12 @@ const ManualMSALoader = observer(function PreLoadedMSA2({ model, feature, handle
|
|
|
35
36
|
const [treeText, setTreeText] = useState('');
|
|
36
37
|
const [msaFileLocation, setMsaFileLocation] = useState();
|
|
37
38
|
const [treeFileLocation, setTreeFileLocation] = useState();
|
|
38
|
-
const transcriptSelection = useTranscriptSelection({
|
|
39
|
-
|
|
39
|
+
const transcriptSelection = useTranscriptSelection({
|
|
40
|
+
feature,
|
|
41
|
+
view,
|
|
42
|
+
preferredTranscriptId,
|
|
43
|
+
});
|
|
44
|
+
const { selectedTranscript, proteinSequence, error, sequenceStatus } = transcriptSelection;
|
|
40
45
|
const queryRow = useQueryRowName(msaText, proteinSequence);
|
|
41
46
|
const e = launchViewError ?? error;
|
|
42
47
|
return (React.createElement(React.Fragment, null,
|
|
@@ -58,7 +63,7 @@ const ManualMSALoader = observer(function PreLoadedMSA2({ model, feature, handle
|
|
|
58
63
|
} })))),
|
|
59
64
|
React.createElement(TranscriptSelector, { feature: feature, ...transcriptSelection }),
|
|
60
65
|
React.createElement(QueryRowSelector, { ...queryRow })),
|
|
61
|
-
React.createElement(SubmitCancelActions, { model: model, submitDisabled: !selectedTranscript ||
|
|
66
|
+
React.createElement(SubmitCancelActions, { model: model, hint: React.createElement(SequenceStatusMessage, { status: sequenceStatus }), submitDisabled: !selectedTranscript ||
|
|
62
67
|
(inputMethod === 'file' && !msaFileLocation) ||
|
|
63
68
|
(inputMethod === 'text' && !msaText.trim()), onSubmit: () => {
|
|
64
69
|
try {
|
|
@@ -69,6 +74,7 @@ const ManualMSALoader = observer(function PreLoadedMSA2({ model, feature, handle
|
|
|
69
74
|
view,
|
|
70
75
|
feature: selectedTranscript,
|
|
71
76
|
querySeqName: queryRow.querySeqName,
|
|
77
|
+
querySeqOffset: queryRow.querySeqOffset,
|
|
72
78
|
...(inputMethod === 'file'
|
|
73
79
|
? {
|
|
74
80
|
msaFilehandle: msaFileLocation,
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import type { Feature, FileLocation } from '@jbrowse/core/util';
|
|
2
2
|
import type { LinearGenomeViewModel } from '@jbrowse/plugin-linear-genome-view';
|
|
3
|
-
export declare function launchView({ newViewTitle, view, feature, msaFilehandle, treeFilehandle, querySeqName, data, }: {
|
|
3
|
+
export declare function launchView({ newViewTitle, view, feature, msaFilehandle, treeFilehandle, querySeqName, querySeqOffset, data, }: {
|
|
4
4
|
newViewTitle: string;
|
|
5
5
|
view: LinearGenomeViewModel;
|
|
6
6
|
feature: Feature;
|
|
7
7
|
msaFilehandle?: FileLocation;
|
|
8
8
|
treeFilehandle?: FileLocation;
|
|
9
9
|
querySeqName?: string;
|
|
10
|
+
/** transcript residues before the query row's first residue */
|
|
11
|
+
querySeqOffset?: number;
|
|
10
12
|
data?: {
|
|
11
13
|
msa: string;
|
|
12
14
|
tree?: string;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { getSession } from '@jbrowse/core/util';
|
|
2
2
|
import { launchMsaView } from '../../../utils/launchMsaView';
|
|
3
3
|
import { readLaunchPlacement } from '../../../utils/workspaces';
|
|
4
|
-
export function launchView({ newViewTitle, view, feature, msaFilehandle, treeFilehandle, querySeqName, data, }) {
|
|
4
|
+
export function launchView({ newViewTitle, view, feature, msaFilehandle, treeFilehandle, querySeqName, querySeqOffset, data, }) {
|
|
5
5
|
launchMsaView(getSession(view), {
|
|
6
6
|
placement: readLaunchPlacement(),
|
|
7
7
|
displayName: newViewTitle,
|
|
@@ -10,6 +10,7 @@ export function launchView({ newViewTitle, view, feature, msaFilehandle, treeFil
|
|
|
10
10
|
msaFilehandle,
|
|
11
11
|
treeFilehandle,
|
|
12
12
|
querySeqName,
|
|
13
|
+
querySeqOffset,
|
|
13
14
|
data,
|
|
14
15
|
});
|
|
15
16
|
}
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import type { AbstractTrackModel, Feature } from '@jbrowse/core/util';
|
|
3
|
-
declare const OrthologPanel: ({ handleClose, feature, model, }: {
|
|
3
|
+
declare const OrthologPanel: ({ handleClose, feature, model, preferredTranscriptId, }: {
|
|
4
4
|
model: AbstractTrackModel;
|
|
5
5
|
feature: Feature;
|
|
6
6
|
handleClose: () => void;
|
|
7
|
+
/** the isoform the user right-clicked, preselected in the picker */
|
|
8
|
+
preferredTranscriptId?: string;
|
|
7
9
|
}) => React.JSX.Element;
|
|
8
10
|
export default OrthologPanel;
|
|
@@ -9,6 +9,7 @@ import { getGeneDisplayName, getGeneIdentifiers, getLinearGenomeView, getTranscr
|
|
|
9
9
|
import MsaAlgorithmSelect from '../BlastQuery/MsaAlgorithmSelect';
|
|
10
10
|
import { useStoredMsaAlgorithm } from '../BlastQuery/searchChoiceStorage';
|
|
11
11
|
import LaunchPanelContent from '../LaunchPanelContent';
|
|
12
|
+
import SequenceStatusMessage from '../SequenceStatus';
|
|
12
13
|
import SubmitCancelActions from '../SubmitCancelActions';
|
|
13
14
|
import TranscriptSelector from '../TranscriptSelector';
|
|
14
15
|
import { useTranscriptSelection } from '../useTranscriptSelection';
|
|
@@ -20,7 +21,14 @@ const useStyles = makeStyles()({
|
|
|
20
21
|
width: 180,
|
|
21
22
|
},
|
|
22
23
|
});
|
|
23
|
-
|
|
24
|
+
// the N was literal: the helper text said "the closest N species" whatever the
|
|
25
|
+
// box held
|
|
26
|
+
const rowsHint = {
|
|
27
|
+
ncbi: rows => `the ${rows} closest species NCBI has`,
|
|
28
|
+
panther: rows => `the ${rows} closest species PANTHER has`,
|
|
29
|
+
uniref: rows => `${rows} rows, one per species, reviewed entries first`,
|
|
30
|
+
};
|
|
31
|
+
const OrthologPanel = observer(function ({ handleClose, feature, model, preferredTranscriptId, }) {
|
|
24
32
|
const { classes } = useStyles();
|
|
25
33
|
const view = getLinearGenomeView(model);
|
|
26
34
|
const [launchViewError, setLaunchViewError] = useState();
|
|
@@ -29,23 +37,29 @@ const OrthologPanel = observer(function ({ handleClose, feature, model, }) {
|
|
|
29
37
|
const [msaAlgorithm, setMsaAlgorithm] = useStoredMsaAlgorithm();
|
|
30
38
|
const [maxSpecies, setMaxSpecies] = useState(String(defaultMaxSpecies));
|
|
31
39
|
const geneCandidates = useMemo(() => getGeneIdentifiers(feature), [feature]);
|
|
32
|
-
const transcriptSelection = useTranscriptSelection({
|
|
33
|
-
|
|
40
|
+
const transcriptSelection = useTranscriptSelection({
|
|
41
|
+
feature,
|
|
42
|
+
view,
|
|
43
|
+
preferredTranscriptId,
|
|
44
|
+
});
|
|
45
|
+
const { selectedTranscript, proteinSequence, sequenceStatus } = transcriptSelection;
|
|
34
46
|
const e = transcriptSelection.error ?? launchViewError;
|
|
35
47
|
const rowCount = Number(maxSpecies);
|
|
36
48
|
const rowCountValid = Number.isInteger(rowCount) && rowCount >= 2;
|
|
37
49
|
return (React.createElement(React.Fragment, null,
|
|
38
50
|
React.createElement(LaunchPanelContent, { error: e },
|
|
39
|
-
React.createElement(Typography, { variant: "body2" }, "Precomputed orthologs, one gene per species, looked up rather than searched for. No BLAST job to queue."),
|
|
51
|
+
React.createElement(Typography, { variant: "body2" }, "Precomputed orthologs, one gene per species, looked up rather than searched for. No BLAST job to queue, and with the in-browser aligner no job at all."),
|
|
40
52
|
React.createElement("div", null,
|
|
41
53
|
React.createElement(OrthologSourceSelect, { className: classes.selectField, value: source, onChange: setSource }),
|
|
42
54
|
React.createElement(QuerySpeciesSelect, { className: classes.selectField, value: taxId, assemblyName: view.assemblyNames[0], onChange: setTaxId }),
|
|
43
55
|
React.createElement(MsaAlgorithmSelect, { className: classes.selectField, value: msaAlgorithm, onChange: setMsaAlgorithm }),
|
|
44
56
|
React.createElement(TextField2, { variant: "outlined", label: "Rows to align", className: classes.selectField, type: "number", value: maxSpecies, onChange: event => {
|
|
45
57
|
setMaxSpecies(event.target.value);
|
|
46
|
-
}, error: !rowCountValid, helperText:
|
|
58
|
+
}, error: !rowCountValid, helperText: rowCountValid
|
|
59
|
+
? rowsHint[source](rowCount)
|
|
60
|
+
: 'a whole number, 2 or more' })),
|
|
47
61
|
React.createElement(TranscriptSelector, { feature: feature, ...transcriptSelection })),
|
|
48
|
-
React.createElement(SubmitCancelActions, { model: model, submitDisabled: !proteinSequence || !rowCountValid, onSubmit: () => {
|
|
62
|
+
React.createElement(SubmitCancelActions, { model: model, hint: React.createElement(SequenceStatusMessage, { status: sequenceStatus }), submitDisabled: !proteinSequence || !rowCountValid, onSubmit: () => {
|
|
49
63
|
try {
|
|
50
64
|
if (selectedTranscript) {
|
|
51
65
|
setLaunchViewError(undefined);
|
|
@@ -59,7 +73,7 @@ const OrthologPanel = observer(function ({ handleClose, feature, model, }) {
|
|
|
59
73
|
maxSpecies: rowCount,
|
|
60
74
|
geneCandidates,
|
|
61
75
|
msaAlgorithm,
|
|
62
|
-
selectedTranscript,
|
|
76
|
+
selectedTranscript: selectedTranscript.toJSON(),
|
|
63
77
|
proteinSequence,
|
|
64
78
|
},
|
|
65
79
|
});
|
|
@@ -5,13 +5,16 @@ export const ORTHOLOG_SOURCE_STORAGE_KEY = 'msaview-ortholog-source';
|
|
|
5
5
|
export const orthologSourceLabels = {
|
|
6
6
|
ncbi: 'NCBI orthologs',
|
|
7
7
|
panther: 'PANTHER',
|
|
8
|
+
uniref: 'UniRef cluster',
|
|
8
9
|
};
|
|
9
10
|
// Which species a source can answer for, in the words a reader picking one
|
|
10
11
|
// needs: NCBI's ortholog sets stop at vertebrates and insects, PANTHER's run
|
|
11
|
-
// from human to yeast and Arabidopsis
|
|
12
|
+
// from human to yeast and Arabidopsis, and a UniRef cluster is every UniProtKB
|
|
13
|
+
// entry within 50% identity of the query, whatever it came from.
|
|
12
14
|
const hints = {
|
|
13
15
|
ncbi: 'vertebrates and insects',
|
|
14
16
|
panther: 'also yeast, worm, fly and plants',
|
|
17
|
+
uniref: 'all of UniProtKB within 50% identity',
|
|
15
18
|
};
|
|
16
19
|
export default function OrthologSourceSelect({ value, onChange, className, }) {
|
|
17
20
|
return (React.createElement(TextField2, { variant: "outlined", label: "Source", className: className, select: true, value: value, helperText: hints[value], onChange: event => {
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import type { AbstractTrackModel, Feature } from '@jbrowse/core/util';
|
|
3
|
-
declare const PreLoadedMSA: ({ model, feature, handleClose, }: {
|
|
3
|
+
declare const PreLoadedMSA: ({ model, feature, handleClose, preferredTranscriptId, }: {
|
|
4
4
|
model: AbstractTrackModel;
|
|
5
5
|
feature: Feature;
|
|
6
6
|
handleClose: () => void;
|
|
7
|
+
/** the isoform the user right-clicked, preselected in the picker */
|
|
8
|
+
preferredTranscriptId?: string;
|
|
7
9
|
}) => React.JSX.Element;
|
|
8
10
|
export default PreLoadedMSA;
|
|
@@ -1,13 +1,16 @@
|
|
|
1
|
-
import React, { useState } from 'react';
|
|
1
|
+
import React, { useMemo, useState } from 'react';
|
|
2
2
|
import { LoadingEllipses, SanitizedHTML } from '@jbrowse/core/ui';
|
|
3
3
|
import { getEnv, getSession } from '@jbrowse/core/util';
|
|
4
|
-
import { MenuItem } from '@mui/material';
|
|
4
|
+
import { MenuItem, Typography } from '@mui/material';
|
|
5
5
|
import { observer } from 'mobx-react';
|
|
6
6
|
import { makeStyles } from 'tss-react/mui';
|
|
7
7
|
import TextField2 from '../../../components/TextField2';
|
|
8
8
|
import { useFetch } from '../../../utils/useFetch';
|
|
9
|
-
import {
|
|
9
|
+
import { resolveQueryRowName, useQueryRowName } from '../../useQueryRowName';
|
|
10
|
+
import { getGeneDisplayName, getLinearGenomeView, getTranscriptDisplayName, } from '../../util';
|
|
10
11
|
import LaunchPanelContent from '../LaunchPanelContent';
|
|
12
|
+
import QueryRowSelector from '../QueryRowSelector';
|
|
13
|
+
import SequenceStatusMessage from '../SequenceStatus';
|
|
11
14
|
import SubmitCancelActions from '../SubmitCancelActions';
|
|
12
15
|
import TranscriptSelector from '../TranscriptSelector';
|
|
13
16
|
import { useTranscriptSelection } from '../useTranscriptSelection';
|
|
@@ -19,7 +22,7 @@ const useStyles = makeStyles()({
|
|
|
19
22
|
marginTop: 50,
|
|
20
23
|
},
|
|
21
24
|
});
|
|
22
|
-
const PreLoadedMSA = observer(function ({ model, feature, handleClose, }) {
|
|
25
|
+
const PreLoadedMSA = observer(function ({ model, feature, handleClose, preferredTranscriptId, }) {
|
|
23
26
|
const session = getSession(model);
|
|
24
27
|
const view = getLinearGenomeView(model);
|
|
25
28
|
const { classes } = useStyles();
|
|
@@ -34,8 +37,9 @@ const PreLoadedMSA = observer(function ({ model, feature, handleClose, }) {
|
|
|
34
37
|
feature,
|
|
35
38
|
view,
|
|
36
39
|
validIds: msaList,
|
|
40
|
+
preferredTranscriptId,
|
|
37
41
|
});
|
|
38
|
-
const { selectedId, selectedTranscript } = transcriptSelection;
|
|
42
|
+
const { selectedId, selectedTranscript, proteinSequence, sequenceStatus } = transcriptSelection;
|
|
39
43
|
const { data: msaData, isLoading: msaDataLoading, error: msaDataFetchError, } = useFetch(selectedId && selectedDataset && msaList
|
|
40
44
|
? `${selectedDataset.datasetId}-${selectedId}-msa`
|
|
41
45
|
: null, () => fetchMSA({
|
|
@@ -43,6 +47,19 @@ const PreLoadedMSA = observer(function ({ model, feature, handleClose, }) {
|
|
|
43
47
|
config: selectedDataset.adapter,
|
|
44
48
|
pluginManager,
|
|
45
49
|
}));
|
|
50
|
+
const msaText = useMemo(() => msaData?.map(r => `>${r.get('refName')}\n${r.get('seq')}`).join('\n') ??
|
|
51
|
+
'', [msaData]);
|
|
52
|
+
// The dataset's row for this transcript used to be assumed -- the launch
|
|
53
|
+
// named `<transcriptId>_<assembly>` and hoped the file agreed. Nothing
|
|
54
|
+
// checked, and a name the alignment does not carry fails silently: the view
|
|
55
|
+
// opens, renders, and never navigates. The row is found by sequence instead,
|
|
56
|
+
// the same way the Manual tab finds it.
|
|
57
|
+
const queryRow = useQueryRowName(msaText, proteinSequence);
|
|
58
|
+
// The name this panel used to launch with unconditionally. A dataset built to
|
|
59
|
+
// that convention does carry the row, so it is worth falling back to when the
|
|
60
|
+
// residues do not match closely enough to find it -- but only when the
|
|
61
|
+
// alignment really has it, which is the check the old code never made.
|
|
62
|
+
const querySeqName = resolveQueryRowName(queryRow, `${selectedId}_${assemblyNames[0] ?? ''}`);
|
|
46
63
|
const e = msaListFetchError ??
|
|
47
64
|
msaDataFetchError ??
|
|
48
65
|
transcriptSelection.error ??
|
|
@@ -53,25 +70,26 @@ const PreLoadedMSA = observer(function ({ model, feature, handleClose, }) {
|
|
|
53
70
|
setSelectedDatasetId(event.target.value);
|
|
54
71
|
} }, datasets?.map(d => (React.createElement(MenuItem, { key: d.datasetId, value: d.datasetId }, d.name)))),
|
|
55
72
|
selectedDataset ? (React.createElement("div", { className: classes.selectedContainer },
|
|
56
|
-
!msaListLoading && msaDataLoading ? (React.createElement(LoadingEllipses, { variant: "h6", message: `Loading MSA for
|
|
73
|
+
!msaListLoading && msaDataLoading ? (React.createElement(LoadingEllipses, { variant: "h6", message: `Loading MSA for ${getTranscriptDisplayName(selectedTranscript) || selectedId}` })) : null,
|
|
57
74
|
msaListLoading ? (React.createElement(LoadingEllipses, { variant: "h6", message: `Loading available MSAs for (${selectedDataset.name})` })) : null,
|
|
58
75
|
msaList ? (React.createElement("div", null,
|
|
59
76
|
React.createElement(SanitizedHTML, { html: selectedDataset.description }),
|
|
60
|
-
React.createElement(TranscriptSelector, { feature: feature, ...transcriptSelection })
|
|
61
|
-
|
|
77
|
+
React.createElement(TranscriptSelector, { feature: feature, ...transcriptSelection }),
|
|
78
|
+
msaText ? (React.createElement(QueryRowSelector, { ...queryRow, querySeqName: querySeqName })) : null)) : null)) : null),
|
|
79
|
+
React.createElement(SubmitCancelActions, { model: model, hint: msaDataLoading ? (React.createElement(LoadingEllipses, { message: "Loading alignment" })) : !!msaData?.length && !querySeqName ? (React.createElement(Typography, { color: "textSecondary", variant: "body2" }, "no row matches this transcript")) : (React.createElement(SequenceStatusMessage, { status: sequenceStatus })),
|
|
80
|
+
// launching without a query row opens a view that renders and then
|
|
81
|
+
// never navigates, which reads as a broken feature rather than a
|
|
82
|
+
// dataset that does not cover this gene
|
|
83
|
+
submitDisabled: !selectedTranscript || !msaData?.length || !querySeqName, onSubmit: () => {
|
|
62
84
|
try {
|
|
63
|
-
if (selectedTranscript &&
|
|
64
|
-
const querySeqName = `${selectedId}_${assemblyNames[0]}`;
|
|
85
|
+
if (selectedTranscript && msaText) {
|
|
65
86
|
preCalculatedLaunchView({
|
|
66
87
|
newViewTitle: getGeneDisplayName(selectedTranscript),
|
|
67
88
|
view,
|
|
68
89
|
querySeqName,
|
|
90
|
+
querySeqOffset: queryRow.querySeqOffset,
|
|
69
91
|
feature: selectedTranscript,
|
|
70
|
-
data: {
|
|
71
|
-
msa: msaData
|
|
72
|
-
.map(r => `>${r.get('refName')}\n${r.get('seq')}`)
|
|
73
|
-
.join('\n'),
|
|
74
|
-
},
|
|
92
|
+
data: { msa: msaText },
|
|
75
93
|
});
|
|
76
94
|
handleClose();
|
|
77
95
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Feature } from '@jbrowse/core/util';
|
|
2
2
|
import type { LinearGenomeViewModel } from '@jbrowse/plugin-linear-genome-view';
|
|
3
|
-
export declare function preCalculatedLaunchView({ newViewTitle, view, feature, data, querySeqName, }: {
|
|
3
|
+
export declare function preCalculatedLaunchView({ newViewTitle, view, feature, data, querySeqName, querySeqOffset, }: {
|
|
4
4
|
data: {
|
|
5
5
|
msa: string;
|
|
6
6
|
};
|
|
@@ -8,4 +8,6 @@ export declare function preCalculatedLaunchView({ newViewTitle, view, feature, d
|
|
|
8
8
|
view: LinearGenomeViewModel;
|
|
9
9
|
feature: Feature;
|
|
10
10
|
querySeqName: string;
|
|
11
|
+
/** transcript residues before the query row's first residue */
|
|
12
|
+
querySeqOffset?: number;
|
|
11
13
|
}): void;
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { getSession } from '@jbrowse/core/util';
|
|
2
2
|
import { launchMsaView } from '../../../utils/launchMsaView';
|
|
3
3
|
import { readLaunchPlacement } from '../../../utils/workspaces';
|
|
4
|
-
export function preCalculatedLaunchView({ newViewTitle, view, feature, data, querySeqName, }) {
|
|
4
|
+
export function preCalculatedLaunchView({ newViewTitle, view, feature, data, querySeqName, querySeqOffset, }) {
|
|
5
5
|
launchMsaView(getSession(view), {
|
|
6
6
|
placement: readLaunchPlacement(),
|
|
7
7
|
displayName: newViewTitle,
|
|
8
8
|
treeAreaWidth: 200,
|
|
9
9
|
querySeqName,
|
|
10
|
+
querySeqOffset,
|
|
10
11
|
treeWidth: 100,
|
|
11
12
|
drawNodeBubbles: false,
|
|
12
13
|
labelsAlignRight: true,
|
|
@@ -10,6 +10,19 @@ const useStyles = makeStyles()({
|
|
|
10
10
|
marginTop: 10,
|
|
11
11
|
},
|
|
12
12
|
});
|
|
13
|
+
/**
|
|
14
|
+
* Where a trimmed row sits in the protein. The offset is what the view maps
|
|
15
|
+
* through, so the alert says it rather than leaving the user to wonder why the
|
|
16
|
+
* row is shorter than their transcript.
|
|
17
|
+
*/
|
|
18
|
+
function partialCoverage({ identity, offset, }) {
|
|
19
|
+
const covering = `, covering ${Math.round(identity * 100)}% of it`;
|
|
20
|
+
return offset > 0
|
|
21
|
+
? `${covering} from residue ${offset + 1}`
|
|
22
|
+
: offset < 0
|
|
23
|
+
? `${covering} and running ${-offset} residues past its start`
|
|
24
|
+
: covering;
|
|
25
|
+
}
|
|
13
26
|
/**
|
|
14
27
|
* Which MSA row corresponds to the selected transcript. Clicking and hovering in
|
|
15
28
|
* the alignment reach the genome only through this name, and a wrong one fails
|
|
@@ -27,12 +40,15 @@ export default function QueryRowSelector({ names, detected, querySeqName, setQue
|
|
|
27
40
|
detected?.name === name ? ' — matches your protein' : ''))))) : (React.createElement(TextField2, { variant: "outlined", label: "MSA row matching the selected transcript", fullWidth: true, className: classes.field, helperText: "Paste an alignment above and this fills in on its own", value: querySeqName, onChange: event => {
|
|
28
41
|
setQuerySeqName(event.target.value);
|
|
29
42
|
} })),
|
|
30
|
-
isAutoDetected && detected ? (React.createElement(Alert, { severity: "
|
|
43
|
+
isAutoDetected && detected ? (detected.quality === 'similar' ? (React.createElement(Alert, { severity: "warning", className: classes.alert },
|
|
44
|
+
React.createElement("strong", null, detected.name),
|
|
45
|
+
" is",
|
|
46
|
+
' ',
|
|
47
|
+
Math.round(detected.identity * 100),
|
|
48
|
+
"% identical to your protein but is not the same sequence. Clicking the alignment navigates the genome view, approximately: wherever the two differ by an insertion or a deletion, every residue after it lands one codon off.")) : (React.createElement(Alert, { severity: "success", className: classes.alert },
|
|
31
49
|
"Matched ",
|
|
32
50
|
React.createElement("strong", null, detected.name),
|
|
33
51
|
" to your protein sequence",
|
|
34
|
-
detected.quality === 'exact'
|
|
35
|
-
|
|
36
|
-
: `, covering ${Math.round(detected.identity * 100)}% of it`,
|
|
37
|
-
". Clicking the alignment will navigate the genome view.")) : names.length > 0 && !querySeqName ? (React.createElement(Alert, { severity: "warning", className: classes.alert }, "No row matched your protein sequence \u2014 pick the one for your gene above. Without it the alignment still renders, but clicking it will not navigate the genome view.")) : null));
|
|
52
|
+
detected.quality === 'exact' ? '' : partialCoverage(detected),
|
|
53
|
+
". Clicking the alignment will navigate the genome view."))) : names.length > 0 && !querySeqName ? (React.createElement(Alert, { severity: "warning", className: classes.alert }, "No row matched your protein sequence \u2014 pick the one for your gene above. Without it the alignment still renders, but clicking it will not navigate the genome view.")) : null));
|
|
38
54
|
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
export type SequenceStatus = 'loading' | 'ready' | 'missing' | 'error';
|
|
3
|
+
/**
|
|
4
|
+
* Why Submit is grey. The query row is the selected transcript's translation,
|
|
5
|
+
* and fetching and translating it takes a round trip to the sequence adapter,
|
|
6
|
+
* so the button starts disabled on every panel — with nothing to distinguish
|
|
7
|
+
* "wait a moment" from "this gene has no protein and never will".
|
|
8
|
+
*
|
|
9
|
+
* An error says so through the panel's own ErrorMessage, so this stays quiet
|
|
10
|
+
* for that one rather than saying it twice.
|
|
11
|
+
*/
|
|
12
|
+
export default function SequenceStatusMessage({ status, }: {
|
|
13
|
+
status: SequenceStatus;
|
|
14
|
+
}): React.JSX.Element | null;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { LoadingEllipses } from '@jbrowse/core/ui';
|
|
3
|
+
import { Typography } from '@mui/material';
|
|
4
|
+
/**
|
|
5
|
+
* Why Submit is grey. The query row is the selected transcript's translation,
|
|
6
|
+
* and fetching and translating it takes a round trip to the sequence adapter,
|
|
7
|
+
* so the button starts disabled on every panel — with nothing to distinguish
|
|
8
|
+
* "wait a moment" from "this gene has no protein and never will".
|
|
9
|
+
*
|
|
10
|
+
* An error says so through the panel's own ErrorMessage, so this stays quiet
|
|
11
|
+
* for that one rather than saying it twice.
|
|
12
|
+
*/
|
|
13
|
+
export default function SequenceStatusMessage({ status, }) {
|
|
14
|
+
return status === 'loading' ? (React.createElement(LoadingEllipses, { message: "Translating transcript" })) : status === 'missing' ? (React.createElement(Typography, { color: "textSecondary", variant: "body2" }, "no coding sequence to align")) : null;
|
|
15
|
+
}
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import type { AbstractTrackModel } from '@jbrowse/core/util';
|
|
3
|
-
export default function SubmitCancelActions({ onSubmit, onCancel, submitDisabled, submitLabel, cancelLabel, model, }: {
|
|
3
|
+
export default function SubmitCancelActions({ onSubmit, onCancel, submitDisabled, hint, submitLabel, cancelLabel, model, }: {
|
|
4
4
|
onSubmit: () => void;
|
|
5
5
|
onCancel: () => void;
|
|
6
6
|
submitDisabled?: boolean;
|
|
7
|
+
/** why Submit is grey, shown beside it */
|
|
8
|
+
hint?: React.ReactNode;
|
|
7
9
|
submitLabel?: string;
|
|
8
10
|
cancelLabel?: string;
|
|
9
11
|
/** omitted by a panel that submits something other than a view launch */
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import React
|
|
1
|
+
import React from 'react';
|
|
2
2
|
import { getSession } from '@jbrowse/core/util';
|
|
3
3
|
import { Button, Checkbox, DialogActions, FormControlLabel, } from '@mui/material';
|
|
4
|
-
import {
|
|
4
|
+
import { sessionSupportsPlacement, writeLaunchPlacement, } from '../../utils/workspaces';
|
|
5
|
+
import { useLaunchPlacement } from './launchPlacement';
|
|
5
6
|
/**
|
|
6
7
|
* Where the launch puts the view, offered wherever a launch is submitted.
|
|
7
8
|
*
|
|
@@ -13,20 +14,31 @@ import { readLaunchPlacement, sessionSupportsPlacement, writeLaunchPlacement, }
|
|
|
13
14
|
* release that places views its own way — because the box would do nothing
|
|
14
15
|
* there and every launch would quietly ignore it.
|
|
15
16
|
*/
|
|
16
|
-
function PlacementToggle({
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
const { checked } = event.target;
|
|
21
|
-
setSideBySide(checked);
|
|
22
|
-
writeLaunchPlacement(checked ? 'splitRight' : 'stack');
|
|
23
|
-
} }) })) : null;
|
|
17
|
+
function PlacementToggle({ checked, onChange, }) {
|
|
18
|
+
return (React.createElement(FormControlLabel, { label: "Open beside the genome view", control: React.createElement(Checkbox, { checked: checked, onChange: event => {
|
|
19
|
+
onChange(event.target.checked);
|
|
20
|
+
} }) }));
|
|
24
21
|
}
|
|
25
|
-
export default function SubmitCancelActions({ onSubmit, onCancel, submitDisabled, submitLabel = 'Submit', cancelLabel = 'Cancel', model, }) {
|
|
22
|
+
export default function SubmitCancelActions({ onSubmit, onCancel, submitDisabled, hint, submitLabel = 'Submit', cancelLabel = 'Cancel', model, }) {
|
|
23
|
+
const [sideBySide, setSideBySide] = useLaunchPlacement();
|
|
24
|
+
// The stored value is what the next launch reads, so it is written on submit
|
|
25
|
+
// rather than on the click: ticking the box and then pressing Cancel used to
|
|
26
|
+
// change where every future launch landed, from a dialog the user backed out
|
|
27
|
+
// of.
|
|
28
|
+
const offerPlacement = !!model && sessionSupportsPlacement(getSession(model));
|
|
26
29
|
return (React.createElement(DialogActions, { sx: { flexWrap: 'wrap', rowGap: 1 } },
|
|
27
|
-
|
|
28
|
-
React.createElement("div", { style: {
|
|
30
|
+
offerPlacement ? (React.createElement(PlacementToggle, { checked: sideBySide, onChange: setSideBySide })) : null,
|
|
31
|
+
React.createElement("div", { style: {
|
|
32
|
+
display: 'flex',
|
|
33
|
+
alignItems: 'center',
|
|
34
|
+
gap: 8,
|
|
35
|
+
marginLeft: 'auto',
|
|
36
|
+
} },
|
|
37
|
+
hint,
|
|
29
38
|
React.createElement(Button, { sx: { flexShrink: 0 }, color: "primary", variant: "contained", disabled: submitDisabled, onClick: () => {
|
|
39
|
+
if (offerPlacement) {
|
|
40
|
+
writeLaunchPlacement(sideBySide ? 'splitRight' : 'stack');
|
|
41
|
+
}
|
|
30
42
|
onSubmit();
|
|
31
43
|
} }, submitLabel),
|
|
32
44
|
React.createElement(Button, { sx: { flexShrink: 0 }, color: "secondary", variant: "contained", onClick: () => {
|