UNPKG

@jbrowse/plugin-linear-genome-view

Version:

JBrowse 2 linear genome view

134 lines (133 loc) 7.68 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const jsx_runtime_1 = require("react/jsx-runtime"); const react_1 = require("react"); const configuration_1 = require("@jbrowse/core/configuration"); const ui_1 = require("@jbrowse/core/ui"); const Icons_1 = require("@jbrowse/core/ui/Icons"); const util_1 = require("@jbrowse/core/util"); const formatFastaStrings_1 = require("@jbrowse/core/util/formatFastaStrings"); const GetApp_1 = __importDefault(require("@mui/icons-material/GetApp")); const material_1 = require("@mui/material"); const copy_to_clipboard_1 = __importDefault(require("copy-to-clipboard")); const file_saver_1 = require("file-saver"); const mobx_react_1 = require("mobx-react"); const mui_1 = require("tss-react/mui"); const useStyles = (0, mui_1.makeStyles)()({ dialogContent: { width: '80em', }, textAreaFont: { fontFamily: 'Courier New', }, ml: { marginLeft: 10, }, }); async function fetchSequence(model, regions) { const session = (0, util_1.getSession)(model); const { leftOffset, rightOffset } = model; if (!leftOffset || !rightOffset) { throw new Error('no offsets on model to use for range'); } if (leftOffset.assemblyName !== rightOffset.assemblyName) { throw new Error('not able to fetch sequences from multiple assemblies'); } const { rpcManager, assemblyManager } = session; const assemblyName = leftOffset.assemblyName || rightOffset.assemblyName || ''; const assembly = assemblyManager.get(assemblyName); if (!assembly) { throw new Error(`assembly ${assemblyName} not found`); } const adapterConfig = (0, configuration_1.getConf)(assembly, ['sequence', 'adapter']); const sessionId = 'getSequence'; return rpcManager.call(sessionId, 'CoreGetFeatures', { adapterConfig, regions, sessionId, }); } const GetSequenceDialog = (0, mobx_react_1.observer)(function ({ model, handleClose, }) { const { classes } = useStyles(); const [error, setError] = (0, react_1.useState)(); const [sequenceChunks, setSequenceChunks] = (0, react_1.useState)(); const [rev, setReverse] = (0, react_1.useState)(false); const [copied, setCopied] = (0, react_1.useState)(false); const [comp, setComplement] = (0, react_1.useState)(false); const { leftOffset, rightOffset } = model; const loading = Boolean(sequenceChunks === undefined); (0, react_1.useEffect)(() => { const controller = new AbortController(); (async () => { try { const selection = model.getSelectedRegions(leftOffset, rightOffset); if (selection.length === 0) { throw new Error('Selected region is out of bounds'); } const chunks = await fetchSequence(model, selection); setSequenceChunks(chunks); } catch (e) { console.error(e); setError(e); } })(); return () => { controller.abort(); }; }, [model, leftOffset, rightOffset]); const sequence = sequenceChunks ? (0, formatFastaStrings_1.formatSeqFasta)(sequenceChunks.map(chunk => { let chunkSeq = chunk.get('seq'); const chunkRefName = chunk.get('refName'); const chunkStart = chunk.get('start') + 1; const chunkEnd = chunk.get('end'); const loc = `${chunkRefName}:${chunkStart}-${chunkEnd}`; if ((chunkSeq === null || chunkSeq === void 0 ? void 0 : chunkSeq.length) !== chunkEnd - chunkStart + 1) { throw new Error(`${loc} returned ${chunkSeq.length.toLocaleString()} bases, but should have returned ${(chunkEnd - chunkStart).toLocaleString()}`); } if (rev) { chunkSeq = (0, util_1.reverse)(chunkSeq); } if (comp) { chunkSeq = (0, util_1.complement)(chunkSeq); } return { header: loc + (rev ? '-rev' : '') + (comp ? '-comp' : ''), seq: chunkSeq, }; })) : ''; const sequenceTooLarge = sequence ? sequence.length > 1000000 : false; return ((0, jsx_runtime_1.jsxs)(ui_1.Dialog, { maxWidth: "xl", open: true, onClose: () => { handleClose(); model.setOffsets(); }, title: "Reference sequence", children: [(0, jsx_runtime_1.jsxs)(material_1.DialogContent, { children: [error ? ((0, jsx_runtime_1.jsx)(material_1.Typography, { color: "error", children: `${error}` })) : loading ? ((0, jsx_runtime_1.jsxs)(material_1.Container, { children: ["Retrieving reference sequence...", (0, jsx_runtime_1.jsx)(material_1.CircularProgress, { className: classes.ml, size: 20, disableShrink: true })] })) : null, (0, jsx_runtime_1.jsx)(material_1.TextField, { "data-testid": "rubberband-sequence", variant: "outlined", multiline: true, minRows: 5, maxRows: 10, disabled: sequenceTooLarge, className: classes.dialogContent, fullWidth: true, value: sequenceTooLarge ? 'Reference sequence too large to display, use the download FASTA button' : sequence, slotProps: { input: { readOnly: true, classes: { input: classes.textAreaFont, }, }, } }), (0, jsx_runtime_1.jsxs)(material_1.FormGroup, { children: [(0, jsx_runtime_1.jsx)(material_1.FormControlLabel, { control: (0, jsx_runtime_1.jsx)(material_1.Checkbox, { value: rev, onChange: event => { setReverse(event.target.checked); } }), label: "Reverse sequence" }), (0, jsx_runtime_1.jsx)(material_1.FormControlLabel, { control: (0, jsx_runtime_1.jsx)(material_1.Checkbox, { value: comp, onChange: event => { setComplement(event.target.checked); } }), label: "Complement sequence" })] }), (0, jsx_runtime_1.jsx)(material_1.Typography, { style: { margin: 10 }, children: "Note: Check both boxes for the \"reverse complement\"" })] }), (0, jsx_runtime_1.jsxs)(material_1.DialogActions, { children: [(0, jsx_runtime_1.jsx)(material_1.Button, { onClick: () => { (0, copy_to_clipboard_1.default)(sequence); setCopied(true); setTimeout(() => { setCopied(false); }, 500); }, disabled: loading || !!error || sequenceTooLarge, color: "primary", startIcon: (0, jsx_runtime_1.jsx)(Icons_1.ContentCopy, {}), children: copied ? 'Copied' : 'Copy to clipboard' }), (0, jsx_runtime_1.jsx)(material_1.Button, { onClick: () => { (0, file_saver_1.saveAs)(new Blob([sequence || ''], { type: 'text/x-fasta;charset=utf-8', }), 'jbrowse_ref_seq.fa'); }, disabled: loading || !!error, color: "primary", startIcon: (0, jsx_runtime_1.jsx)(GetApp_1.default, {}), children: "Download FASTA" }), (0, jsx_runtime_1.jsx)(material_1.Button, { onClick: handleClose, variant: "contained", children: "Close" })] })] })); }); exports.default = GetSequenceDialog;