@jbrowse/plugin-menus 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.
Files changed (86) hide show
  1. package/LICENSE +201 -0
  2. package/dist/AboutWidget/components/AboutWidget.d.ts +7 -0
  3. package/dist/AboutWidget/components/AboutWidget.js +63 -0
  4. package/dist/AboutWidget/components/AboutWidget.js.map +1 -0
  5. package/dist/AboutWidget/index.d.ts +5 -0
  6. package/dist/AboutWidget/index.js +12 -0
  7. package/dist/AboutWidget/index.js.map +1 -0
  8. package/dist/HelpWidget/components/HelpWidget.d.ts +7 -0
  9. package/dist/HelpWidget/components/HelpWidget.js +37 -0
  10. package/dist/HelpWidget/components/HelpWidget.js.map +1 -0
  11. package/dist/HelpWidget/index.d.ts +5 -0
  12. package/dist/HelpWidget/index.js +12 -0
  13. package/dist/HelpWidget/index.js.map +1 -0
  14. package/dist/ImportSessionWidget/components/ImportError.d.ts +4 -0
  15. package/dist/ImportSessionWidget/components/ImportError.js +35 -0
  16. package/dist/ImportSessionWidget/components/ImportError.js.map +1 -0
  17. package/dist/ImportSessionWidget/components/ImportSessionWidget.d.ts +7 -0
  18. package/dist/ImportSessionWidget/components/ImportSessionWidget.js +114 -0
  19. package/dist/ImportSessionWidget/components/ImportSessionWidget.js.map +1 -0
  20. package/dist/ImportSessionWidget/index.d.ts +5 -0
  21. package/dist/ImportSessionWidget/index.js +12 -0
  22. package/dist/ImportSessionWidget/index.js.map +1 -0
  23. package/dist/SessionManager/components/DeleteDialog.d.ts +6 -0
  24. package/dist/SessionManager/components/DeleteDialog.js +18 -0
  25. package/dist/SessionManager/components/DeleteDialog.js.map +1 -0
  26. package/dist/SessionManager/components/SessionManager.d.ts +20 -0
  27. package/dist/SessionManager/components/SessionManager.js +102 -0
  28. package/dist/SessionManager/components/SessionManager.js.map +1 -0
  29. package/dist/SessionManager/index.d.ts +5 -0
  30. package/dist/SessionManager/index.js +12 -0
  31. package/dist/SessionManager/index.js.map +1 -0
  32. package/dist/index.d.ts +7 -0
  33. package/dist/index.js +104 -0
  34. package/dist/index.js.map +1 -0
  35. package/esm/AboutWidget/components/AboutWidget.d.ts +7 -0
  36. package/esm/AboutWidget/components/AboutWidget.js +58 -0
  37. package/esm/AboutWidget/components/AboutWidget.js.map +1 -0
  38. package/esm/AboutWidget/index.d.ts +5 -0
  39. package/esm/AboutWidget/index.js +9 -0
  40. package/esm/AboutWidget/index.js.map +1 -0
  41. package/esm/HelpWidget/components/HelpWidget.d.ts +7 -0
  42. package/esm/HelpWidget/components/HelpWidget.js +32 -0
  43. package/esm/HelpWidget/components/HelpWidget.js.map +1 -0
  44. package/esm/HelpWidget/index.d.ts +5 -0
  45. package/esm/HelpWidget/index.js +9 -0
  46. package/esm/HelpWidget/index.js.map +1 -0
  47. package/esm/ImportSessionWidget/components/ImportError.d.ts +4 -0
  48. package/esm/ImportSessionWidget/components/ImportError.js +29 -0
  49. package/esm/ImportSessionWidget/components/ImportError.js.map +1 -0
  50. package/esm/ImportSessionWidget/components/ImportSessionWidget.d.ts +7 -0
  51. package/esm/ImportSessionWidget/components/ImportSessionWidget.js +86 -0
  52. package/esm/ImportSessionWidget/components/ImportSessionWidget.js.map +1 -0
  53. package/esm/ImportSessionWidget/index.d.ts +5 -0
  54. package/esm/ImportSessionWidget/index.js +9 -0
  55. package/esm/ImportSessionWidget/index.js.map +1 -0
  56. package/esm/SessionManager/components/DeleteDialog.d.ts +6 -0
  57. package/esm/SessionManager/components/DeleteDialog.js +12 -0
  58. package/esm/SessionManager/components/DeleteDialog.js.map +1 -0
  59. package/esm/SessionManager/components/SessionManager.d.ts +20 -0
  60. package/esm/SessionManager/components/SessionManager.js +74 -0
  61. package/esm/SessionManager/components/SessionManager.js.map +1 -0
  62. package/esm/SessionManager/index.d.ts +5 -0
  63. package/esm/SessionManager/index.js +9 -0
  64. package/esm/SessionManager/index.js.map +1 -0
  65. package/esm/index.d.ts +7 -0
  66. package/esm/index.js +75 -0
  67. package/esm/index.js.map +1 -0
  68. package/package.json +61 -0
  69. package/src/AboutWidget/components/AboutWidget.test.tsx +35 -0
  70. package/src/AboutWidget/components/AboutWidget.tsx +91 -0
  71. package/src/AboutWidget/components/__snapshots__/AboutWidget.test.tsx.snap +65 -0
  72. package/src/AboutWidget/index.ts +10 -0
  73. package/src/HelpWidget/components/HelpWidget.test.js +10 -0
  74. package/src/HelpWidget/components/HelpWidget.tsx +75 -0
  75. package/src/HelpWidget/components/__snapshots__/HelpWidget.test.js.snap +63 -0
  76. package/src/HelpWidget/index.ts +10 -0
  77. package/src/ImportSessionWidget/components/ImportError.tsx +38 -0
  78. package/src/ImportSessionWidget/components/ImportSessionWidget.tsx +109 -0
  79. package/src/ImportSessionWidget/index.ts +10 -0
  80. package/src/SessionManager/components/DeleteDialog.tsx +41 -0
  81. package/src/SessionManager/components/SessionManager.test.tsx +14 -0
  82. package/src/SessionManager/components/SessionManager.tsx +166 -0
  83. package/src/SessionManager/components/__snapshots__/SessionManager.test.tsx.snap +22 -0
  84. package/src/SessionManager/index.ts +10 -0
  85. package/src/index.test.ts +12 -0
  86. package/src/index.ts +100 -0
@@ -0,0 +1,10 @@
1
+ import { ConfigurationSchema } from '@jbrowse/core/configuration'
2
+ import { ElementId } from '@jbrowse/core/util/types/mst'
3
+ import { types } from 'mobx-state-tree'
4
+
5
+ export const configSchema = ConfigurationSchema('HelpWidget', {})
6
+
7
+ export const stateModel = types.model('HelpWidget', {
8
+ id: ElementId,
9
+ type: types.literal('HelpWidget'),
10
+ })
@@ -0,0 +1,38 @@
1
+ import React from 'react'
2
+ import { Paper, Typography } from '@mui/material'
3
+ import { makeStyles } from 'tss-react/mui'
4
+
5
+ // icons
6
+ import ErrorIcon from '@mui/icons-material/Error'
7
+
8
+ const useStyles = makeStyles()(theme => ({
9
+ error: {
10
+ margin: theme.spacing(2),
11
+ },
12
+ errorHeader: {
13
+ background: theme.palette.error.light,
14
+ color: theme.palette.error.contrastText,
15
+ padding: theme.spacing(2),
16
+ textAlign: 'center',
17
+ },
18
+ errorMessage: {
19
+ padding: theme.spacing(2),
20
+ },
21
+ }))
22
+
23
+ export default function ImportError({ error }: { error: unknown }) {
24
+ const { classes } = useStyles()
25
+ return (
26
+ <Paper className={classes.error}>
27
+ <div className={classes.errorHeader}>
28
+ <ErrorIcon color="inherit" fontSize="large" />
29
+ <div>
30
+ <Typography variant="h6" color="inherit" align="center">
31
+ Import error
32
+ </Typography>
33
+ </div>
34
+ </div>
35
+ <Typography className={classes.errorMessage}>{`${error}`}</Typography>
36
+ </Paper>
37
+ )
38
+ }
@@ -0,0 +1,109 @@
1
+ import React, { useState } from 'react'
2
+ import { Button, Paper, Typography, alpha } from '@mui/material'
3
+ import { getSession } from '@jbrowse/core/util'
4
+ import { makeStyles } from 'tss-react/mui'
5
+ import { observer } from 'mobx-react'
6
+ import { useDropzone } from 'react-dropzone'
7
+
8
+ // icons
9
+ import CloudUploadIcon from '@mui/icons-material/CloudUpload'
10
+
11
+ // locals
12
+ import ImportError from './ImportError'
13
+ import { IAnyStateTreeNode } from 'mobx-state-tree'
14
+
15
+ const MAX_FILE_SIZE = 512 * 1024 ** 2 // 512 MiB
16
+
17
+ function styledBy(property: string, mapping: { [key: string]: string }) {
18
+ return (props: { [key: string]: string }) => mapping[props[property]]
19
+ }
20
+
21
+ // @ts-expect-error
22
+ const useStyles = makeStyles()(theme => ({
23
+ root: {
24
+ margin: theme.spacing(1),
25
+ },
26
+ paper: {
27
+ display: 'flex',
28
+ flexDirection: 'column',
29
+ },
30
+ dropZone: {
31
+ textAlign: 'center',
32
+ margin: theme.spacing(2),
33
+ padding: theme.spacing(2),
34
+ borderWidth: 2,
35
+ borderRadius: 2,
36
+ borderColor: styledBy('isDragActive', {
37
+ true: theme.palette.secondary.light,
38
+ false: theme.palette.divider,
39
+ }),
40
+ borderStyle: 'dashed',
41
+ backgroundColor: styledBy('isDragActive', {
42
+ true: alpha(
43
+ theme.palette.text.primary,
44
+ theme.palette.action.hoverOpacity,
45
+ ),
46
+ false: theme.palette.background.default,
47
+ }),
48
+ outline: 'none',
49
+ transition: 'border .24s ease-in-out',
50
+ '&:focus': {
51
+ borderColor: theme.palette.secondary.light,
52
+ },
53
+ },
54
+ uploadIcon: {
55
+ color: theme.palette.text.secondary,
56
+ },
57
+ }))
58
+
59
+ function ImportSession({ model }: { model: IAnyStateTreeNode }) {
60
+ const [error, setError] = useState<unknown>()
61
+ const { getRootProps, getInputProps, isDragActive } = useDropzone({
62
+ // @ts-expect-error
63
+ accept: 'application/json',
64
+ maxSize: MAX_FILE_SIZE,
65
+ multiple: false,
66
+ onDrop: async (acceptedFiles, rejectedFiles) => {
67
+ try {
68
+ if (rejectedFiles.length > 0) {
69
+ throw new Error(
70
+ `${rejectedFiles[0].errors.map(e => `${e}`).join(', ')}`,
71
+ )
72
+ } else {
73
+ const sessionText = await acceptedFiles[0].text()
74
+ getSession(model).setSession(JSON.parse(sessionText).session)
75
+ }
76
+ } catch (e) {
77
+ console.error(e)
78
+ setError(e)
79
+ }
80
+ },
81
+ })
82
+
83
+ // @ts-expect-error
84
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
85
+ const { classes } = useStyles({ isDragActive }) as any
86
+
87
+ return (
88
+ <div className={classes.root}>
89
+ <Paper className={classes.paper}>
90
+ <div {...getRootProps({ className: classes.dropZone })}>
91
+ <input {...getInputProps()} />
92
+ <CloudUploadIcon className={classes.uploadIcon} fontSize="large" />
93
+ <Typography color="textSecondary" align="center" variant="body1">
94
+ Drag and drop files here
95
+ </Typography>
96
+ <Typography color="textSecondary" align="center" variant="body2">
97
+ or
98
+ </Typography>
99
+ <Button color="primary" variant="contained">
100
+ Browse Files
101
+ </Button>
102
+ </div>
103
+ </Paper>
104
+ {error ? <ImportError error={error} /> : null}
105
+ </div>
106
+ )
107
+ }
108
+
109
+ export default observer(ImportSession)
@@ -0,0 +1,10 @@
1
+ import { ConfigurationSchema } from '@jbrowse/core/configuration'
2
+ import { ElementId } from '@jbrowse/core/util/types/mst'
3
+ import { types } from 'mobx-state-tree'
4
+
5
+ export const configSchema = ConfigurationSchema('ImportSessionWidget', {})
6
+
7
+ export const stateModel = types.model('ImportSessionWidget', {
8
+ id: ElementId,
9
+ type: types.literal('ImportSessionWidget'),
10
+ })
@@ -0,0 +1,41 @@
1
+ import React from 'react'
2
+ import {
3
+ Button,
4
+ DialogActions,
5
+ DialogContent,
6
+ DialogContentText,
7
+ } from '@mui/material'
8
+ import { Dialog } from '@jbrowse/core/ui'
9
+
10
+ export default function DeleteDialog({
11
+ open,
12
+ sessionNameToDelete,
13
+ handleClose,
14
+ }: {
15
+ sessionNameToDelete: string
16
+ open: boolean
17
+ handleClose: (arg?: boolean) => void
18
+ }) {
19
+ return (
20
+ <Dialog
21
+ open={open}
22
+ aria-labelledby="alert-dialog-title"
23
+ aria-describedby="alert-dialog-description"
24
+ title={`Delete session "${sessionNameToDelete}"?`}
25
+ >
26
+ <DialogContent>
27
+ <DialogContentText id="alert-dialog-description">
28
+ This action cannot be undone
29
+ </DialogContentText>
30
+ </DialogContent>
31
+ <DialogActions>
32
+ <Button onClick={() => handleClose()} color="primary">
33
+ Cancel
34
+ </Button>
35
+ <Button onClick={() => handleClose(true)} color="primary" autoFocus>
36
+ Delete
37
+ </Button>
38
+ </DialogActions>
39
+ </Dialog>
40
+ )
41
+ }
@@ -0,0 +1,14 @@
1
+ import { createTestSession } from '@jbrowse/web/src/rootModel'
2
+
3
+ import React from 'react'
4
+ import { render } from '@testing-library/react'
5
+ import SessionManager from './SessionManager'
6
+ jest.mock('@jbrowse/web/src/makeWorkerInstance', () => () => {})
7
+
8
+ describe('<SessionManager />', () => {
9
+ it('renders', () => {
10
+ const session = createTestSession()
11
+ const { container } = render(<SessionManager session={session} />)
12
+ expect(container.firstChild).toMatchSnapshot()
13
+ })
14
+ })
@@ -0,0 +1,166 @@
1
+ import React, { useState } from 'react'
2
+ import {
3
+ IconButton,
4
+ List,
5
+ ListItem,
6
+ ListItemIcon,
7
+ ListItemSecondaryAction,
8
+ ListItemText,
9
+ ListSubheader,
10
+ Paper,
11
+ Typography,
12
+ } from '@mui/material'
13
+ import { makeStyles } from 'tss-react/mui'
14
+
15
+ import { observer } from 'mobx-react'
16
+ import pluralize from 'pluralize'
17
+ import { AbstractSessionModel } from '@jbrowse/core/util'
18
+
19
+ // icons
20
+ import DeleteIcon from '@mui/icons-material/Delete'
21
+ import ViewListIcon from '@mui/icons-material/ViewList'
22
+ import DeleteDialog from './DeleteDialog'
23
+
24
+ const useStyles = makeStyles()(theme => ({
25
+ root: {
26
+ margin: theme.spacing(1),
27
+ },
28
+ message: {
29
+ padding: theme.spacing(3),
30
+ },
31
+ }))
32
+
33
+ interface SessionSnap {
34
+ name: string
35
+ views?: { tracks: unknown[] }[]
36
+ [key: string]: unknown
37
+ }
38
+
39
+ interface SessionModel extends AbstractSessionModel {
40
+ savedSessions: SessionSnap[]
41
+ removeSavedSession: (arg: SessionSnap) => void
42
+ activateSession: (arg: string) => void
43
+ loadAutosaveSession: () => void
44
+ previousAutosaveId: string
45
+ }
46
+
47
+ const AutosaveEntry = observer(({ session }: { session: SessionModel }) => {
48
+ const { classes } = useStyles()
49
+ const autosavedSession = JSON.parse(
50
+ localStorage.getItem(session.previousAutosaveId) || '{}',
51
+ ).session as SessionSnap
52
+
53
+ const { views = [] } = autosavedSession || {}
54
+ const totalTracks = views
55
+ .map(view => view.tracks.length)
56
+ .reduce((a, b) => a + b, 0)
57
+
58
+ return autosavedSession ? (
59
+ <Paper className={classes.root}>
60
+ <List subheader={<ListSubheader>Previous autosaved entry</ListSubheader>}>
61
+ <ListItem button onClick={() => session.loadAutosaveSession()}>
62
+ <ListItemIcon>
63
+ <ViewListIcon />
64
+ </ListItemIcon>
65
+ <ListItemText
66
+ primary={autosavedSession.name}
67
+ secondary={
68
+ session.name === autosavedSession.name
69
+ ? 'Currently open'
70
+ : `${views.length} ${pluralize(
71
+ 'view',
72
+ views.length,
73
+ )}; ${totalTracks}
74
+ open ${pluralize('track', totalTracks)}`
75
+ }
76
+ />
77
+ </ListItem>
78
+ </List>
79
+ </Paper>
80
+ ) : null
81
+ })
82
+
83
+ const SessionManager = observer(({ session }: { session: SessionModel }) => {
84
+ const { classes } = useStyles()
85
+ const [sessionIndexToDelete, setSessionIndexToDelete] = useState<number>()
86
+ const [open, setOpen] = useState(false)
87
+
88
+ function handleDialogClose(deleteSession = false) {
89
+ if (deleteSession && sessionIndexToDelete !== undefined) {
90
+ session.removeSavedSession(session.savedSessions[sessionIndexToDelete])
91
+ }
92
+ setSessionIndexToDelete(undefined)
93
+ setOpen(false)
94
+ }
95
+
96
+ const sessionNameToDelete =
97
+ sessionIndexToDelete !== undefined
98
+ ? session.savedSessions[sessionIndexToDelete].name
99
+ : ''
100
+
101
+ return (
102
+ <>
103
+ <AutosaveEntry session={session} />
104
+ <Paper className={classes.root}>
105
+ <List subheader={<ListSubheader>Saved sessions</ListSubheader>}>
106
+ {session.savedSessions.length ? (
107
+ session.savedSessions.map((sessionSnapshot, idx) => {
108
+ const { views = [] } = sessionSnapshot
109
+ const totalTracks = views
110
+ .map(view => view.tracks.length)
111
+ .reduce((a, b) => a + b, 0)
112
+ return (
113
+ <ListItem
114
+ button
115
+ disabled={session.name === sessionSnapshot.name}
116
+ onClick={() => session.activateSession(sessionSnapshot.name)}
117
+ key={sessionSnapshot.name}
118
+ >
119
+ <ListItemIcon>
120
+ <ViewListIcon />
121
+ </ListItemIcon>
122
+ <ListItemText
123
+ primary={sessionSnapshot.name}
124
+ secondary={
125
+ session.name === sessionSnapshot.name
126
+ ? 'Currently open'
127
+ : `${views.length} ${pluralize(
128
+ 'view',
129
+ views.length,
130
+ )}; ${totalTracks}
131
+ open ${pluralize('track', totalTracks)}`
132
+ }
133
+ />
134
+ <ListItemSecondaryAction>
135
+ <IconButton
136
+ edge="end"
137
+ disabled={session.name === sessionSnapshot.name}
138
+ aria-label="Delete"
139
+ onClick={() => {
140
+ setSessionIndexToDelete(idx)
141
+ setOpen(true)
142
+ }}
143
+ >
144
+ <DeleteIcon />
145
+ </IconButton>
146
+ </ListItemSecondaryAction>
147
+ </ListItem>
148
+ )
149
+ })
150
+ ) : (
151
+ <Typography className={classes.message}>
152
+ No saved sessions found
153
+ </Typography>
154
+ )}
155
+ </List>
156
+ </Paper>
157
+ <DeleteDialog
158
+ open={open}
159
+ sessionNameToDelete={sessionNameToDelete}
160
+ handleClose={handleDialogClose}
161
+ />
162
+ </>
163
+ )
164
+ })
165
+
166
+ export default SessionManager
@@ -0,0 +1,22 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`<SessionManager /> renders 1`] = `
4
+ <div
5
+ class="MuiPaper-root MuiPaper-elevation MuiPaper-rounded MuiPaper-elevation1 css-1l9cidk-MuiPaper-root-root"
6
+ >
7
+ <ul
8
+ class="MuiList-root MuiList-padding MuiList-subheader css-p64g7a-MuiList-root"
9
+ >
10
+ <li
11
+ class="MuiListSubheader-root MuiListSubheader-gutters MuiListSubheader-sticky css-uob957-MuiListSubheader-root"
12
+ >
13
+ Saved sessions
14
+ </li>
15
+ <p
16
+ class="MuiTypography-root MuiTypography-body1 css-g9z2qm-MuiTypography-root-message"
17
+ >
18
+ No saved sessions found
19
+ </p>
20
+ </ul>
21
+ </div>
22
+ `;
@@ -0,0 +1,10 @@
1
+ import { ConfigurationSchema } from '@jbrowse/core/configuration'
2
+ import { ElementId } from '@jbrowse/core/util/types/mst'
3
+ import { types } from 'mobx-state-tree'
4
+
5
+ export const configSchema = ConfigurationSchema('SessionManager', {})
6
+
7
+ export const stateModel = types.model('SessionManager', {
8
+ id: ElementId,
9
+ type: types.literal('SessionManager'),
10
+ })
@@ -0,0 +1,12 @@
1
+ import PluginManager from '@jbrowse/core/PluginManager'
2
+ import ThisPlugin from '.'
3
+
4
+ test("won't add if already added", () => {
5
+ const pluginManager = new PluginManager([new ThisPlugin()])
6
+ pluginManager.createPluggableElements()
7
+ pluginManager.configure()
8
+
9
+ expect(() => pluginManager.addPlugin(new ThisPlugin())).toThrow(
10
+ /JBrowse already configured, cannot add plugins/,
11
+ )
12
+ })
package/src/index.ts ADDED
@@ -0,0 +1,100 @@
1
+ import { lazy } from 'react'
2
+ import WidgetType from '@jbrowse/core/pluggableElementTypes/WidgetType'
3
+ import Plugin from '@jbrowse/core/Plugin'
4
+ import PluginManager from '@jbrowse/core/PluginManager'
5
+ import { SessionWithWidgets, isAbstractMenuManager } from '@jbrowse/core/util'
6
+
7
+ import HelpIcon from '@mui/icons-material/Help'
8
+ import InfoIcon from '@mui/icons-material/Info'
9
+
10
+ import {
11
+ configSchema as aboutConfigSchema,
12
+ stateModel as aboutStateModel,
13
+ } from './AboutWidget'
14
+ import {
15
+ configSchema as helpConfigSchema,
16
+ stateModel as helpStateModel,
17
+ } from './HelpWidget'
18
+ import {
19
+ configSchema as importSessionConfigSchema,
20
+ stateModel as importSessionStateModel,
21
+ } from './ImportSessionWidget'
22
+ import {
23
+ configSchema as sessionManagerConfigSchema,
24
+ stateModel as sessionManagerStateModel,
25
+ } from './SessionManager'
26
+
27
+ export default class extends Plugin {
28
+ name = 'MenusPlugin'
29
+
30
+ install(pluginManager: PluginManager) {
31
+ pluginManager.addWidgetType(() => {
32
+ return new WidgetType({
33
+ name: 'AboutWidget',
34
+ heading: 'About',
35
+ configSchema: aboutConfigSchema,
36
+ stateModel: aboutStateModel,
37
+ ReactComponent: lazy(
38
+ () => import('./AboutWidget/components/AboutWidget'),
39
+ ),
40
+ })
41
+ })
42
+
43
+ pluginManager.addWidgetType(() => {
44
+ return new WidgetType({
45
+ name: 'HelpWidget',
46
+ heading: 'Help',
47
+ configSchema: helpConfigSchema,
48
+ stateModel: helpStateModel,
49
+ ReactComponent: lazy(
50
+ () => import('./HelpWidget/components/HelpWidget'),
51
+ ),
52
+ })
53
+ })
54
+
55
+ pluginManager.addWidgetType(() => {
56
+ return new WidgetType({
57
+ name: 'ImportSessionWidget',
58
+ heading: 'Import session',
59
+ configSchema: importSessionConfigSchema,
60
+ stateModel: importSessionStateModel,
61
+ ReactComponent: lazy(
62
+ () => import('./ImportSessionWidget/components/ImportSessionWidget'),
63
+ ),
64
+ })
65
+ })
66
+
67
+ pluginManager.addWidgetType(() => {
68
+ return new WidgetType({
69
+ name: 'SessionManager',
70
+ heading: 'Sessions',
71
+ configSchema: sessionManagerConfigSchema,
72
+ stateModel: sessionManagerStateModel,
73
+ ReactComponent: lazy(
74
+ () => import('./SessionManager/components/SessionManager'),
75
+ ),
76
+ })
77
+ })
78
+ }
79
+
80
+ configure(pluginManager: PluginManager) {
81
+ if (isAbstractMenuManager(pluginManager.rootModel)) {
82
+ pluginManager.rootModel.appendToMenu('Help', {
83
+ label: 'About',
84
+ icon: InfoIcon,
85
+ onClick: (session: SessionWithWidgets) => {
86
+ const widget = session.addWidget('AboutWidget', 'aboutWidget')
87
+ session.showWidget(widget)
88
+ },
89
+ })
90
+ pluginManager.rootModel.appendToMenu('Help', {
91
+ label: 'Help',
92
+ icon: HelpIcon,
93
+ onClick: (session: SessionWithWidgets) => {
94
+ const widget = session.addWidget('HelpWidget', 'helpWidget')
95
+ session.showWidget(widget)
96
+ },
97
+ })
98
+ }
99
+ }
100
+ }