UNPKG

@jbrowse/plugin-wiggle

Version:

JBrowse 2 wiggle adapters, tracks, etc.

260 lines (259 loc) 9.86 kB
import { lazy } from 'react'; import { getConf } from '@jbrowse/core/configuration'; import { getContainingView, getSession } from '@jbrowse/core/util'; import { types } from '@jbrowse/mobx-state-tree'; import EqualizerIcon from '@mui/icons-material/Equalizer'; import PaletteIcon from '@mui/icons-material/Palette'; import VisibilityIcon from '@mui/icons-material/Visibility'; import SharedWiggleMixin from "../shared/SharedWiggleMixin.js"; import axisPropsFromTickScale from "../shared/axisPropsFromTickScale.js"; import { YSCALEBAR_LABEL_OFFSET, getScale } from "../util.js"; const Tooltip = lazy(() => import("./components/Tooltip.js")); const SetColorDialog = lazy(() => import("./components/SetColorDialog.js")); const rendererTypes = new Map([ ['xyplot', 'XYPlotRenderer'], ['density', 'DensityRenderer'], ['line', 'LinePlotRenderer'], ]); function stateModelFactory(pluginManager, configSchema) { return types .compose('LinearWiggleDisplay', SharedWiggleMixin(configSchema), types.model({ type: types.literal('LinearWiggleDisplay'), invertedSetting: types.maybe(types.boolean), })) .actions(self => ({ setInverted(arg) { self.invertedSetting = arg; }, })) .views(self => ({ get TooltipComponent() { return Tooltip; }, get rendererTypeName() { const name = self.rendererTypeNameSimple; const rendererType = rendererTypes.get(name); if (!rendererType) { throw new Error(`unknown renderer ${name}`); } return rendererType; }, get quantitativeStatsRelevantToCurrentZoom() { const view = getContainingView(self); return self.stats?.currStatsBpPerPx === view.bpPerPx; }, get graphType() { return (self.rendererTypeName === 'XYPlotRenderer' || self.rendererTypeName === 'LinePlotRenderer'); }, get inverted() { return self.invertedSetting ?? getConf(self, 'inverted'); }, })) .views(self => { const { renderProps: superRenderProps } = self; return { adapterProps() { const superProps = superRenderProps(); const { filters, resolution, scaleOpts } = self; return { ...superProps, rpcDriverName: self.effectiveRpcDriverName, config: self.rendererConfig, displayCrossHatches: self.displayCrossHatchesSetting, scaleOpts, resolution, filters, }; }, get ticks() { const { inverted, scaleType, domain, height } = self; const minimalTicks = getConf(self, 'minimalTicks'); if (domain) { const ticks = axisPropsFromTickScale(getScale({ scaleType, domain, range: [ height - YSCALEBAR_LABEL_OFFSET, YSCALEBAR_LABEL_OFFSET, ], inverted, }), 4); return height < 100 || minimalTicks ? { ...ticks, values: domain } : ticks; } else { return undefined; } }, }; }) .views(self => ({ renderProps() { const { inverted, ticks, height, domain } = self; const superProps = self.adapterProps(); const view = getContainingView(self); const statsRegion = JSON.stringify(view.dynamicBlocks); return { ...self.adapterProps(), notReady: superProps.notReady || !domain || self.stats?.statsRegion !== statsRegion, height, ticks, inverted, offset: YSCALEBAR_LABEL_OFFSET, }; }, get fillSetting() { if (self.filled) { return 0; } else if (self.minSize === 1) { return 1; } else { return 2; } }, get quantitativeStatsReady() { const view = getContainingView(self); return (view.initialized && self.featureDensityStatsReadyAndRegionNotTooLarge && !self.error); }, })) .views(self => { const { trackMenuItems: superTrackMenuItems } = self; const hasRenderings = getConf(self, 'defaultRendering'); return { wiggleBaseTrackMenuItems() { return [ ...superTrackMenuItems(), { label: 'Score', icon: EqualizerIcon, subMenu: self.scoreTrackMenuItems(), }, ]; }, wiggleOnlyTrackMenuItems() { return [ ...(self.canHaveFill ? [ { label: 'Fill mode', subMenu: ['filled', 'no fill', 'no fill w/ emphasis'].map((elt, idx) => ({ label: elt, type: 'radio', checked: self.fillSetting === idx, onClick: () => { self.setFill(idx); }, })), }, ] : []), ...(hasRenderings ? [ { label: 'Renderer type', subMenu: ['xyplot', 'density', 'line'].map(key => ({ label: key, type: 'radio', checked: self.rendererTypeNameSimple === key, onClick: () => { self.setRendererType(key); }, })), }, ] : []), { label: 'Color', icon: PaletteIcon, onClick: () => { getSession(self).queueDialog(handleClose => [ SetColorDialog, { model: self, handleClose, }, ]); }, }, ]; }, }; }) .views(self => ({ trackMenuItems() { return [ ...self.wiggleBaseTrackMenuItems(), { label: 'Show...', icon: VisibilityIcon, subMenu: [ { label: 'Show tooltips', type: 'checkbox', checked: self.showTooltipsEnabled, onClick: () => { self.setShowTooltips(!self.showTooltipsEnabled); }, }, ...(self.graphType ? [ { label: 'Show inverted', type: 'checkbox', checked: self.inverted, onClick: () => { self.setInverted(!self.inverted); }, }, { label: 'Show cross hatches', type: 'checkbox', checked: self.displayCrossHatchesSetting, onClick: () => { self.toggleCrossHatches(); }, }, ] : []), ], }, ...self.wiggleOnlyTrackMenuItems(), ]; }, })) .actions(self => { const { renderSvg: superRenderSvg } = self; return { afterAttach() { ; (async () => { const { getQuantitativeStatsAutorun } = await import("../getQuantitativeStatsAutorun.js"); getQuantitativeStatsAutorun(self); })(); }, async renderSvg(opts) { const { renderSvg } = await import("./renderSvg.js"); return renderSvg(self, opts, superRenderSvg); }, }; }) .postProcessSnapshot(snap => { if (!snap) { return snap; } const { invertedSetting, ...rest } = snap; return { ...rest, ...(invertedSetting !== undefined ? { invertedSetting } : {}), }; }); } export default stateModelFactory;