Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add infrastructure for creating linear-genome-view sub-classes #3227

Merged
merged 9 commits into from
Sep 29, 2022
4 changes: 3 additions & 1 deletion packages/core/PluginManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -518,8 +518,10 @@ export default class PluginManager {
const displays = this.getElementTypesInGroup('display') as DisplayType[]
displays.forEach(display => {
// view may have already added the displayType in its callback
// see ViewType for description of extendedName
if (
display.viewType === newView.name &&
(display.viewType === newView.name ||
display.viewType === newView.extendedName) &&
!newView.displayTypes.includes(display)
) {
newView.addDisplayType(display)
Expand Down
10 changes: 10 additions & 0 deletions packages/core/pluggableElementTypes/ViewType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,24 @@ export default class ViewType extends PluggableElementBase {

displayTypes: DisplayType[] = []

// extendedName can be used for when you extend a given view type, and want
// to register all of that view types displays to yourself e.g. you create a
// linear-genome-view subtype, and want all the tracks that are compatible
// display types for the linear-genome-view to be compatible with your type
// also (without this, display types are only registered to a single view
// type)
extendedName?: string

constructor(stuff: {
name: string
ReactComponent: ViewReactComponent
stateModel: IAnyModelType
extendedName?: string
}) {
super(stuff)
this.ReactComponent = stuff.ReactComponent
this.stateModel = stuff.stateModel
this.extendedName = stuff.extendedName
if (!this.ReactComponent) {
throw new Error(`no ReactComponent defined for view ${this.name}`)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,13 +119,15 @@ const Controls = ({ model }: { model: LGV }) => {
}

const LinearGenomeViewHeader = observer(({ model }: { model: LGV }) => {
return model.hideHeaderOverview ? (
<Controls model={model} />
) : (
<OverviewScaleBar model={model}>
return !model.hideHeader ? (
model.hideHeaderOverview ? (
<Controls model={model} />
</OverviewScaleBar>
)
) : (
<OverviewScaleBar model={model}>
<Controls model={model} />
</OverviewScaleBar>
)
) : null
})

export default LinearGenomeViewHeader
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,9 @@ import { observer } from 'mobx-react'

// locals
import { LinearGenomeViewModel } from '..'
import Header from './Header'
import TrackContainer from './TrackContainer'
import TracksContainer from './TracksContainer'
import ImportForm from './ImportForm'
import MiniControls from './MiniControls'
import GetSequenceDialog from './GetSequenceDialog'
import SearchResultsDialog from './SearchResultsDialog'

Expand Down Expand Up @@ -47,7 +45,7 @@ const useStyles = makeStyles()(theme => ({
}))

const LinearGenomeView = observer(({ model }: { model: LGV }) => {
const { tracks, error, hideHeader, initialized, hasDisplayedRegions } = model
const { tracks, error, initialized, hasDisplayedRegions } = model
const { classes } = useStyles()

if (!initialized && !error) {
Expand All @@ -61,6 +59,9 @@ const LinearGenomeView = observer(({ model }: { model: LGV }) => {
return <ImportForm model={model} />
}

const MiniControlsComponent = model.MiniControlsComponent()
const HeaderComponent = model.HeaderComponent()

return (
<div style={{ position: 'relative' }}>
{model.seqDialogDisplayed ? (
Expand All @@ -75,19 +76,8 @@ const LinearGenomeView = observer(({ model }: { model: LGV }) => {
handleClose={() => model.setSearchResults(undefined, undefined)}
/>
) : null}
{!hideHeader ? (
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is there a reason for the mllv why hideHeader got moved into the minicontrols/header?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, MLLV has its own conditions for rendering the header, and if I recall correctly moving these conditions into their own components fixed an issue where the header was being duplicated or not shown at all.

<Header model={model} />
) : (
<div
style={{
position: 'absolute',
right: 0,
zIndex: 1001,
}}
>
<MiniControls model={model} />
</div>
)}
<HeaderComponent model={model} />
<MiniControlsComponent model={model} />
<TracksContainer model={model}>
{!tracks.length ? (
<Paper variant="outlined" className={classes.note}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,46 +9,48 @@ import { LinearGenomeViewModel } from '..'

const MiniControls = observer((props: { model: LinearGenomeViewModel }) => {
const { model } = props
const { bpPerPx, maxBpPerPx, minBpPerPx, scaleFactor } = model
const { bpPerPx, maxBpPerPx, minBpPerPx, scaleFactor, hideHeader } = model
const [anchorEl, setAnchorEl] = useState<HTMLElement>()

return (
<Paper style={{ background: '#aaa7' }}>
<IconButton
color="secondary"
onClick={event => setAnchorEl(event.currentTarget)}
>
<ArrowDown fontSize="small" />
</IconButton>
return hideHeader ? (
<div style={{ position: 'absolute', right: '0px', zIndex: '1001' }}>
<Paper style={{ background: '#aaa7' }}>
<IconButton
color="secondary"
onClick={event => setAnchorEl(event.currentTarget)}
>
<ArrowDown fontSize="small" />
</IconButton>

<IconButton
data-testid="zoom_out"
onClick={() => model.zoom(bpPerPx * 2)}
disabled={bpPerPx >= maxBpPerPx - 0.0001 || scaleFactor !== 1}
color="secondary"
>
<ZoomOut fontSize="small" />
</IconButton>
<IconButton
data-testid="zoom_in"
onClick={() => model.zoom(model.bpPerPx / 2)}
disabled={bpPerPx <= minBpPerPx + 0.0001 || scaleFactor !== 1}
color="secondary"
>
<ZoomIn fontSize="small" />
</IconButton>
<Menu
anchorEl={anchorEl}
open={Boolean(anchorEl)}
onMenuItemClick={(_, callback) => {
callback()
setAnchorEl(undefined)
}}
onClose={() => setAnchorEl(undefined)}
menuItems={model.menuItems()}
/>
</Paper>
)
<IconButton
data-testid="zoom_out"
onClick={() => model.zoom(bpPerPx * 2)}
disabled={bpPerPx >= maxBpPerPx - 0.0001 || scaleFactor !== 1}
color="secondary"
>
<ZoomOut fontSize="small" />
</IconButton>
<IconButton
data-testid="zoom_in"
onClick={() => model.zoom(model.bpPerPx / 2)}
disabled={bpPerPx <= minBpPerPx + 0.0001 || scaleFactor !== 1}
color="secondary"
>
<ZoomIn fontSize="small" />
</IconButton>
<Menu
anchorEl={anchorEl}
open={Boolean(anchorEl)}
onMenuItemClick={(_, callback) => {
callback()
setAnchorEl(undefined)
}}
onClose={() => setAnchorEl(undefined)}
menuItems={model.menuItems()}
/>
</Paper>
</div>
) : null
})

export default MiniControls
24 changes: 22 additions & 2 deletions plugins/linear-genome-view/src/LinearGenomeView/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { lazy } from 'react'
import React, { lazy } from 'react'
import { getConf, AnyConfigurationModel } from '@jbrowse/core/configuration'
import { BaseViewModel } from '@jbrowse/core/pluggableElementTypes/models'
import { Region } from '@jbrowse/core/util/types'
Expand Down Expand Up @@ -53,6 +53,10 @@ import { renderToSvg } from './components/LinearGenomeViewSvg'
import RefNameAutocomplete from './components/RefNameAutocomplete'
import SearchBox from './components/SearchBox'
import ExportSvgDlg from './components/ExportSvgDialog'
import MiniControls from './components/MiniControls'
import Header from './components/Header'
import ZoomControls from './components/ZoomControls'
import LinearGenomeView from './components/LinearGenomeView'

const SequenceSearchDialog = lazy(
() => import('./components/SequenceSearchDialog'),
Expand Down Expand Up @@ -192,6 +196,16 @@ export function stateModelFactory(pluginManager: PluginManager) {
},
}))
.views(self => ({
// eslint-disable-next-line @typescript-eslint/no-explicit-any
MiniControlsComponent(): React.FC<any> {
return MiniControls
},

// eslint-disable-next-line @typescript-eslint/no-explicit-any
HeaderComponent(): React.FC<any> {
return Header
},

get assemblyErrors() {
const { assemblyManager } = getSession(self)
const { assemblyNames } = self
Expand Down Expand Up @@ -1220,7 +1234,13 @@ export function stateModelFactory(pluginManager: PluginManager) {
}))
}

export { renderToSvg, RefNameAutocomplete, SearchBox }
export {
renderToSvg,
RefNameAutocomplete,
SearchBox,
ZoomControls,
LinearGenomeView,
}
export type LinearGenomeViewStateModel = ReturnType<typeof stateModelFactory>
export type LinearGenomeViewModel = Instance<LinearGenomeViewStateModel>
export { default as ReactComponent } from './components/LinearGenomeView'
5 changes: 5 additions & 0 deletions plugins/linear-genome-view/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ import {
renderToSvg,
RefNameAutocomplete,
SearchBox,
ZoomControls,
LinearGenomeView,
} from './LinearGenomeView'

import {
Expand All @@ -45,6 +47,9 @@ export default class LinearGenomeViewPlugin extends Plugin {
BaseLinearDisplayComponent,
BaseLinearDisplay,
baseLinearDisplayConfigSchema,
SearchBox,
ZoomControls,
LinearGenomeView,
}

install(pluginManager: PluginManager) {
Expand Down