@jbrowse/plugin-sv-inspector 2.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/dist/LaunchSvInspectorView/index.d.ts +3 -0
- package/dist/LaunchSvInspectorView/index.js +27 -0
- package/dist/LaunchSvInspectorView/index.js.map +1 -0
- package/dist/SvInspectorView/components/CircularViewOptions.d.ts +6 -0
- package/dist/SvInspectorView/components/CircularViewOptions.js +22 -0
- package/dist/SvInspectorView/components/CircularViewOptions.js.map +1 -0
- package/dist/SvInspectorView/components/SvInspectorView.d.ts +6 -0
- package/dist/SvInspectorView/components/SvInspectorView.js +51 -0
- package/dist/SvInspectorView/components/SvInspectorView.js.map +1 -0
- package/dist/SvInspectorView/index.d.ts +3 -0
- package/dist/SvInspectorView/index.js +35 -0
- package/dist/SvInspectorView/index.js.map +1 -0
- package/dist/SvInspectorView/models/SvInspectorView.d.ts +881 -0
- package/dist/SvInspectorView/models/SvInspectorView.js +317 -0
- package/dist/SvInspectorView/models/SvInspectorView.js.map +1 -0
- package/dist/SvInspectorView/models/adhocFeatureUtils.d.ts +44 -0
- package/dist/SvInspectorView/models/adhocFeatureUtils.js +97 -0
- package/dist/SvInspectorView/models/adhocFeatureUtils.js.map +1 -0
- package/dist/SvInspectorView/models/breakpointSplitViewFromTableRow.d.ts +4 -0
- package/dist/SvInspectorView/models/breakpointSplitViewFromTableRow.js +49 -0
- package/dist/SvInspectorView/models/breakpointSplitViewFromTableRow.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +35 -0
- package/dist/index.js.map +1 -0
- package/esm/LaunchSvInspectorView/index.d.ts +3 -0
- package/esm/LaunchSvInspectorView/index.js +25 -0
- package/esm/LaunchSvInspectorView/index.js.map +1 -0
- package/esm/SvInspectorView/components/CircularViewOptions.d.ts +6 -0
- package/esm/SvInspectorView/components/CircularViewOptions.js +17 -0
- package/esm/SvInspectorView/components/CircularViewOptions.js.map +1 -0
- package/esm/SvInspectorView/components/SvInspectorView.d.ts +6 -0
- package/esm/SvInspectorView/components/SvInspectorView.js +46 -0
- package/esm/SvInspectorView/components/SvInspectorView.js.map +1 -0
- package/esm/SvInspectorView/index.d.ts +3 -0
- package/esm/SvInspectorView/index.js +30 -0
- package/esm/SvInspectorView/index.js.map +1 -0
- package/esm/SvInspectorView/models/SvInspectorView.d.ts +881 -0
- package/esm/SvInspectorView/models/SvInspectorView.js +312 -0
- package/esm/SvInspectorView/models/SvInspectorView.js.map +1 -0
- package/esm/SvInspectorView/models/adhocFeatureUtils.d.ts +44 -0
- package/esm/SvInspectorView/models/adhocFeatureUtils.js +90 -0
- package/esm/SvInspectorView/models/adhocFeatureUtils.js.map +1 -0
- package/esm/SvInspectorView/models/breakpointSplitViewFromTableRow.d.ts +4 -0
- package/esm/SvInspectorView/models/breakpointSplitViewFromTableRow.js +42 -0
- package/esm/SvInspectorView/models/breakpointSplitViewFromTableRow.js.map +1 -0
- package/esm/index.d.ts +7 -0
- package/esm/index.js +29 -0
- package/esm/index.js.map +1 -0
- package/package.json +62 -0
- package/src/LaunchSvInspectorView/index.ts +41 -0
- package/src/SvInspectorView/components/CircularViewOptions.tsx +47 -0
- package/src/SvInspectorView/components/SvInspectorView.tsx +81 -0
- package/src/SvInspectorView/index.ts +41 -0
- package/src/SvInspectorView/models/SvInspectorView.ts +406 -0
- package/src/SvInspectorView/models/adhocFeatureUtils.js +152 -0
- package/src/SvInspectorView/models/breakpointSplitViewFromTableRow.js +84 -0
- package/src/index.ts +34 -0
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import PluginManager from '@jbrowse/core/PluginManager'
|
|
2
|
+
import { AbstractSessionModel } from '@jbrowse/core/util'
|
|
3
|
+
import { SvInspectorViewModel } from '../SvInspectorView/models/SvInspectorView'
|
|
4
|
+
|
|
5
|
+
export default (pluginManager: PluginManager) => {
|
|
6
|
+
pluginManager.addToExtensionPoint(
|
|
7
|
+
'LaunchView-SvInspectorView',
|
|
8
|
+
// @ts-expect-error
|
|
9
|
+
async ({
|
|
10
|
+
session,
|
|
11
|
+
assembly,
|
|
12
|
+
uri,
|
|
13
|
+
fileType,
|
|
14
|
+
}: {
|
|
15
|
+
session: AbstractSessionModel
|
|
16
|
+
assembly: string
|
|
17
|
+
uri: string
|
|
18
|
+
fileType?: string
|
|
19
|
+
}) => {
|
|
20
|
+
// add view, make typescript happy with return type
|
|
21
|
+
const view = session.addView('SvInspectorView') as SvInspectorViewModel
|
|
22
|
+
|
|
23
|
+
if (!view) {
|
|
24
|
+
throw new Error('Failed to initialize view')
|
|
25
|
+
}
|
|
26
|
+
const exts = uri.split('.')
|
|
27
|
+
let ext = exts?.pop()?.toUpperCase()
|
|
28
|
+
if (ext === 'GZ') {
|
|
29
|
+
ext = exts?.pop()?.toUpperCase()
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
view.spreadsheetView.importWizard.setFileType(fileType || ext || '')
|
|
33
|
+
view.spreadsheetView.importWizard.setSelectedAssemblyName(assembly)
|
|
34
|
+
view.spreadsheetView.importWizard.setFileSource({
|
|
35
|
+
uri,
|
|
36
|
+
locationType: 'UriLocation',
|
|
37
|
+
})
|
|
38
|
+
await view.spreadsheetView.importWizard.import(assembly)
|
|
39
|
+
},
|
|
40
|
+
)
|
|
41
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { observer } from 'mobx-react'
|
|
3
|
+
import { Grid, FormControlLabel, Checkbox } from '@mui/material'
|
|
4
|
+
import { makeStyles } from 'tss-react/mui'
|
|
5
|
+
|
|
6
|
+
// locals
|
|
7
|
+
import { SvInspectorViewModel } from '../models/SvInspectorView'
|
|
8
|
+
|
|
9
|
+
const useStyles = makeStyles()(theme => ({
|
|
10
|
+
circularViewOptions: {
|
|
11
|
+
padding: theme.spacing(1),
|
|
12
|
+
},
|
|
13
|
+
}))
|
|
14
|
+
|
|
15
|
+
const CircularViewOptions = observer(function ({
|
|
16
|
+
svInspector,
|
|
17
|
+
}: {
|
|
18
|
+
svInspector: SvInspectorViewModel
|
|
19
|
+
}) {
|
|
20
|
+
const { classes } = useStyles()
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
<Grid
|
|
24
|
+
container
|
|
25
|
+
className={classes.circularViewOptions}
|
|
26
|
+
style={{ height: svInspector.circularViewOptionsBarHeight }}
|
|
27
|
+
>
|
|
28
|
+
<Grid item>
|
|
29
|
+
<FormControlLabel
|
|
30
|
+
control={
|
|
31
|
+
<Checkbox
|
|
32
|
+
checked={svInspector.onlyDisplayRelevantRegionsInCircularView}
|
|
33
|
+
onChange={e =>
|
|
34
|
+
svInspector.setOnlyDisplayRelevantRegionsInCircularView(
|
|
35
|
+
e.target.checked,
|
|
36
|
+
)
|
|
37
|
+
}
|
|
38
|
+
/>
|
|
39
|
+
}
|
|
40
|
+
label="show only regions with data"
|
|
41
|
+
/>
|
|
42
|
+
</Grid>
|
|
43
|
+
</Grid>
|
|
44
|
+
)
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
export default CircularViewOptions
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { observer } from 'mobx-react'
|
|
3
|
+
import { makeStyles } from 'tss-react/mui'
|
|
4
|
+
import { ResizeHandle } from '@jbrowse/core/ui'
|
|
5
|
+
|
|
6
|
+
// locals
|
|
7
|
+
import { SvInspectorViewModel } from '../models/SvInspectorView'
|
|
8
|
+
import CircularViewOptions from './CircularViewOptions'
|
|
9
|
+
|
|
10
|
+
const useStyles = makeStyles()(theme => ({
|
|
11
|
+
resizeHandleVert: {
|
|
12
|
+
background: theme.palette.action.selected,
|
|
13
|
+
width: 4,
|
|
14
|
+
boxSizing: 'border-box',
|
|
15
|
+
borderTop: '1px solid #fafafa',
|
|
16
|
+
},
|
|
17
|
+
resizeHandleHoriz: {
|
|
18
|
+
background: theme.palette.action.selected,
|
|
19
|
+
height: 4,
|
|
20
|
+
boxSizing: 'border-box',
|
|
21
|
+
borderTop: '1px solid #fafafa',
|
|
22
|
+
},
|
|
23
|
+
viewControls: {
|
|
24
|
+
margin: 0,
|
|
25
|
+
},
|
|
26
|
+
viewsContainer: {
|
|
27
|
+
display: 'flex',
|
|
28
|
+
},
|
|
29
|
+
container: {
|
|
30
|
+
overflow: 'hidden',
|
|
31
|
+
},
|
|
32
|
+
}))
|
|
33
|
+
|
|
34
|
+
export default observer(function SvInspectorView({
|
|
35
|
+
model,
|
|
36
|
+
}: {
|
|
37
|
+
model: SvInspectorViewModel
|
|
38
|
+
}) {
|
|
39
|
+
const { classes } = useStyles()
|
|
40
|
+
|
|
41
|
+
const {
|
|
42
|
+
SpreadsheetViewReactComponent,
|
|
43
|
+
CircularViewReactComponent,
|
|
44
|
+
showCircularView,
|
|
45
|
+
} = model
|
|
46
|
+
|
|
47
|
+
return (
|
|
48
|
+
<div className={classes.container}>
|
|
49
|
+
<div className={classes.viewsContainer}>
|
|
50
|
+
<div
|
|
51
|
+
style={{ width: model.spreadsheetView.width }}
|
|
52
|
+
className={classes.container}
|
|
53
|
+
>
|
|
54
|
+
<SpreadsheetViewReactComponent model={model.spreadsheetView} />
|
|
55
|
+
</div>
|
|
56
|
+
|
|
57
|
+
{showCircularView ? (
|
|
58
|
+
<>
|
|
59
|
+
<ResizeHandle
|
|
60
|
+
onDrag={distance => {
|
|
61
|
+
const ret1 = model.circularView.resizeWidth(-distance)
|
|
62
|
+
return model.spreadsheetView.resizeWidth(-ret1)
|
|
63
|
+
}}
|
|
64
|
+
vertical
|
|
65
|
+
flexbox
|
|
66
|
+
className={classes.resizeHandleVert}
|
|
67
|
+
/>
|
|
68
|
+
<div style={{ width: model.circularView.width }}>
|
|
69
|
+
<CircularViewOptions svInspector={model} />
|
|
70
|
+
<CircularViewReactComponent model={model.circularView} />
|
|
71
|
+
</div>
|
|
72
|
+
</>
|
|
73
|
+
) : null}
|
|
74
|
+
</div>
|
|
75
|
+
<ResizeHandle
|
|
76
|
+
onDrag={model.resizeHeight}
|
|
77
|
+
className={classes.resizeHandleHoriz}
|
|
78
|
+
/>
|
|
79
|
+
</div>
|
|
80
|
+
)
|
|
81
|
+
})
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import PluginManager from '@jbrowse/core/PluginManager'
|
|
2
|
+
import ViewType from '@jbrowse/core/pluggableElementTypes/ViewType'
|
|
3
|
+
|
|
4
|
+
import ReactComponent from './components/SvInspectorView'
|
|
5
|
+
import stateModelFactory from './models/SvInspectorView'
|
|
6
|
+
import { Feature, getContainingView, getSession } from '@jbrowse/core/util'
|
|
7
|
+
import { IAnyStateTreeNode } from 'mobx-state-tree'
|
|
8
|
+
|
|
9
|
+
function defaultOnChordClick(
|
|
10
|
+
feature: Feature,
|
|
11
|
+
chordTrack: IAnyStateTreeNode,
|
|
12
|
+
pluginManager: PluginManager,
|
|
13
|
+
) {
|
|
14
|
+
const session = getSession(chordTrack)
|
|
15
|
+
session.setSelection(feature)
|
|
16
|
+
const view = getContainingView(chordTrack)
|
|
17
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
18
|
+
const viewType = pluginManager.getViewType('BreakpointSplitView') as any
|
|
19
|
+
const viewSnapshot = viewType.snapshotFromBreakendFeature(feature, view)
|
|
20
|
+
|
|
21
|
+
// try to center the offsetPx
|
|
22
|
+
viewSnapshot.views[0].offsetPx -= view.width / 2 + 100
|
|
23
|
+
viewSnapshot.views[1].offsetPx -= view.width / 2 + 100
|
|
24
|
+
viewSnapshot.featureData = feature.toJSON()
|
|
25
|
+
|
|
26
|
+
session.addView('BreakpointSplitView', viewSnapshot)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export default (pluginManager: PluginManager) => {
|
|
30
|
+
pluginManager.jexl.addFunction('defaultOnChordClick', defaultOnChordClick)
|
|
31
|
+
|
|
32
|
+
pluginManager.addViewType(() => {
|
|
33
|
+
const stateModel = stateModelFactory(pluginManager)
|
|
34
|
+
return new ViewType({
|
|
35
|
+
name: 'SvInspectorView',
|
|
36
|
+
displayName: 'SV inspector',
|
|
37
|
+
stateModel,
|
|
38
|
+
ReactComponent,
|
|
39
|
+
})
|
|
40
|
+
})
|
|
41
|
+
}
|
|
@@ -0,0 +1,406 @@
|
|
|
1
|
+
import clone from 'clone'
|
|
2
|
+
import { autorun, reaction } from 'mobx'
|
|
3
|
+
import { types, getParent, addDisposer, Instance } from 'mobx-state-tree'
|
|
4
|
+
|
|
5
|
+
import PluginManager from '@jbrowse/core/PluginManager'
|
|
6
|
+
import { getSession, Region } from '@jbrowse/core/util'
|
|
7
|
+
import { readConfObject } from '@jbrowse/core/configuration'
|
|
8
|
+
import { ElementId } from '@jbrowse/core/util/types/mst'
|
|
9
|
+
import { BaseViewModel } from '@jbrowse/core/pluggableElementTypes/models'
|
|
10
|
+
import { SpreadsheetViewStateModel } from '@jbrowse/plugin-spreadsheet-view'
|
|
11
|
+
import { CircularViewStateModel } from '@jbrowse/plugin-circular-view'
|
|
12
|
+
|
|
13
|
+
// icons
|
|
14
|
+
import OpenInNewIcon from '@mui/icons-material/OpenInNew'
|
|
15
|
+
import FolderOpenIcon from '@mui/icons-material/FolderOpen'
|
|
16
|
+
|
|
17
|
+
// locals
|
|
18
|
+
import {
|
|
19
|
+
canOpenBreakpointSplitViewFromTableRow,
|
|
20
|
+
openBreakpointSplitViewFromTableRow,
|
|
21
|
+
getFeatureForRow,
|
|
22
|
+
} from './breakpointSplitViewFromTableRow'
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* #stateModel SvInspectorView
|
|
26
|
+
* #category view
|
|
27
|
+
* combination of a spreadsheetview and a circularview
|
|
28
|
+
*/
|
|
29
|
+
function SvInspectorViewF(pluginManager: PluginManager) {
|
|
30
|
+
const SpreadsheetViewType = pluginManager.getViewType('SpreadsheetView')
|
|
31
|
+
const CircularViewType = pluginManager.getViewType('CircularView')
|
|
32
|
+
|
|
33
|
+
const SpreadsheetModel =
|
|
34
|
+
SpreadsheetViewType.stateModel as SpreadsheetViewStateModel
|
|
35
|
+
const CircularModel = CircularViewType.stateModel as CircularViewStateModel
|
|
36
|
+
|
|
37
|
+
const minHeight = 400
|
|
38
|
+
const defaultHeight = 550
|
|
39
|
+
const headerHeight = 52
|
|
40
|
+
const circularViewOptionsBarHeight = 52
|
|
41
|
+
return types
|
|
42
|
+
.compose(
|
|
43
|
+
BaseViewModel,
|
|
44
|
+
types.model('SvInspectorView', {
|
|
45
|
+
/**
|
|
46
|
+
* #property
|
|
47
|
+
*/
|
|
48
|
+
id: ElementId,
|
|
49
|
+
/**
|
|
50
|
+
* #property
|
|
51
|
+
*/
|
|
52
|
+
type: types.literal('SvInspectorView'),
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* #property
|
|
56
|
+
*/
|
|
57
|
+
height: types.optional(
|
|
58
|
+
types.refinement(
|
|
59
|
+
'SvInspectorViewHeight',
|
|
60
|
+
types.number,
|
|
61
|
+
n => n >= minHeight,
|
|
62
|
+
),
|
|
63
|
+
defaultHeight,
|
|
64
|
+
),
|
|
65
|
+
/**
|
|
66
|
+
* #property
|
|
67
|
+
*/
|
|
68
|
+
onlyDisplayRelevantRegionsInCircularView: false,
|
|
69
|
+
/**
|
|
70
|
+
* #property
|
|
71
|
+
* switch specifying whether we are showing the import wizard or the
|
|
72
|
+
* spreadsheet in our viewing area
|
|
73
|
+
*/
|
|
74
|
+
mode: types.optional(
|
|
75
|
+
types.enumeration('SvInspectorViewMode', ['import', 'display']),
|
|
76
|
+
'import',
|
|
77
|
+
),
|
|
78
|
+
/**
|
|
79
|
+
* #property
|
|
80
|
+
*/
|
|
81
|
+
spreadsheetView: types.optional(SpreadsheetModel, () =>
|
|
82
|
+
SpreadsheetModel.create({
|
|
83
|
+
type: 'SpreadsheetView',
|
|
84
|
+
hideVerticalResizeHandle: true,
|
|
85
|
+
}),
|
|
86
|
+
),
|
|
87
|
+
/**
|
|
88
|
+
* #property
|
|
89
|
+
*/
|
|
90
|
+
circularView: types.optional(CircularModel, () =>
|
|
91
|
+
CircularModel.create({
|
|
92
|
+
type: 'CircularView',
|
|
93
|
+
hideVerticalResizeHandle: true,
|
|
94
|
+
hideTrackSelectorButton: true,
|
|
95
|
+
disableImportForm: true,
|
|
96
|
+
}),
|
|
97
|
+
),
|
|
98
|
+
}),
|
|
99
|
+
)
|
|
100
|
+
.volatile(() => ({
|
|
101
|
+
width: 800,
|
|
102
|
+
}))
|
|
103
|
+
.views(self => ({
|
|
104
|
+
/**
|
|
105
|
+
* #getter
|
|
106
|
+
*/
|
|
107
|
+
get selectedRows() {
|
|
108
|
+
// @ts-expect-error
|
|
109
|
+
return self.spreadsheetView.rowSet.selectedRows
|
|
110
|
+
},
|
|
111
|
+
/**
|
|
112
|
+
* #getter
|
|
113
|
+
*/
|
|
114
|
+
get assemblyName() {
|
|
115
|
+
const { assembly } = self.spreadsheetView
|
|
116
|
+
return assembly ? readConfObject(assembly, 'name') : undefined
|
|
117
|
+
},
|
|
118
|
+
/**
|
|
119
|
+
* #getter
|
|
120
|
+
*/
|
|
121
|
+
get showCircularView() {
|
|
122
|
+
return self.spreadsheetView.mode === 'display'
|
|
123
|
+
},
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* #getter
|
|
127
|
+
*/
|
|
128
|
+
get features() {
|
|
129
|
+
const session = getSession(self)
|
|
130
|
+
const { spreadsheetView } = self
|
|
131
|
+
const { outputRows = [] } = spreadsheetView
|
|
132
|
+
return outputRows
|
|
133
|
+
.map((r, i) => getFeatureForRow(session, spreadsheetView, r, i))
|
|
134
|
+
.filter(f => !!f)
|
|
135
|
+
},
|
|
136
|
+
/**
|
|
137
|
+
* #getter
|
|
138
|
+
*/
|
|
139
|
+
get featuresAdapterConfigSnapshot() {
|
|
140
|
+
return {
|
|
141
|
+
type: 'FromConfigAdapter',
|
|
142
|
+
features: this.features,
|
|
143
|
+
}
|
|
144
|
+
},
|
|
145
|
+
/**
|
|
146
|
+
* #getter
|
|
147
|
+
*/
|
|
148
|
+
get featureRefNames() {
|
|
149
|
+
const refs = this.features.map(r => r.refName)
|
|
150
|
+
const CHR2 = this.features.flatMap(r => r.INFO?.CHR2).filter(f => !!f)
|
|
151
|
+
return [...refs, ...CHR2]
|
|
152
|
+
},
|
|
153
|
+
/**
|
|
154
|
+
* #getter
|
|
155
|
+
*/
|
|
156
|
+
get featuresCircularTrackConfiguration() {
|
|
157
|
+
return {
|
|
158
|
+
type: 'VariantTrack',
|
|
159
|
+
trackId: `sv-inspector-variant-track-${self.id}`,
|
|
160
|
+
name: 'features from tabular data',
|
|
161
|
+
adapter: this.featuresAdapterConfigSnapshot,
|
|
162
|
+
assemblyNames: [this.assemblyName],
|
|
163
|
+
displays: [
|
|
164
|
+
{
|
|
165
|
+
type: 'ChordVariantDisplay',
|
|
166
|
+
displayId: `sv-inspector-variant-track-chord-display-${self.id}`,
|
|
167
|
+
onChordClick: `jexl:defaultOnChordClick(feature, track, pluginManager)`,
|
|
168
|
+
renderer: { type: 'StructuralVariantChordRenderer' },
|
|
169
|
+
},
|
|
170
|
+
],
|
|
171
|
+
}
|
|
172
|
+
},
|
|
173
|
+
}))
|
|
174
|
+
.volatile(() => ({
|
|
175
|
+
SpreadsheetViewReactComponent: SpreadsheetViewType.ReactComponent,
|
|
176
|
+
CircularViewReactComponent: CircularViewType.ReactComponent,
|
|
177
|
+
circularViewOptionsBarHeight,
|
|
178
|
+
}))
|
|
179
|
+
.actions(self => ({
|
|
180
|
+
/**
|
|
181
|
+
* #action
|
|
182
|
+
*/
|
|
183
|
+
setWidth(newWidth: number) {
|
|
184
|
+
self.width = newWidth
|
|
185
|
+
},
|
|
186
|
+
/**
|
|
187
|
+
* #action
|
|
188
|
+
*/
|
|
189
|
+
setHeight(newHeight: number) {
|
|
190
|
+
self.height = newHeight > minHeight ? newHeight : minHeight
|
|
191
|
+
return self.height
|
|
192
|
+
},
|
|
193
|
+
/**
|
|
194
|
+
* #action
|
|
195
|
+
*/
|
|
196
|
+
setImportMode() {
|
|
197
|
+
self.spreadsheetView.setImportMode()
|
|
198
|
+
},
|
|
199
|
+
/**
|
|
200
|
+
* #action
|
|
201
|
+
*/
|
|
202
|
+
setDisplayMode() {
|
|
203
|
+
self.spreadsheetView.setDisplayMode()
|
|
204
|
+
},
|
|
205
|
+
/**
|
|
206
|
+
* #action
|
|
207
|
+
*/
|
|
208
|
+
closeView() {
|
|
209
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
210
|
+
getParent<any>(self, 2).removeView(self)
|
|
211
|
+
},
|
|
212
|
+
/**
|
|
213
|
+
* #action
|
|
214
|
+
*/
|
|
215
|
+
setDisplayedRegions(regions: Region[]) {
|
|
216
|
+
self.circularView.setDisplayedRegions(regions)
|
|
217
|
+
},
|
|
218
|
+
/**
|
|
219
|
+
* #action
|
|
220
|
+
*/
|
|
221
|
+
setOnlyDisplayRelevantRegionsInCircularView(val: boolean) {
|
|
222
|
+
self.onlyDisplayRelevantRegionsInCircularView = Boolean(val)
|
|
223
|
+
},
|
|
224
|
+
}))
|
|
225
|
+
.views(self => ({
|
|
226
|
+
/**
|
|
227
|
+
* #method
|
|
228
|
+
*/
|
|
229
|
+
menuItems() {
|
|
230
|
+
return [
|
|
231
|
+
{
|
|
232
|
+
label: 'Return to import form',
|
|
233
|
+
onClick: () => self.setImportMode(),
|
|
234
|
+
icon: FolderOpenIcon,
|
|
235
|
+
},
|
|
236
|
+
]
|
|
237
|
+
},
|
|
238
|
+
}))
|
|
239
|
+
.actions(self => ({
|
|
240
|
+
/**
|
|
241
|
+
* #action
|
|
242
|
+
*/
|
|
243
|
+
resizeHeight(distance: number) {
|
|
244
|
+
const oldHeight = self.height
|
|
245
|
+
const newHeight = self.setHeight(self.height + distance)
|
|
246
|
+
return newHeight - oldHeight
|
|
247
|
+
},
|
|
248
|
+
afterAttach() {
|
|
249
|
+
// synchronize subview widths
|
|
250
|
+
addDisposer(
|
|
251
|
+
self,
|
|
252
|
+
autorun(
|
|
253
|
+
() => {
|
|
254
|
+
const borderWidth = 1
|
|
255
|
+
if (self.showCircularView) {
|
|
256
|
+
const spreadsheetWidth = Math.round(self.width * 0.66)
|
|
257
|
+
const circularViewWidth = self.width - spreadsheetWidth
|
|
258
|
+
self.spreadsheetView.setWidth(spreadsheetWidth - borderWidth)
|
|
259
|
+
self.circularView.setWidth(circularViewWidth)
|
|
260
|
+
} else {
|
|
261
|
+
self.spreadsheetView.setWidth(self.width)
|
|
262
|
+
}
|
|
263
|
+
},
|
|
264
|
+
{ name: 'SvInspectorView width binding' },
|
|
265
|
+
),
|
|
266
|
+
)
|
|
267
|
+
// synchronize subview heights
|
|
268
|
+
addDisposer(
|
|
269
|
+
self,
|
|
270
|
+
autorun(
|
|
271
|
+
() => {
|
|
272
|
+
self.spreadsheetView.setHeight(self.height - headerHeight)
|
|
273
|
+
self.circularView.setHeight(
|
|
274
|
+
self.height - headerHeight - circularViewOptionsBarHeight,
|
|
275
|
+
)
|
|
276
|
+
},
|
|
277
|
+
{ name: 'SvInspectorView height binding' },
|
|
278
|
+
),
|
|
279
|
+
)
|
|
280
|
+
|
|
281
|
+
// bind circularview displayedRegions to spreadsheet assembly, mediated
|
|
282
|
+
// by the onlyRelevantRegions toggle
|
|
283
|
+
addDisposer(
|
|
284
|
+
self,
|
|
285
|
+
autorun(
|
|
286
|
+
async () => {
|
|
287
|
+
const {
|
|
288
|
+
assemblyName,
|
|
289
|
+
onlyDisplayRelevantRegionsInCircularView,
|
|
290
|
+
circularView,
|
|
291
|
+
featureRefNames,
|
|
292
|
+
} = self
|
|
293
|
+
const { tracks } = circularView
|
|
294
|
+
const { assemblyManager } = getSession(self)
|
|
295
|
+
if (!assemblyName) {
|
|
296
|
+
return
|
|
297
|
+
}
|
|
298
|
+
const asm = await assemblyManager.waitForAssembly(assemblyName)
|
|
299
|
+
if (!asm) {
|
|
300
|
+
return
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
const { getCanonicalRefName, regions = [] } = asm
|
|
304
|
+
if (onlyDisplayRelevantRegionsInCircularView) {
|
|
305
|
+
if (tracks.length === 1) {
|
|
306
|
+
try {
|
|
307
|
+
// canonicalize the store's ref names if necessary
|
|
308
|
+
const refSet = new Set(
|
|
309
|
+
featureRefNames.map(r => getCanonicalRefName(r) || r),
|
|
310
|
+
)
|
|
311
|
+
|
|
312
|
+
circularView.setDisplayedRegions(
|
|
313
|
+
clone(regions.filter(r => refSet.has(r.refName))),
|
|
314
|
+
)
|
|
315
|
+
} catch (e) {
|
|
316
|
+
circularView.setError(e)
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
} else {
|
|
320
|
+
circularView.setDisplayedRegions(regions)
|
|
321
|
+
}
|
|
322
|
+
},
|
|
323
|
+
{ name: 'SvInspectorView displayed regions bind' },
|
|
324
|
+
),
|
|
325
|
+
)
|
|
326
|
+
|
|
327
|
+
// bind circularview tracks to our track snapshot view
|
|
328
|
+
addDisposer(
|
|
329
|
+
self,
|
|
330
|
+
reaction(
|
|
331
|
+
() => ({
|
|
332
|
+
generatedTrackConf: self?.featuresCircularTrackConfiguration,
|
|
333
|
+
assemblyName: self?.assemblyName,
|
|
334
|
+
}),
|
|
335
|
+
data => {
|
|
336
|
+
if (!data) {
|
|
337
|
+
return
|
|
338
|
+
}
|
|
339
|
+
const { assemblyName, generatedTrackConf } = data
|
|
340
|
+
const { circularView } = self
|
|
341
|
+
// hide any visible tracks
|
|
342
|
+
circularView.tracks.forEach(t =>
|
|
343
|
+
circularView.hideTrack(t.configuration.trackId),
|
|
344
|
+
)
|
|
345
|
+
|
|
346
|
+
// put our track in as the only track
|
|
347
|
+
if (assemblyName && generatedTrackConf) {
|
|
348
|
+
// @ts-expect-error
|
|
349
|
+
circularView.addTrackConf(generatedTrackConf, {
|
|
350
|
+
assemblyName,
|
|
351
|
+
})
|
|
352
|
+
}
|
|
353
|
+
},
|
|
354
|
+
{
|
|
355
|
+
name: 'SvInspectorView track configuration binding',
|
|
356
|
+
fireImmediately: true,
|
|
357
|
+
},
|
|
358
|
+
),
|
|
359
|
+
)
|
|
360
|
+
|
|
361
|
+
// bind spreadsheetView row menu actions to us
|
|
362
|
+
addDisposer(
|
|
363
|
+
self,
|
|
364
|
+
autorun(() => {
|
|
365
|
+
self.spreadsheetView.setRowMenuItems(
|
|
366
|
+
// these are the MenuItem entries for the row menu actions in the
|
|
367
|
+
// spreadsheet view. these are installed into the child
|
|
368
|
+
// SpreadsheetView using an autorun below
|
|
369
|
+
[
|
|
370
|
+
{
|
|
371
|
+
label: 'Open split detail view',
|
|
372
|
+
icon: OpenInNewIcon,
|
|
373
|
+
// @ts-expect-error
|
|
374
|
+
disabled(spreadsheetView, spreadsheet, rowNumber, row) {
|
|
375
|
+
return !canOpenBreakpointSplitViewFromTableRow(
|
|
376
|
+
self,
|
|
377
|
+
spreadsheetView,
|
|
378
|
+
spreadsheet,
|
|
379
|
+
row,
|
|
380
|
+
rowNumber,
|
|
381
|
+
)
|
|
382
|
+
},
|
|
383
|
+
|
|
384
|
+
// @ts-expect-error
|
|
385
|
+
onClick(spreadsheetView, spreadsheet, rowNumber, row) {
|
|
386
|
+
openBreakpointSplitViewFromTableRow(
|
|
387
|
+
self,
|
|
388
|
+
spreadsheetView,
|
|
389
|
+
spreadsheet,
|
|
390
|
+
row,
|
|
391
|
+
rowNumber,
|
|
392
|
+
)
|
|
393
|
+
},
|
|
394
|
+
},
|
|
395
|
+
],
|
|
396
|
+
)
|
|
397
|
+
}),
|
|
398
|
+
)
|
|
399
|
+
},
|
|
400
|
+
}))
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
export type SvInspectorViewStateModel = ReturnType<typeof SvInspectorViewF>
|
|
404
|
+
export type SvInspectorViewModel = Instance<SvInspectorViewStateModel>
|
|
405
|
+
|
|
406
|
+
export default SvInspectorViewF
|