jbrowse-plugin-graphgenomeviewer 1.0.0
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/README.md +36 -0
- package/dist/GraphGenomeView/components/FeatureDialog.d.ts +5 -0
- package/dist/GraphGenomeView/components/FeatureDialog.js +18 -0
- package/dist/GraphGenomeView/components/FeatureDialog.js.map +1 -0
- package/dist/GraphGenomeView/components/GraphGenomeView.d.ts +6 -0
- package/dist/GraphGenomeView/components/GraphGenomeView.js +17 -0
- package/dist/GraphGenomeView/components/GraphGenomeView.js.map +1 -0
- package/dist/GraphGenomeView/components/GraphPanel.d.ts +6 -0
- package/dist/GraphGenomeView/components/GraphPanel.js +17 -0
- package/dist/GraphGenomeView/components/GraphPanel.js.map +1 -0
- package/dist/GraphGenomeView/components/Header.d.ts +6 -0
- package/dist/GraphGenomeView/components/Header.js +59 -0
- package/dist/GraphGenomeView/components/Header.js.map +1 -0
- package/dist/GraphGenomeView/components/SettingsDialog.d.ts +7 -0
- package/dist/GraphGenomeView/components/SettingsDialog.js +24 -0
- package/dist/GraphGenomeView/components/SettingsDialog.js.map +1 -0
- package/dist/GraphGenomeView/index.d.ts +2 -0
- package/dist/GraphGenomeView/index.js +15 -0
- package/dist/GraphGenomeView/index.js.map +1 -0
- package/dist/GraphGenomeView/model.d.ts +80 -0
- package/dist/GraphGenomeView/model.js +96 -0
- package/dist/GraphGenomeView/model.js.map +1 -0
- package/dist/GraphGenomeView/util.d.ts +1 -0
- package/dist/GraphGenomeView/util.js +8 -0
- package/dist/GraphGenomeView/util.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +24 -0
- package/dist/index.js.map +1 -0
- package/dist/jbrowse-plugin-graphgenomeviewer.umd.production.min.js +67 -0
- package/dist/jbrowse-plugin-graphgenomeviewer.umd.production.min.js.map +7 -0
- package/package.json +63 -0
- package/src/GraphGenomeView/components/FeatureDialog.tsx +51 -0
- package/src/GraphGenomeView/components/GraphGenomeView.tsx +32 -0
- package/src/GraphGenomeView/components/GraphPanel.tsx +37 -0
- package/src/GraphGenomeView/components/Header.tsx +90 -0
- package/src/GraphGenomeView/components/SettingsDialog.tsx +71 -0
- package/src/GraphGenomeView/index.ts +19 -0
- package/src/GraphGenomeView/model.ts +110 -0
- package/src/GraphGenomeView/util.ts +7 -0
- package/src/declare.d.ts +1 -0
- package/src/index.ts +28 -0
package/package.json
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "1.0.0",
|
|
3
|
+
"name": "jbrowse-plugin-graphgenomeviewer",
|
|
4
|
+
"keywords": [
|
|
5
|
+
"jbrowse",
|
|
6
|
+
"jbrowse2"
|
|
7
|
+
],
|
|
8
|
+
"main": "dist/index.js",
|
|
9
|
+
"files": [
|
|
10
|
+
"dist",
|
|
11
|
+
"src"
|
|
12
|
+
],
|
|
13
|
+
"scripts": {
|
|
14
|
+
"test": "vitest",
|
|
15
|
+
"clean": "rimraf dist",
|
|
16
|
+
"format": "prettier --write .",
|
|
17
|
+
"prebuild": "npm run clean",
|
|
18
|
+
"start": "node esbuild.mjs",
|
|
19
|
+
"build": "tsc && NODE_ENV=production node esbuild.mjs",
|
|
20
|
+
"lint": "eslint --report-unused-disable-directives --max-warnings 0",
|
|
21
|
+
"prepack": "npm run build",
|
|
22
|
+
"postversion": "git push --follow-tags"
|
|
23
|
+
},
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"@mui/icons-material": "^6.4.1",
|
|
26
|
+
"graphgenomeviewer": "^5.0.4"
|
|
27
|
+
},
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"@emotion/react": "^11.13.0",
|
|
30
|
+
"@eslint/compat": "^1.1.1",
|
|
31
|
+
"@fal-works/esbuild-plugin-global-externals": "^2.1.2",
|
|
32
|
+
"@jbrowse/core": "^3.0.1",
|
|
33
|
+
"@jbrowse/plugin-linear-genome-view": "^3.0.1",
|
|
34
|
+
"@mui/material": "^6.1.5",
|
|
35
|
+
"@mui/system": "^6.1.5",
|
|
36
|
+
"@mui/x-data-grid": "^7.3.0",
|
|
37
|
+
"@types/node": "^22.5.0",
|
|
38
|
+
"@types/react": "^19.0.2",
|
|
39
|
+
"@typescript-eslint/eslint-plugin": "^8.1.0",
|
|
40
|
+
"@typescript-eslint/parser": "^8.1.0",
|
|
41
|
+
"esbuild": "^0.25.0",
|
|
42
|
+
"eslint": "^9.0.0",
|
|
43
|
+
"eslint-plugin-import": "^2.31.0",
|
|
44
|
+
"eslint-plugin-react": "^7.20.3",
|
|
45
|
+
"eslint-plugin-react-hooks": "^5.0.0",
|
|
46
|
+
"eslint-plugin-react-refresh": "^0.4.11",
|
|
47
|
+
"eslint-plugin-unicorn": "^56.0.0",
|
|
48
|
+
"mobx": "^6.0.0",
|
|
49
|
+
"mobx-react": "^9.0.1",
|
|
50
|
+
"mobx-state-tree": "5.4.2",
|
|
51
|
+
"prettier": "^3.2.5",
|
|
52
|
+
"pretty-bytes": "^6.1.1",
|
|
53
|
+
"react": "^19.0.0",
|
|
54
|
+
"rimraf": "^6.0.1",
|
|
55
|
+
"rxjs": "^7.0.0",
|
|
56
|
+
"tss-react": "^4.9.4",
|
|
57
|
+
"typescript": "^5.3.3",
|
|
58
|
+
"typescript-eslint": "^8.1.0",
|
|
59
|
+
"vitest": "^3.0.5"
|
|
60
|
+
},
|
|
61
|
+
"author": "Colin <colin.diesh@gmail.com>",
|
|
62
|
+
"license": "MIT"
|
|
63
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
|
|
3
|
+
import { Dialog } from '@jbrowse/core/ui'
|
|
4
|
+
|
|
5
|
+
export default function FeatureDialog({
|
|
6
|
+
data,
|
|
7
|
+
onClose,
|
|
8
|
+
}: {
|
|
9
|
+
data: Record<string, unknown>
|
|
10
|
+
onClose: () => void
|
|
11
|
+
}) {
|
|
12
|
+
return (
|
|
13
|
+
<Dialog open title="Feature details" onClose={onClose}>
|
|
14
|
+
<div>Attributes</div>
|
|
15
|
+
{Object.entries(data)
|
|
16
|
+
.filter(
|
|
17
|
+
entry => !['source', 'target', 'linkNum', 'tags'].includes(entry[0]),
|
|
18
|
+
)
|
|
19
|
+
.map(([key, value]) => (
|
|
20
|
+
<div
|
|
21
|
+
key={`${key}_${value}`}
|
|
22
|
+
style={{ display: 'flex', maxHeight: 150, margin: 3 }}
|
|
23
|
+
>
|
|
24
|
+
<div style={{ backgroundColor: '#dda', minWidth: 100 }}>{key}</div>
|
|
25
|
+
<div style={{ wordBreak: 'break-word', overflow: 'auto' }}>
|
|
26
|
+
{String(value)}
|
|
27
|
+
</div>
|
|
28
|
+
</div>
|
|
29
|
+
))}
|
|
30
|
+
<hr />
|
|
31
|
+
{data.tags && Object.keys(data.tags).length > 0 ? (
|
|
32
|
+
<>
|
|
33
|
+
<div>Tags</div>
|
|
34
|
+
{Object.entries(data.tags).map(([key, value]) => (
|
|
35
|
+
<div
|
|
36
|
+
key={`${key}_${value}`}
|
|
37
|
+
style={{ display: 'flex', maxHeight: 150, margin: 3 }}
|
|
38
|
+
>
|
|
39
|
+
<div style={{ backgroundColor: '#dda', minWidth: 100 }}>
|
|
40
|
+
{key}
|
|
41
|
+
</div>
|
|
42
|
+
<div style={{ wordBreak: 'break-word', overflow: 'auto' }}>
|
|
43
|
+
{String(value)}
|
|
44
|
+
</div>
|
|
45
|
+
</div>
|
|
46
|
+
))}
|
|
47
|
+
</>
|
|
48
|
+
) : null}
|
|
49
|
+
</Dialog>
|
|
50
|
+
)
|
|
51
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
|
|
3
|
+
import { ErrorMessage } from '@jbrowse/core/ui'
|
|
4
|
+
import { observer } from 'mobx-react'
|
|
5
|
+
|
|
6
|
+
import GraphPanel from './GraphPanel'
|
|
7
|
+
import Header from './Header'
|
|
8
|
+
|
|
9
|
+
import type { GraphGenomeViewModel } from '../model'
|
|
10
|
+
|
|
11
|
+
const GraphGenomeView = observer(function ({
|
|
12
|
+
model,
|
|
13
|
+
}: {
|
|
14
|
+
model: GraphGenomeViewModel
|
|
15
|
+
}) {
|
|
16
|
+
const { error } = model
|
|
17
|
+
return (
|
|
18
|
+
<div
|
|
19
|
+
style={{
|
|
20
|
+
padding: 10,
|
|
21
|
+
}}
|
|
22
|
+
>
|
|
23
|
+
<Header model={model} />
|
|
24
|
+
<div>
|
|
25
|
+
{error ? <ErrorMessage error={error} /> : null}
|
|
26
|
+
<GraphPanel model={model} />
|
|
27
|
+
</div>
|
|
28
|
+
</div>
|
|
29
|
+
)
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
export default GraphGenomeView
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import React, { useState } from 'react'
|
|
2
|
+
|
|
3
|
+
import { Graph } from 'graphgenomeviewer'
|
|
4
|
+
import { observer } from 'mobx-react'
|
|
5
|
+
|
|
6
|
+
import FeatureDialog from './FeatureDialog'
|
|
7
|
+
|
|
8
|
+
import type { GraphGenomeViewModel } from '../model'
|
|
9
|
+
|
|
10
|
+
const GraphPanel = observer(function ({
|
|
11
|
+
model,
|
|
12
|
+
}: {
|
|
13
|
+
model: GraphGenomeViewModel
|
|
14
|
+
}) {
|
|
15
|
+
const { graph } = model
|
|
16
|
+
const [featureData, setFeatureData] = useState<Record<string, unknown>>()
|
|
17
|
+
return graph ? (
|
|
18
|
+
<>
|
|
19
|
+
{featureData ? (
|
|
20
|
+
<FeatureDialog
|
|
21
|
+
data={featureData}
|
|
22
|
+
onClose={() => {
|
|
23
|
+
setFeatureData(undefined)
|
|
24
|
+
}}
|
|
25
|
+
/>
|
|
26
|
+
) : null}
|
|
27
|
+
<Graph
|
|
28
|
+
graph={graph}
|
|
29
|
+
onFeatureClick={data => {
|
|
30
|
+
setFeatureData(data)
|
|
31
|
+
}}
|
|
32
|
+
/>
|
|
33
|
+
</>
|
|
34
|
+
) : null
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
export default GraphPanel
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import React, { useEffect, useState } from 'react'
|
|
2
|
+
|
|
3
|
+
import { FileSelector } from '@jbrowse/core/ui'
|
|
4
|
+
import { FileLocation, getSession } from '@jbrowse/core/util'
|
|
5
|
+
import { openLocation } from '@jbrowse/core/util/io'
|
|
6
|
+
import { Button, IconButton, TextField } from '@mui/material'
|
|
7
|
+
import { observer } from 'mobx-react'
|
|
8
|
+
|
|
9
|
+
import { Settings } from '@mui/icons-material'
|
|
10
|
+
|
|
11
|
+
import SettingsDialog from './SettingsDialog'
|
|
12
|
+
|
|
13
|
+
import type { GraphGenomeViewModel } from '../model'
|
|
14
|
+
|
|
15
|
+
const Header = observer(function ({ model }: { model: GraphGenomeViewModel }) {
|
|
16
|
+
const { mode } = model
|
|
17
|
+
const [val, setVal] = useState('GRCh38#chr21:1000-2000')
|
|
18
|
+
const [loc, setLoc] = useState<FileLocation>()
|
|
19
|
+
useEffect(() => {
|
|
20
|
+
if (!loc) {
|
|
21
|
+
return
|
|
22
|
+
}
|
|
23
|
+
if ('uri' in loc && loc.uri) {
|
|
24
|
+
model.setGfaUrl(loc.uri)
|
|
25
|
+
} else {
|
|
26
|
+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
27
|
+
;(async () => {
|
|
28
|
+
try {
|
|
29
|
+
model.setError(undefined)
|
|
30
|
+
const data = await openLocation(loc).readFile('utf8')
|
|
31
|
+
model.setResultData(data)
|
|
32
|
+
} catch (e) {
|
|
33
|
+
console.error(e)
|
|
34
|
+
model.setError(e)
|
|
35
|
+
}
|
|
36
|
+
})()
|
|
37
|
+
}
|
|
38
|
+
})
|
|
39
|
+
return (
|
|
40
|
+
<div style={{ display: 'flex' }}>
|
|
41
|
+
{mode === 'files' ? (
|
|
42
|
+
<div style={{ maxWidth: 500 }}>
|
|
43
|
+
<FileSelector
|
|
44
|
+
name="GFA path"
|
|
45
|
+
inline
|
|
46
|
+
location={loc}
|
|
47
|
+
setLocation={loc => {
|
|
48
|
+
setLoc(loc)
|
|
49
|
+
}}
|
|
50
|
+
/>
|
|
51
|
+
</div>
|
|
52
|
+
) : (
|
|
53
|
+
<div>
|
|
54
|
+
<TextField
|
|
55
|
+
type="text"
|
|
56
|
+
variant="outlined"
|
|
57
|
+
margin="normal"
|
|
58
|
+
size="small"
|
|
59
|
+
value={val}
|
|
60
|
+
style={{ minWidth: 500 }}
|
|
61
|
+
onChange={event => {
|
|
62
|
+
setVal(event.target.value)
|
|
63
|
+
}}
|
|
64
|
+
/>
|
|
65
|
+
<Button variant="contained" type="submit">
|
|
66
|
+
Submit
|
|
67
|
+
</Button>
|
|
68
|
+
</div>
|
|
69
|
+
)}
|
|
70
|
+
<div style={{ flexGrow: 1 }} />
|
|
71
|
+
<div>
|
|
72
|
+
<IconButton
|
|
73
|
+
onClick={() => {
|
|
74
|
+
getSession(model).queueDialog(handleClose => [
|
|
75
|
+
SettingsDialog,
|
|
76
|
+
{
|
|
77
|
+
handleClose,
|
|
78
|
+
model,
|
|
79
|
+
},
|
|
80
|
+
])
|
|
81
|
+
}}
|
|
82
|
+
>
|
|
83
|
+
<Settings />
|
|
84
|
+
</IconButton>
|
|
85
|
+
</div>
|
|
86
|
+
</div>
|
|
87
|
+
)
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
export default Header
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
|
|
3
|
+
import { Dialog } from '@jbrowse/core/ui'
|
|
4
|
+
import {
|
|
5
|
+
Button,
|
|
6
|
+
DialogActions,
|
|
7
|
+
DialogContent,
|
|
8
|
+
FormControl,
|
|
9
|
+
FormControlLabel,
|
|
10
|
+
Radio,
|
|
11
|
+
RadioGroup,
|
|
12
|
+
} from '@mui/material'
|
|
13
|
+
import { observer } from 'mobx-react'
|
|
14
|
+
|
|
15
|
+
import type { GraphGenomeViewModel } from '../model'
|
|
16
|
+
|
|
17
|
+
const SettingsDialog = observer(function ({
|
|
18
|
+
model,
|
|
19
|
+
handleClose,
|
|
20
|
+
}: {
|
|
21
|
+
model: GraphGenomeViewModel
|
|
22
|
+
handleClose: () => void
|
|
23
|
+
}) {
|
|
24
|
+
const { mode } = model
|
|
25
|
+
return (
|
|
26
|
+
<Dialog open maxWidth="xl" title="Settings" onClose={handleClose}>
|
|
27
|
+
<DialogContent style={{ minWidth: 800 }}>
|
|
28
|
+
<FormControl component="fieldset">
|
|
29
|
+
<RadioGroup
|
|
30
|
+
value={mode}
|
|
31
|
+
onChange={event => {
|
|
32
|
+
model.setMode(event.target.value)
|
|
33
|
+
}}
|
|
34
|
+
>
|
|
35
|
+
<FormControlLabel
|
|
36
|
+
value="files"
|
|
37
|
+
control={<Radio />}
|
|
38
|
+
label="Use GFA files"
|
|
39
|
+
/>
|
|
40
|
+
<FormControlLabel
|
|
41
|
+
value="server"
|
|
42
|
+
control={<Radio />}
|
|
43
|
+
label="Use GFA server"
|
|
44
|
+
/>
|
|
45
|
+
</RadioGroup>
|
|
46
|
+
</FormControl>
|
|
47
|
+
</DialogContent>
|
|
48
|
+
<DialogActions>
|
|
49
|
+
<Button
|
|
50
|
+
variant="contained"
|
|
51
|
+
onClick={() => {
|
|
52
|
+
handleClose()
|
|
53
|
+
}}
|
|
54
|
+
>
|
|
55
|
+
Submit
|
|
56
|
+
</Button>
|
|
57
|
+
<Button
|
|
58
|
+
variant="contained"
|
|
59
|
+
color="secondary"
|
|
60
|
+
onClick={() => {
|
|
61
|
+
handleClose()
|
|
62
|
+
}}
|
|
63
|
+
>
|
|
64
|
+
Cancel
|
|
65
|
+
</Button>
|
|
66
|
+
</DialogActions>
|
|
67
|
+
</Dialog>
|
|
68
|
+
)
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
export default SettingsDialog
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { lazy } from 'react'
|
|
2
|
+
|
|
3
|
+
import PluginManager from '@jbrowse/core/PluginManager'
|
|
4
|
+
import { ViewType } from '@jbrowse/core/pluggableElementTypes'
|
|
5
|
+
|
|
6
|
+
import stateModelF from './model'
|
|
7
|
+
|
|
8
|
+
const ReactComponent = lazy(() => import('./components/GraphGenomeView'))
|
|
9
|
+
|
|
10
|
+
export default function ProteinViewF(pluginManager: PluginManager) {
|
|
11
|
+
pluginManager.addViewType(() => {
|
|
12
|
+
return new ViewType({
|
|
13
|
+
name: 'GraphGenomeView',
|
|
14
|
+
displayName: 'Graph genome view',
|
|
15
|
+
stateModel: stateModelF(),
|
|
16
|
+
ReactComponent,
|
|
17
|
+
})
|
|
18
|
+
})
|
|
19
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { BaseViewModel } from '@jbrowse/core/pluggableElementTypes'
|
|
2
|
+
import { parseGFA } from 'graphgenomeviewer'
|
|
3
|
+
import { autorun } from 'mobx'
|
|
4
|
+
import { addDisposer, types } from 'mobx-state-tree'
|
|
5
|
+
|
|
6
|
+
import { myfetchtext } from './util'
|
|
7
|
+
|
|
8
|
+
import type { Instance } from 'mobx-state-tree'
|
|
9
|
+
|
|
10
|
+
export default function stateModelFactory() {
|
|
11
|
+
return types.compose(
|
|
12
|
+
'GraphGenomeView',
|
|
13
|
+
BaseViewModel,
|
|
14
|
+
types
|
|
15
|
+
.model({
|
|
16
|
+
/**
|
|
17
|
+
* #property
|
|
18
|
+
*/
|
|
19
|
+
type: 'GraphGenomeView',
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* #property
|
|
23
|
+
*/
|
|
24
|
+
genomicRegionLocString: types.maybe(types.string),
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* #property
|
|
28
|
+
*/
|
|
29
|
+
gfaUrl: types.maybe(types.string),
|
|
30
|
+
/**
|
|
31
|
+
* #property
|
|
32
|
+
*/
|
|
33
|
+
mode: 'files',
|
|
34
|
+
})
|
|
35
|
+
.volatile(() => ({
|
|
36
|
+
/**
|
|
37
|
+
* #volatile
|
|
38
|
+
*/
|
|
39
|
+
resultData: undefined as string | undefined,
|
|
40
|
+
/**
|
|
41
|
+
* #volatile
|
|
42
|
+
*/
|
|
43
|
+
error: undefined as unknown,
|
|
44
|
+
}))
|
|
45
|
+
.views(self => ({
|
|
46
|
+
/**
|
|
47
|
+
* #getter
|
|
48
|
+
*/
|
|
49
|
+
get graph() {
|
|
50
|
+
return self.resultData ? parseGFA(self.resultData) : undefined
|
|
51
|
+
},
|
|
52
|
+
}))
|
|
53
|
+
.actions(self => ({
|
|
54
|
+
/**
|
|
55
|
+
* #action
|
|
56
|
+
*/
|
|
57
|
+
setMode(arg: string) {
|
|
58
|
+
self.mode = arg
|
|
59
|
+
},
|
|
60
|
+
/**
|
|
61
|
+
* #action
|
|
62
|
+
*/
|
|
63
|
+
setError(arg: unknown) {
|
|
64
|
+
self.error = arg
|
|
65
|
+
},
|
|
66
|
+
/**
|
|
67
|
+
* #action
|
|
68
|
+
*/
|
|
69
|
+
setResultData(arg: string) {
|
|
70
|
+
self.resultData = arg
|
|
71
|
+
},
|
|
72
|
+
/**
|
|
73
|
+
* #action
|
|
74
|
+
*/
|
|
75
|
+
setGenomicRegionLocString(arg: string) {
|
|
76
|
+
self.genomicRegionLocString = arg
|
|
77
|
+
},
|
|
78
|
+
/**
|
|
79
|
+
* #action
|
|
80
|
+
*/
|
|
81
|
+
setGfaUrl(url: string) {
|
|
82
|
+
self.gfaUrl = url
|
|
83
|
+
},
|
|
84
|
+
}))
|
|
85
|
+
.actions(self => ({
|
|
86
|
+
afterAttach() {
|
|
87
|
+
addDisposer(
|
|
88
|
+
self,
|
|
89
|
+
autorun(async () => {
|
|
90
|
+
if (!self.gfaUrl) {
|
|
91
|
+
return
|
|
92
|
+
}
|
|
93
|
+
try {
|
|
94
|
+
self.setError(undefined)
|
|
95
|
+
const url = self.gfaUrl
|
|
96
|
+
const data = await myfetchtext(url)
|
|
97
|
+
self.setResultData(data)
|
|
98
|
+
} catch (e) {
|
|
99
|
+
self.setError(e)
|
|
100
|
+
console.error(e)
|
|
101
|
+
}
|
|
102
|
+
}),
|
|
103
|
+
)
|
|
104
|
+
},
|
|
105
|
+
})),
|
|
106
|
+
)
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export type GraphGenomeViewStateModel = ReturnType<typeof stateModelFactory>
|
|
110
|
+
export type GraphGenomeViewModel = Instance<GraphGenomeViewStateModel>
|
package/src/declare.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
declare module '*.json'
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import Plugin from '@jbrowse/core/Plugin'
|
|
2
|
+
import PluginManager from '@jbrowse/core/PluginManager'
|
|
3
|
+
import { AbstractSessionModel, isAbstractMenuManager } from '@jbrowse/core/util'
|
|
4
|
+
import HubIcon from '@mui/icons-material/Hub'
|
|
5
|
+
|
|
6
|
+
import { version } from '../package.json'
|
|
7
|
+
import GraphGenomeViewF from './GraphGenomeView'
|
|
8
|
+
|
|
9
|
+
export default class GraphGenomeViewer extends Plugin {
|
|
10
|
+
name = 'GraphGenomeViewer'
|
|
11
|
+
version = version
|
|
12
|
+
|
|
13
|
+
install(pluginManager: PluginManager) {
|
|
14
|
+
GraphGenomeViewF(pluginManager)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
configure(pluginManager: PluginManager) {
|
|
18
|
+
if (isAbstractMenuManager(pluginManager.rootModel)) {
|
|
19
|
+
pluginManager.rootModel.appendToSubMenu(['Add'], {
|
|
20
|
+
label: 'Graph genome viewer',
|
|
21
|
+
icon: HubIcon,
|
|
22
|
+
onClick: (session: AbstractSessionModel) => {
|
|
23
|
+
session.addView('GraphGenomeView', {})
|
|
24
|
+
},
|
|
25
|
+
})
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|