Skip to content

Commit

Permalink
Allow adding session tracks to embedded react component along with di…
Browse files Browse the repository at this point in the history
…sableAddTracks option if unwanted (#3223)

* Add session tracks to embedded

* Enable adding tracks to embedded, with a flag to disable it at view creation

* Allow passing props to extension point
  • Loading branch information
cmdcolin committed Sep 29, 2022
1 parent ea9df34 commit c6facdc
Show file tree
Hide file tree
Showing 11 changed files with 148 additions and 72 deletions.
13 changes: 9 additions & 4 deletions packages/core/PluginManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -554,7 +554,7 @@ export default class PluginManager {

addToExtensionPoint<T>(
extensionPointName: string,
callback: (extendee: T) => T,
callback: (extendee: T, props: Record<string, unknown>) => T,
) {
let callbacks = this.extensionPoints.get(extensionPointName)
if (!callbacks) {
Expand All @@ -564,13 +564,17 @@ export default class PluginManager {
callbacks.push(callback)
}

evaluateExtensionPoint(extensionPointName: string, extendee: unknown) {
evaluateExtensionPoint(
extensionPointName: string,
extendee: unknown,
props?: Record<string, unknown>,
) {
const callbacks = this.extensionPoints.get(extensionPointName)
let accumulator = extendee
if (callbacks) {
for (const callback of callbacks) {
try {
accumulator = callback(accumulator)
accumulator = callback(accumulator, props)
} catch (error) {
console.error(error)
}
Expand All @@ -582,13 +586,14 @@ export default class PluginManager {
async evaluateAsyncExtensionPoint(
extensionPointName: string,
extendee: unknown,
props?: Record<string, unknown>,
) {
const callbacks = this.extensionPoints.get(extensionPointName)
let accumulator = extendee
if (callbacks) {
for (const callback of callbacks) {
try {
accumulator = await callback(accumulator)
accumulator = await callback(accumulator, props)
} catch (error) {
console.error(error)
}
Expand Down
5 changes: 4 additions & 1 deletion packages/core/util/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,10 @@ export interface SessionWithConfigEditing extends AbstractSessionModel {
export function isSessionWithAddTracks(
thing: unknown,
): thing is SessionWithConfigEditing {
return isSessionModel(thing) && 'addTrackConf' in thing
return (
// @ts-ignore
isSessionModel(thing) && 'addTrackConf' in thing && !thing.disableAddTracks
)
}

export interface Widget {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,8 @@ function HierarchicalTrackSelectorHeader({

const items = getEnv(model).pluginManager.evaluateExtensionPoint(
'TrackSelector-multiTrackMenuItems',
[],
{ session },
) as MenuItem[]
return (
<div
Expand Down
25 changes: 15 additions & 10 deletions plugins/linear-genome-view/src/LinearGenomeView/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
measureText,
parseLocString,
springAnimate,
isSessionWithAddTracks,
} from '@jbrowse/core/util'
import BaseResult from '@jbrowse/core/TextSearch/BaseResults'
import { BlockSet, BaseBlock } from '@jbrowse/core/util/blockTypes'
Expand Down Expand Up @@ -721,7 +722,7 @@ export function stateModelFactory(pluginManager: PluginManager) {
.views(self => ({
menuItems(): MenuItem[] {
const { canShowCytobands, showCytobands } = self

const session = getSession(self)
const menuItems: MenuItem[] = [
{
label: 'Return to import form',
Expand All @@ -733,15 +734,19 @@ export function stateModelFactory(pluginManager: PluginManager) {
},
icon: FolderOpenIcon,
},
{
label: 'Sequence search',
onClick: () => {
getSession(self).queueDialog(handleClose => [
SequenceSearchDialog,
{ model: self, handleClose },
])
},
},
...(isSessionWithAddTracks(session)
? [
{
label: 'Sequence search',
onClick: () => {
getSession(self).queueDialog(handleClose => [
SequenceSearchDialog,
{ model: self, handleClose },
])
},
},
]
: []),
{
label: 'Export SVG',
icon: PhotoCameraIcon,
Expand Down
103 changes: 57 additions & 46 deletions plugins/wiggle/src/CreateMultiWiggleExtension/index.ts
Original file line number Diff line number Diff line change
@@ -1,57 +1,68 @@
import { lazy } from 'react'
import PluginManager from '@jbrowse/core/PluginManager'
import { readConfObject } from '@jbrowse/core/configuration'
import { getSession } from '@jbrowse/core/util'
import { getSession, isSessionWithAddTracks } from '@jbrowse/core/util'
import { HierarchicalTrackSelectorModel } from '@jbrowse/plugin-data-management'

const ConfirmDialog = lazy(() => import('./ConfirmDialog'))

export default function (pm: PluginManager) {
pm.addToExtensionPoint('TrackSelector-multiTrackMenuItems', () => {
return [
{
label: 'Create multi-wiggle track',
onClick: (model: HierarchicalTrackSelectorModel) => {
const tracks = model.selection
const trackIds = tracks.map(c => readConfObject(c, 'name'))
function makeTrack(arg: { name: string }) {
const subadapters = tracks
.map(c => readConfObject(c, 'adapter'))
.map((c, idx) => ({ ...c, source: trackIds[idx] }))
const assemblyNames = [
...new Set(
tracks.map(c => readConfObject(c, 'assemblyNames')).flat(),
),
]
const now = +Date.now()
const trackId = `multitrack-${now}-sessionTrack`
pm.addToExtensionPoint(
'TrackSelector-multiTrackMenuItems',
(items: unknown[], props: Record<string, unknown>) => {
const { session } = props
return [
...items,
...(isSessionWithAddTracks(session)
? [
{
label: 'Create multi-wiggle track',
onClick: (model: HierarchicalTrackSelectorModel) => {
const tracks = model.selection
const trackIds = tracks.map(c => readConfObject(c, 'name'))
function makeTrack(arg: { name: string }) {
const subadapters = tracks
.map(c => readConfObject(c, 'adapter'))
.map((c, idx) => ({ ...c, source: trackIds[idx] }))
const assemblyNames = [
...new Set(
tracks
.map(c => readConfObject(c, 'assemblyNames'))
.flat(),
),
]
const now = +Date.now()
const trackId = `multitrack-${now}-sessionTrack`

getSession(model).addTrackConf({
type: 'MultiQuantitativeTrack',
trackId,
name: arg.name,
assemblyNames,
adapter: {
type: 'MultiWiggleAdapter',
subadapters,
},
})
model.view.showTrack(trackId)
}
getSession(model).queueDialog(handleClose => [
ConfirmDialog,
{
tracks,
onClose: (arg: boolean, arg1: { name: string }) => {
if (arg) {
makeTrack(arg1)
}
handleClose()
getSession(model).addTrackConf({
type: 'MultiQuantitativeTrack',
trackId,
name: arg.name,
assemblyNames,
adapter: {
type: 'MultiWiggleAdapter',
subadapters,
},
})
model.view.showTrack(trackId)
}
getSession(model).queueDialog(handleClose => [
ConfirmDialog,
{
tracks,
onClose: (arg: boolean, arg1: { name: string }) => {
if (arg) {
makeTrack(arg1)
}
handleClose()
},
},
])
},
},
},
])
},
},
]
})
]
: []),
]
},
)
}
2 changes: 1 addition & 1 deletion plugins/wiggle/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import PluginManager from '@jbrowse/core/PluginManager'
import { FileLocation } from '@jbrowse/core/util/types'
import {
AdapterGuesser,
getFileName,
TrackTypeGuesser,
getFileName,
} from '@jbrowse/core/util/tracks'

// locals
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,13 @@ export default function createModel(runtimePlugins: PluginConstructor[]) {
pluginManager.createPluggableElements()
const Session = createSessionModel(pluginManager)
const assemblyConfig = assemblyConfigSchemaFactory(pluginManager)
const assemblyManagerType = assemblyManagerFactory(
assemblyConfig,
pluginManager,
)
const AssemblyManager = assemblyManagerFactory(assemblyConfig, pluginManager)
const rootModel = types
.model('ReactLinearGenomeView', {
config: createConfigModel(pluginManager, assemblyConfig),
session: Session,
assemblyManager: assemblyManagerType,
assemblyManager: types.optional(AssemblyManager, {}),
disableAddTracks: types.optional(types.boolean, false),
})
.volatile(() => ({
error: undefined as Error | undefined,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ export default function sessionModelFactory(pluginManager: PluginManager) {
connectionInstances: types.array(
pluginManager.pluggableMstType('connection', 'stateModel'),
),
sessionTracks: types.array(
pluginManager.pluggableConfigSchemaType('track'),
),
})
.volatile((/* self */) => ({
/**
Expand All @@ -69,17 +72,18 @@ export default function sessionModelFactory(pluginManager: PluginManager) {
queueOfDialogs: [] as [DialogComponentType, any][],
}))
.views(self => ({
get disableAddTracks() {
return getParent<any>(self).disableAddTracks
},
get DialogComponent() {
if (self.queueOfDialogs.length) {
const firstInQueue = self.queueOfDialogs[0]
return firstInQueue && firstInQueue[0]
return self.queueOfDialogs[0]?.[0]
}
return undefined
},
get DialogProps() {
if (self.queueOfDialogs.length) {
const firstInQueue = self.queueOfDialogs[0]
return firstInQueue && firstInQueue[1]
return self.queueOfDialogs[0]?.[1]
}
return undefined
},
Expand Down Expand Up @@ -155,6 +159,18 @@ export default function sessionModelFactory(pluginManager: PluginManager) {
},
}))
.actions(self => ({
addTrackConf(trackConf: AnyConfigurationModel) {
const { trackId, type } = trackConf
if (!type) {
throw new Error(`unknown track type ${type}`)
}
const track = self.sessionTracks.find((t: any) => t.trackId === trackId)
if (track) {
return track
}
const length = self.sessionTracks.push(trackConf)
return self.sessionTracks[length - 1]
},
queueDialog(
callback: (doneCallback: () => void) => [DialogComponentType, any],
): void {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ interface ViewStateOptions {
plugins?: PluginConstructor[]
location?: string | Location
defaultSession?: SessionSnapshot
disableAddTracks?: boolean
onChange?: (patch: IJsonPatch, reversePatch: IJsonPatch) => void
}

Expand All @@ -39,6 +40,7 @@ export default function createViewState(opts: ViewStateOptions) {
plugins,
location,
onChange,
disableAddTracks = false,
} = opts
const { model, pluginManager } = createModel(plugins || [])
let { defaultSession } = opts
Expand All @@ -59,7 +61,7 @@ export default function createViewState(opts: ViewStateOptions) {
aggregateTextSearchAdapters,
defaultSession,
},
assemblyManager: {},
disableAddTracks,
session: defaultSession,
}
const stateTree = model.create(stateSnapshot, { pluginManager })
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,28 @@ Example
<Story id="linear-view--using-loc-object" />
</Canvas>

## Disabling the ability to add tracks

By default users can add tracks, but you can add the `disableAddTracks` flag to
prevent this. Note that this prevents the ability to create e.g. sequence search
tracks or multi-wiggle tracks too.

```js
const state = createViewState({
assembly,
tracks,
location: '10:29,838,737..29,838,819',
defaultSession,
disableAddTracks: true,
})
```

Example

<Canvas withSource="open">
<Story id="linear-view--disable-add-tracks" />
</Canvas>

## Opening up tracks by default

You can use the "showTrack" function on the object returned by
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,18 @@ export const UsingLocObject = () => {
return <JBrowseLinearGenomeView viewState={state} />
}

export const DisableAddTracks = () => {
const state = createViewState({
assembly,
tracks,
defaultSession,
// use 0-based coordinates for "location object" here
location: { refName: 'ctgA', start: 10000, end: 20000 },
disableAddTracks: true,
})
return <JBrowseLinearGenomeView viewState={state} />
}

export const WithLongReads = () => {
const state = createViewState({
assembly,
Expand Down

0 comments on commit c6facdc

Please sign in to comment.