Skip to content

Commit

Permalink
Advanced panel
Browse files Browse the repository at this point in the history
  • Loading branch information
cmdcolin committed May 11, 2024
1 parent 97be26f commit 7fa6bca
Show file tree
Hide file tree
Showing 6 changed files with 533 additions and 76 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import React, { lazy, useRef, useState, Suspense } from 'react'
import {
Button,
FormControl,
IconButton,
MenuItem,
Select,
Typography,
Expand All @@ -16,13 +15,18 @@ import { useFeatureSequence } from './hooks'
import { ErrorMessage, LoadingEllipses } from '../../ui'
import { SimpleFeatureSerialized, getSession } from '../../util'
import { BaseFeatureWidgetModel } from '../stateModelFactory'
import CascadingMenuButton from '../../ui/CascadingMenuButton'

// icons
import MoreVert from '@mui/icons-material/MoreVert'
import Settings from '@mui/icons-material/Settings'

// lazies
const SequencePanel = lazy(() => import('./SequencePanel'))
const SettingsDialog = lazy(() => import('./dialogs/SettingsDialog'))
const AdvancedSequenceDialog = lazy(
() => import('./dialogs/AdvancedSequenceDialog'),
)

const useStyles = makeStyles()({
formControl: {
Expand Down Expand Up @@ -123,42 +127,49 @@ const SequenceFeatureDetails = observer(function ({
))}
</Select>
</FormControl>
<Button
className={classes.formControl}
variant="contained"
onClick={() => {
const ref = seqPanelRef.current
if (ref) {
copy(ref.textContent || '', { format: 'text/plain' })
}
}}
<CascadingMenuButton
menuItems={[
{
label: 'Copy plaintext',
onClick: () => {
const ref = seqPanelRef.current
if (ref) {
copy(ref.textContent || '', { format: 'text/plain' })
}
},
},
{
label: 'Copy HTML',
onClick: () => {
const ref = seqPanelRef.current
if (ref) {
copy(ref.innerHTML, { format: 'text/html' })
}
},
},
{
label: 'Launch advanced view...',
onClick: () => {
getSession(model).queueDialog(handleClose => [
AdvancedSequenceDialog,
{ model, feature, handleClose },
])
},
},
{
label: 'Settings',
icon: Settings,
onClick: () => {
getSession(model).queueDialog(handleClose => [
SettingsDialog,
{ model: sequenceFeatureDetails, handleClose },
])
},
},
]}
>
Copy plaintext
</Button>
<Button
className={classes.formControl}
variant="contained"
onClick={() => {
const ref = seqPanelRef.current
if (ref) {
copy(ref.innerHTML, { format: 'text/html' })
}
}}
>
Copy HTML
</Button>

<IconButton
className={classes.formControl}
onClick={() =>
getSession(model).queueDialog(handleClose => [
SettingsDialog,
{ model: sequenceFeatureDetails, handleClose },
])
}
>
<Settings />
</IconButton>
<MoreVert />
</CascadingMenuButton>
</div>
<div>
{feature.type === 'gene' ? (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ const SequenceFeaturePanel = observer(function ({
const { classes } = useStyles()
const [shown, setShown] = useState(false)

return !model ? null : (
return model ? (
<div className={classes.container}>
<FormControl className={classes.formControl}>
<Button variant="contained" onClick={() => setShown(!shown)}>
Expand Down Expand Up @@ -67,7 +67,7 @@ const SequenceFeaturePanel = observer(function ({
</Suspense>
) : null}
</div>
)
) : null
})

export default SequenceFeaturePanel
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
import React, { Suspense, useRef, useState } from 'react'
import {
Button,
DialogContent,
DialogActions,
FormControl,
MenuItem,
Select,
Typography,
Checkbox,
FormControlLabel,
} from '@mui/material'
import { Dialog, ErrorMessage, LoadingEllipses } from '@jbrowse/core/ui'
import { makeStyles } from 'tss-react/mui'
import { observer } from 'mobx-react'

// locals
import { useFeatureSequence } from '../hooks'
import { SimpleFeatureSerialized } from '../../../util'
import { BaseFeatureWidgetModel } from '../../stateModelFactory'
import AdvancedSequencePanel from './AdvancedSequencePanel'

const useStyles = makeStyles()({
dialogContent: {
width: '80em',
},
formControl: {
margin: 0,
marginLeft: 4,
},
})

const AdvancedSequenceDialog = observer(function ({
handleClose,
model,
feature,
}: {
handleClose: () => void
feature: SimpleFeatureSerialized
model: BaseFeatureWidgetModel
}) {
const { sequenceFeatureDetails } = model
const { intronBp, upDownBp, showCoordinates } = sequenceFeatureDetails
const { classes } = useStyles()
const seqPanelRef = useRef<HTMLDivElement>(null)

const [force, setForce] = useState(false)
const hasCDS = feature.subfeatures?.some(sub => sub.type === 'CDS')
const hasExon = feature.subfeatures?.some(sub => sub.type === 'exon')
const hasExonOrCDS = hasExon || hasCDS
const { sequence, error } = useFeatureSequence(
model,
feature,
upDownBp,
force,
)

const [mode, setMode] = useState(
hasCDS ? 'cds' : hasExon ? 'cdna' : 'genomic',
)
return (
<Dialog
maxWidth="xl"
open
onClose={() => handleClose()}
title="Advanced sequence view dialog"
>
<DialogContent className={classes.dialogContent}>
<div>
<FormControlLabel
label="Show coordinates"
control={
<Checkbox
checked={sequenceFeatureDetails.showCoordinates}
onChange={() =>
sequenceFeatureDetails.setShowCoordinates(
!sequenceFeatureDetails.showCoordinates,
)
}
/>
}
/>
<FormControl className={classes.formControl}>
<Select
size="small"
value={mode}
onChange={event => setMode(event.target.value)}
>
{Object.entries({
...(hasCDS
? {
cds: 'CDS',
}
: {}),
...(hasCDS
? {
protein: 'Protein',
}
: {}),
...(hasExonOrCDS
? {
cdna: 'cDNA',
}
: {}),
...(hasExonOrCDS
? {
gene: `Genomic w/ full introns`,
}
: {}),
...(hasExonOrCDS
? {
gene_updownstream: `Genomic w/ full introns +/- ${upDownBp}bp up+down stream`,
}
: {}),
...(hasExonOrCDS
? {
gene_collapsed_intron: `Genomic w/ ${intronBp}bp intron`,
}
: {}),
...(hasExonOrCDS
? {
gene_updownstream_collapsed_intron: `Genomic w/ ${intronBp}bp intron +/- ${upDownBp}bp up+down stream `,
}
: {}),

...(!hasExonOrCDS
? {
genomic: 'Genomic',
}
: {}),
...(!hasExonOrCDS
? {
genomic_sequence_updownstream: `Genomic +/- ${upDownBp}bp up+down stream`,
}
: {}),
}).map(([key, val]) => (
<MenuItem key={key} value={key}>
{val}
</MenuItem>
))}
</Select>
</FormControl>
</div>

<div>
{feature.type === 'gene' ? (
<Typography>
Note: inspect subfeature sequences for protein/CDS computations
</Typography>
) : null}
{error ? (
<ErrorMessage error={error} />
) : !sequence ? (
<LoadingEllipses />
) : sequence ? (
'error' in sequence ? (
<>
<Typography color="error">{sequence.error}</Typography>
<Button
variant="contained"
color="inherit"
onClick={() => setForce(true)}
>
Force load
</Button>
</>
) : (
<Suspense fallback={<LoadingEllipses />}>
<AdvancedSequencePanel
ref={seqPanelRef}
showCoords={showCoordinates}
feature={feature}
mode={mode}
sequence={sequence}
model={sequenceFeatureDetails}
/>
</Suspense>
)
) : (
<Typography>No sequence found</Typography>
)}
</div>
</DialogContent>

<DialogActions>
<Button onClick={() => handleClose()} variant="contained">
Close
</Button>
</DialogActions>
</Dialog>
)
})

export default AdvancedSequenceDialog

0 comments on commit 7fa6bca

Please sign in to comment.