UNPKG

@jbrowse/plugin-linear-genome-view

Version:

JBrowse 2 linear genome view

102 lines (101 loc) 5.73 kB
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime"; import { useEffect, useState } from 'react'; import { Dialog, ErrorMessage, LoadingEllipses } from '@jbrowse/core/ui'; import { complement, reverse, toLocale } from '@jbrowse/core/util'; import { formatSeqFasta } from '@jbrowse/core/util/formatFastaStrings'; import { makeStyles } from '@jbrowse/core/util/tss-react'; import ContentCopyIcon from '@mui/icons-material/ContentCopy'; import GetAppIcon from '@mui/icons-material/GetApp'; import { Button, Checkbox, DialogActions, DialogContent, FormControlLabel, FormGroup, TextField, Typography, } from '@mui/material'; import { observer } from 'mobx-react'; import { fetchSequence } from "./fetchSequence.js"; const useStyles = makeStyles()({ dialogContent: { width: '80em', }, textAreaFont: { fontFamily: 'Courier New', }, }); const GetSequenceDialog = observer(function GetSequenceDialog({ model, handleClose, }) { const { classes } = useStyles(); const [error, setError] = useState(); const [sequenceChunks, setSequenceChunks] = useState(); const [rev, setReverse] = useState(false); const [copied, setCopied] = useState(false); const [comp, setComplement] = useState(false); const { leftOffset, rightOffset } = model; const loading = sequenceChunks === undefined; 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'); } setSequenceChunks(await fetchSequence(model, selection)); } catch (e) { console.error(e); setError(e); } })(); return () => { controller.abort(); }; }, [model, leftOffset, rightOffset]); const sequence = sequenceChunks ? 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?.length !== chunkEnd - chunkStart + 1) { throw new Error(`${loc} returned ${toLocale(chunkSeq.length)} bases, but should have returned ${toLocale(chunkEnd - chunkStart)}`); } if (rev) { chunkSeq = reverse(chunkSeq); } if (comp) { chunkSeq = complement(chunkSeq); } return { header: loc + (rev ? '-rev' : '') + (comp ? '-comp' : ''), seq: chunkSeq, }; })) : ''; const sequenceTooLarge = sequence ? sequence.length > 1_000_000 : false; return (_jsxs(Dialog, { maxWidth: "xl", open: true, title: "Reference sequence", onClose: () => { handleClose(); model.setOffsets(); }, children: [_jsxs(DialogContent, { children: [error ? (_jsx(ErrorMessage, { error: error })) : loading ? (_jsx(LoadingEllipses, { message: "Retrieving sequences" })) : null, _jsx(TextField, { 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, }, }, } }), _jsxs(FormGroup, { children: [_jsx(FormControlLabel, { control: _jsx(Checkbox, { value: rev, onChange: event => { setReverse(event.target.checked); } }), label: "Reverse sequence" }), _jsx(FormControlLabel, { control: _jsx(Checkbox, { value: comp, onChange: event => { setComplement(event.target.checked); } }), label: "Complement sequence" })] }), _jsx(Typography, { style: { margin: 10 }, children: "Note: Check both boxes for the \"reverse complement\"" })] }), _jsxs(DialogActions, { children: [_jsx(Button, { onClick: async () => { const { default: copy } = await import('copy-to-clipboard'); copy(sequence); setCopied(true); setTimeout(() => { setCopied(false); }, 500); }, disabled: loading || !!error || sequenceTooLarge, color: "primary", startIcon: _jsx(ContentCopyIcon, {}), children: copied ? 'Copied' : 'Copy to clipboard' }), _jsx(Button, { onClick: async () => { const { saveAs } = await import('file-saver-es'); saveAs(new Blob([sequence || ''], { type: 'text/x-fasta;charset=utf-8', }), 'jbrowse_ref_seq.fa'); }, disabled: loading || !!error, color: "primary", startIcon: _jsx(GetAppIcon, {}), children: "Download FASTA" }), _jsx(Button, { onClick: handleClose, variant: "contained", children: "Close" })] })] })); }); export default GetSequenceDialog;