@jbrowse/app-core 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/Assemblies/SessionAssembliesMixin.d.ts +72 -0
- package/dist/Assemblies/SessionAssembliesMixin.js +44 -0
- package/dist/Assemblies/TemporaryAssembliesMixin.d.ts +22 -0
- package/dist/Assemblies/TemporaryAssembliesMixin.js +45 -0
- package/dist/Assemblies/index.d.ts +2 -0
- package/dist/Assemblies/index.js +18 -0
- package/dist/HistoryManagement/index.d.ts +30 -0
- package/dist/HistoryManagement/index.js +54 -0
- package/dist/JBrowseConfig/index.d.ts +158 -0
- package/dist/JBrowseConfig/index.js +155 -0
- package/dist/JBrowseModel/index.d.ts +144 -0
- package/dist/JBrowseModel/index.js +132 -0
- package/dist/RootMenu/index.d.ts +68 -0
- package/dist/RootMenu/index.js +142 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +22 -0
- package/dist/ui/App/App.d.ts +19 -0
- package/dist/ui/App/App.js +95 -0
- package/dist/ui/App/AppFab.d.ts +6 -0
- package/dist/ui/App/AppFab.js +32 -0
- package/dist/ui/App/AppToolbar.d.ts +19 -0
- package/dist/ui/App/AppToolbar.js +54 -0
- package/dist/ui/App/DialogQueue.d.ts +6 -0
- package/dist/ui/App/DialogQueue.js +32 -0
- package/dist/ui/App/Drawer.d.ts +8 -0
- package/dist/ui/App/Drawer.js +34 -0
- package/dist/ui/App/DrawerWidget.d.ts +6 -0
- package/dist/ui/App/DrawerWidget.js +132 -0
- package/dist/ui/App/ViewContainer.d.ts +9 -0
- package/dist/ui/App/ViewContainer.js +76 -0
- package/dist/ui/App/ViewContainerTitle.d.ts +6 -0
- package/dist/ui/App/ViewContainerTitle.js +42 -0
- package/dist/ui/App/ViewLauncher.d.ts +18 -0
- package/dist/ui/App/ViewLauncher.js +50 -0
- package/dist/ui/App/ViewMenu.d.ts +9 -0
- package/dist/ui/App/ViewMenu.js +38 -0
- package/dist/ui/App/ViewPanel.d.ts +19 -0
- package/dist/ui/App/ViewPanel.js +50 -0
- package/dist/ui/App/index.d.ts +1 -0
- package/dist/ui/App/index.js +17 -0
- package/dist/ui/index.d.ts +1 -0
- package/dist/ui/index.js +17 -0
- package/esm/Assemblies/SessionAssembliesMixin.d.ts +72 -0
- package/esm/Assemblies/SessionAssembliesMixin.js +40 -0
- package/esm/Assemblies/TemporaryAssembliesMixin.d.ts +22 -0
- package/esm/Assemblies/TemporaryAssembliesMixin.js +41 -0
- package/esm/Assemblies/index.d.ts +2 -0
- package/esm/Assemblies/index.js +2 -0
- package/esm/HistoryManagement/index.d.ts +30 -0
- package/esm/HistoryManagement/index.js +47 -0
- package/esm/JBrowseConfig/index.d.ts +158 -0
- package/esm/JBrowseConfig/index.js +148 -0
- package/esm/JBrowseModel/index.d.ts +144 -0
- package/esm/JBrowseModel/index.js +128 -0
- package/esm/RootMenu/index.d.ts +68 -0
- package/esm/RootMenu/index.js +138 -0
- package/esm/index.d.ts +6 -0
- package/esm/index.js +6 -0
- package/esm/ui/App/App.d.ts +19 -0
- package/esm/ui/App/App.js +66 -0
- package/esm/ui/App/AppFab.d.ts +6 -0
- package/esm/ui/App/AppFab.js +27 -0
- package/esm/ui/App/AppToolbar.d.ts +19 -0
- package/esm/ui/App/AppToolbar.js +49 -0
- package/esm/ui/App/DialogQueue.d.ts +6 -0
- package/esm/ui/App/DialogQueue.js +7 -0
- package/esm/ui/App/Drawer.d.ts +8 -0
- package/esm/ui/App/Drawer.js +29 -0
- package/esm/ui/App/DrawerWidget.d.ts +6 -0
- package/esm/ui/App/DrawerWidget.js +104 -0
- package/esm/ui/App/ViewContainer.d.ts +9 -0
- package/esm/ui/App/ViewContainer.js +48 -0
- package/esm/ui/App/ViewContainerTitle.d.ts +6 -0
- package/esm/ui/App/ViewContainerTitle.js +37 -0
- package/esm/ui/App/ViewLauncher.d.ts +18 -0
- package/esm/ui/App/ViewLauncher.js +25 -0
- package/esm/ui/App/ViewMenu.d.ts +9 -0
- package/esm/ui/App/ViewMenu.js +33 -0
- package/esm/ui/App/ViewPanel.d.ts +19 -0
- package/esm/ui/App/ViewPanel.js +22 -0
- package/esm/ui/App/index.d.ts +1 -0
- package/esm/ui/App/index.js +1 -0
- package/esm/ui/index.d.ts +1 -0
- package/esm/ui/index.js +1 -0
- package/package.json +66 -0
- package/src/Assemblies/SessionAssembliesMixin.ts +50 -0
- package/src/Assemblies/TemporaryAssembliesMixin.ts +51 -0
- package/src/Assemblies/index.ts +2 -0
- package/src/HistoryManagement/index.ts +56 -0
- package/src/JBrowseConfig/index.ts +173 -0
- package/src/JBrowseModel/index.ts +150 -0
- package/src/RootMenu/index.ts +157 -0
- package/src/index.ts +6 -0
- package/src/ui/App/App.tsx +117 -0
- package/src/ui/App/AppFab.tsx +45 -0
- package/src/ui/App/AppToolbar.tsx +89 -0
- package/src/ui/App/DialogQueue.tsx +22 -0
- package/src/ui/App/Drawer.tsx +56 -0
- package/src/ui/App/DrawerWidget.tsx +238 -0
- package/src/ui/App/ViewContainer.tsx +76 -0
- package/src/ui/App/ViewContainerTitle.tsx +55 -0
- package/src/ui/App/ViewLauncher.tsx +64 -0
- package/src/ui/App/ViewMenu.tsx +54 -0
- package/src/ui/App/ViewPanel.tsx +63 -0
- package/src/ui/App/index.ts +1 -0
- package/src/ui/index.ts +1 -0
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
import React, { Suspense, useState } from 'react'
|
|
2
|
+
import { ErrorBoundary } from 'react-error-boundary'
|
|
3
|
+
import {
|
|
4
|
+
AppBar,
|
|
5
|
+
FormControl,
|
|
6
|
+
IconButton,
|
|
7
|
+
Menu,
|
|
8
|
+
MenuItem,
|
|
9
|
+
Select,
|
|
10
|
+
Toolbar,
|
|
11
|
+
Tooltip,
|
|
12
|
+
Typography,
|
|
13
|
+
} from '@mui/material'
|
|
14
|
+
import { makeStyles } from 'tss-react/mui'
|
|
15
|
+
import { observer } from 'mobx-react'
|
|
16
|
+
import { getEnv } from '@jbrowse/core/util'
|
|
17
|
+
import LoadingEllipses from '@jbrowse/core/ui/LoadingEllipses'
|
|
18
|
+
import ErrorMessage from '@jbrowse/core/ui/ErrorMessage'
|
|
19
|
+
import { SessionWithDrawerWidgets } from '@jbrowse/core/util/types'
|
|
20
|
+
|
|
21
|
+
// icons
|
|
22
|
+
import DeleteIcon from '@mui/icons-material/Delete'
|
|
23
|
+
import CloseIcon from '@mui/icons-material/Close'
|
|
24
|
+
import MinimizeIcon from '@mui/icons-material/Minimize'
|
|
25
|
+
import MoreVertIcon from '@mui/icons-material/MoreVert'
|
|
26
|
+
|
|
27
|
+
// locals
|
|
28
|
+
import Drawer from './Drawer'
|
|
29
|
+
|
|
30
|
+
const useStyles = makeStyles()(theme => ({
|
|
31
|
+
formControl: {
|
|
32
|
+
margin: 0,
|
|
33
|
+
},
|
|
34
|
+
spacer: {
|
|
35
|
+
flexGrow: 1,
|
|
36
|
+
},
|
|
37
|
+
drawerSelect: {
|
|
38
|
+
margin: 0,
|
|
39
|
+
color: theme.palette.secondary.contrastText,
|
|
40
|
+
},
|
|
41
|
+
|
|
42
|
+
dropDownIcon: {
|
|
43
|
+
color: theme.palette.secondary.contrastText,
|
|
44
|
+
},
|
|
45
|
+
header: {
|
|
46
|
+
background: theme.palette.secondary.main,
|
|
47
|
+
},
|
|
48
|
+
}))
|
|
49
|
+
|
|
50
|
+
const DrawerHeader = observer(function ({
|
|
51
|
+
session,
|
|
52
|
+
setToolbarHeight,
|
|
53
|
+
}: {
|
|
54
|
+
session: SessionWithDrawerWidgets
|
|
55
|
+
setToolbarHeight: (arg: number) => void
|
|
56
|
+
}) {
|
|
57
|
+
const { pluginManager } = getEnv(session)
|
|
58
|
+
const { visibleWidget, activeWidgets, drawerPosition } = session
|
|
59
|
+
const { classes } = useStyles()
|
|
60
|
+
|
|
61
|
+
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null)
|
|
62
|
+
|
|
63
|
+
return (
|
|
64
|
+
<AppBar
|
|
65
|
+
position="sticky"
|
|
66
|
+
className={classes.header}
|
|
67
|
+
ref={ref => setToolbarHeight(ref?.getBoundingClientRect().height || 0)}
|
|
68
|
+
>
|
|
69
|
+
<Toolbar disableGutters>
|
|
70
|
+
<FormControl className={classes.formControl}>
|
|
71
|
+
<Select
|
|
72
|
+
value={visibleWidget?.id}
|
|
73
|
+
data-testid="widget-drawer-selects"
|
|
74
|
+
className={classes.drawerSelect}
|
|
75
|
+
classes={{ icon: classes.dropDownIcon }}
|
|
76
|
+
renderValue={widgetId => {
|
|
77
|
+
const widget = session.activeWidgets.get(widgetId)
|
|
78
|
+
if (!widget) {
|
|
79
|
+
return (
|
|
80
|
+
<Typography variant="h6" color="inherit">
|
|
81
|
+
Unknown widget
|
|
82
|
+
</Typography>
|
|
83
|
+
)
|
|
84
|
+
}
|
|
85
|
+
const widgetType = pluginManager.getWidgetType(widget.type)
|
|
86
|
+
const { HeadingComponent, heading } = widgetType
|
|
87
|
+
return HeadingComponent ? (
|
|
88
|
+
<HeadingComponent model={widget} />
|
|
89
|
+
) : (
|
|
90
|
+
<Typography variant="h6" color="inherit">
|
|
91
|
+
{heading}
|
|
92
|
+
</Typography>
|
|
93
|
+
)
|
|
94
|
+
}}
|
|
95
|
+
onChange={e => {
|
|
96
|
+
const w = session.activeWidgets.get(e.target.value)
|
|
97
|
+
if (w) {
|
|
98
|
+
session.showWidget(w)
|
|
99
|
+
} else {
|
|
100
|
+
session.notify(`Widget not found ${e.target.value}`, 'warning')
|
|
101
|
+
}
|
|
102
|
+
}}
|
|
103
|
+
>
|
|
104
|
+
{[...activeWidgets.values()].map(widget => {
|
|
105
|
+
const widgetType = pluginManager.getWidgetType(widget.type)
|
|
106
|
+
const { HeadingComponent, heading } = widgetType
|
|
107
|
+
return (
|
|
108
|
+
<MenuItem
|
|
109
|
+
data-testid={`widget-drawer-selects-item-${widget.type}`}
|
|
110
|
+
key={widget.id}
|
|
111
|
+
value={widget.id}
|
|
112
|
+
>
|
|
113
|
+
{HeadingComponent ? (
|
|
114
|
+
<HeadingComponent model={widget} />
|
|
115
|
+
) : (
|
|
116
|
+
<Typography variant="h6" color="inherit">
|
|
117
|
+
{heading}
|
|
118
|
+
</Typography>
|
|
119
|
+
)}
|
|
120
|
+
<IconButton
|
|
121
|
+
data-testid={`${widget.type}-drawer-delete`}
|
|
122
|
+
color="inherit"
|
|
123
|
+
aria-label="Delete"
|
|
124
|
+
onClick={() => session.hideWidget(widget)}
|
|
125
|
+
>
|
|
126
|
+
<DeleteIcon />
|
|
127
|
+
</IconButton>
|
|
128
|
+
</MenuItem>
|
|
129
|
+
)
|
|
130
|
+
})}
|
|
131
|
+
</Select>
|
|
132
|
+
</FormControl>
|
|
133
|
+
<div className={classes.spacer} />
|
|
134
|
+
<div>
|
|
135
|
+
<IconButton
|
|
136
|
+
data-testid="drawer-close"
|
|
137
|
+
color="inherit"
|
|
138
|
+
onClick={event => setAnchorEl(event.currentTarget)}
|
|
139
|
+
>
|
|
140
|
+
<MoreVertIcon />
|
|
141
|
+
</IconButton>
|
|
142
|
+
<Tooltip title="Minimize drawer">
|
|
143
|
+
<IconButton
|
|
144
|
+
data-testid="drawer-minimize"
|
|
145
|
+
color="inherit"
|
|
146
|
+
onClick={() => {
|
|
147
|
+
session.notify(
|
|
148
|
+
`Drawer minimized, click button on ${drawerPosition} side of screen to re-open`,
|
|
149
|
+
'info',
|
|
150
|
+
)
|
|
151
|
+
session.minimizeWidgetDrawer()
|
|
152
|
+
}}
|
|
153
|
+
>
|
|
154
|
+
<MinimizeIcon />
|
|
155
|
+
</IconButton>
|
|
156
|
+
</Tooltip>
|
|
157
|
+
<Tooltip title="Close drawer">
|
|
158
|
+
<IconButton
|
|
159
|
+
data-testid="drawer-close"
|
|
160
|
+
color="inherit"
|
|
161
|
+
onClick={() => session.hideWidget(visibleWidget)}
|
|
162
|
+
>
|
|
163
|
+
<CloseIcon />
|
|
164
|
+
</IconButton>
|
|
165
|
+
</Tooltip>
|
|
166
|
+
</div>
|
|
167
|
+
</Toolbar>
|
|
168
|
+
<Menu
|
|
169
|
+
anchorEl={anchorEl}
|
|
170
|
+
open={Boolean(anchorEl)}
|
|
171
|
+
onClose={() => setAnchorEl(null)}
|
|
172
|
+
>
|
|
173
|
+
{['left', 'right'].map(option => (
|
|
174
|
+
<MenuItem
|
|
175
|
+
key={option}
|
|
176
|
+
selected={drawerPosition === 'option'}
|
|
177
|
+
onClick={() => {
|
|
178
|
+
session.setDrawerPosition(option)
|
|
179
|
+
setAnchorEl(null)
|
|
180
|
+
}}
|
|
181
|
+
>
|
|
182
|
+
{option}
|
|
183
|
+
</MenuItem>
|
|
184
|
+
))}
|
|
185
|
+
</Menu>
|
|
186
|
+
</AppBar>
|
|
187
|
+
)
|
|
188
|
+
})
|
|
189
|
+
|
|
190
|
+
const DrawerWidget = observer(function ({
|
|
191
|
+
session,
|
|
192
|
+
}: {
|
|
193
|
+
session: SessionWithDrawerWidgets
|
|
194
|
+
}) {
|
|
195
|
+
const { visibleWidget } = session
|
|
196
|
+
const { pluginManager } = getEnv(session)
|
|
197
|
+
|
|
198
|
+
const DrawerComponent = visibleWidget
|
|
199
|
+
? (pluginManager.evaluateExtensionPoint(
|
|
200
|
+
'Core-replaceWidget',
|
|
201
|
+
pluginManager.getWidgetType(visibleWidget.type).ReactComponent,
|
|
202
|
+
{
|
|
203
|
+
session,
|
|
204
|
+
model: visibleWidget,
|
|
205
|
+
},
|
|
206
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
207
|
+
) as React.FC<any>)
|
|
208
|
+
: null
|
|
209
|
+
|
|
210
|
+
// we track the toolbar height because components that use virtualized
|
|
211
|
+
// height want to be able to fill the contained, minus the toolbar height
|
|
212
|
+
// (the position static/sticky is included in AutoSizer estimates)
|
|
213
|
+
const [toolbarHeight, setToolbarHeight] = useState(0)
|
|
214
|
+
|
|
215
|
+
return (
|
|
216
|
+
<Drawer session={session}>
|
|
217
|
+
<DrawerHeader session={session} setToolbarHeight={setToolbarHeight} />
|
|
218
|
+
<Suspense fallback={<LoadingEllipses />}>
|
|
219
|
+
<ErrorBoundary
|
|
220
|
+
FallbackComponent={({ error }) => <ErrorMessage error={error} />}
|
|
221
|
+
>
|
|
222
|
+
{DrawerComponent ? (
|
|
223
|
+
<>
|
|
224
|
+
<DrawerComponent
|
|
225
|
+
model={visibleWidget}
|
|
226
|
+
session={session}
|
|
227
|
+
toolbarHeight={toolbarHeight}
|
|
228
|
+
/>
|
|
229
|
+
<div style={{ height: 300 }} />
|
|
230
|
+
</>
|
|
231
|
+
) : null}
|
|
232
|
+
</ErrorBoundary>
|
|
233
|
+
</Suspense>
|
|
234
|
+
</Drawer>
|
|
235
|
+
)
|
|
236
|
+
})
|
|
237
|
+
|
|
238
|
+
export default DrawerWidget
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import React, { useEffect, useRef } from 'react'
|
|
2
|
+
import { IconButton, Paper, useTheme } from '@mui/material'
|
|
3
|
+
import { makeStyles } from 'tss-react/mui'
|
|
4
|
+
import { observer } from 'mobx-react'
|
|
5
|
+
import { useWidthSetter } from '@jbrowse/core/util'
|
|
6
|
+
import { IBaseViewModel } from '@jbrowse/core/pluggableElementTypes/models'
|
|
7
|
+
|
|
8
|
+
// icons
|
|
9
|
+
import CloseIcon from '@mui/icons-material/Close'
|
|
10
|
+
import MinimizeIcon from '@mui/icons-material/Minimize'
|
|
11
|
+
import AddIcon from '@mui/icons-material/Add'
|
|
12
|
+
|
|
13
|
+
// locals
|
|
14
|
+
import ViewMenu from './ViewMenu'
|
|
15
|
+
import ViewContainerTitle from './ViewContainerTitle'
|
|
16
|
+
|
|
17
|
+
const useStyles = makeStyles()(theme => ({
|
|
18
|
+
viewContainer: {
|
|
19
|
+
overflow: 'hidden',
|
|
20
|
+
background: theme.palette.secondary.main,
|
|
21
|
+
margin: theme.spacing(0.5),
|
|
22
|
+
padding: `0 ${theme.spacing(1)} ${theme.spacing(1)}`,
|
|
23
|
+
},
|
|
24
|
+
icon: {
|
|
25
|
+
color: theme.palette.secondary.contrastText,
|
|
26
|
+
},
|
|
27
|
+
grow: {
|
|
28
|
+
flexGrow: 1,
|
|
29
|
+
},
|
|
30
|
+
}))
|
|
31
|
+
|
|
32
|
+
export default observer(function ({
|
|
33
|
+
view,
|
|
34
|
+
onClose,
|
|
35
|
+
onMinimize,
|
|
36
|
+
children,
|
|
37
|
+
}: {
|
|
38
|
+
view: IBaseViewModel
|
|
39
|
+
onClose: () => void
|
|
40
|
+
onMinimize: () => void
|
|
41
|
+
children: React.ReactNode
|
|
42
|
+
}) {
|
|
43
|
+
const { classes } = useStyles()
|
|
44
|
+
const theme = useTheme()
|
|
45
|
+
const ref = useWidthSetter(view, theme.spacing(1))
|
|
46
|
+
const scrollRef = useRef<HTMLDivElement>(null)
|
|
47
|
+
|
|
48
|
+
// scroll the view into view when first mounted. note: this effect will run
|
|
49
|
+
// only once, because of the empty array second param
|
|
50
|
+
useEffect(() => {
|
|
51
|
+
scrollRef.current?.scrollIntoView?.({ block: 'center' })
|
|
52
|
+
}, [])
|
|
53
|
+
|
|
54
|
+
return (
|
|
55
|
+
<Paper ref={ref} elevation={12} className={classes.viewContainer}>
|
|
56
|
+
<div ref={scrollRef} style={{ display: 'flex' }}>
|
|
57
|
+
<ViewMenu model={view} IconProps={{ className: classes.icon }} />
|
|
58
|
+
<div className={classes.grow} />
|
|
59
|
+
|
|
60
|
+
<ViewContainerTitle view={view} />
|
|
61
|
+
<div className={classes.grow} />
|
|
62
|
+
<IconButton data-testid="minimize_view" onClick={onMinimize}>
|
|
63
|
+
{view.minimized ? (
|
|
64
|
+
<AddIcon className={classes.icon} fontSize="small" />
|
|
65
|
+
) : (
|
|
66
|
+
<MinimizeIcon className={classes.icon} fontSize="small" />
|
|
67
|
+
)}
|
|
68
|
+
</IconButton>
|
|
69
|
+
<IconButton data-testid="close_view" onClick={onClose}>
|
|
70
|
+
<CloseIcon className={classes.icon} fontSize="small" />
|
|
71
|
+
</IconButton>
|
|
72
|
+
</div>
|
|
73
|
+
<Paper>{children}</Paper>
|
|
74
|
+
</Paper>
|
|
75
|
+
)
|
|
76
|
+
})
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { Tooltip } from '@mui/material'
|
|
3
|
+
import { makeStyles } from 'tss-react/mui'
|
|
4
|
+
import { observer } from 'mobx-react'
|
|
5
|
+
|
|
6
|
+
// locals
|
|
7
|
+
import EditableTypography from '@jbrowse/core/ui/EditableTypography'
|
|
8
|
+
import { IBaseViewModel } from '@jbrowse/core/pluggableElementTypes'
|
|
9
|
+
|
|
10
|
+
const useStyles = makeStyles()(theme => ({
|
|
11
|
+
input: {
|
|
12
|
+
paddingBottom: 0,
|
|
13
|
+
paddingTop: 2,
|
|
14
|
+
},
|
|
15
|
+
inputBase: {
|
|
16
|
+
color: theme.palette.secondary.contrastText,
|
|
17
|
+
},
|
|
18
|
+
inputRoot: {
|
|
19
|
+
'&:hover': {
|
|
20
|
+
backgroundColor: theme.palette.secondary.light,
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
inputFocused: {
|
|
24
|
+
borderColor: theme.palette.primary.main,
|
|
25
|
+
backgroundColor: theme.palette.secondary.light,
|
|
26
|
+
},
|
|
27
|
+
}))
|
|
28
|
+
export default observer(function ViewContainerTitle({
|
|
29
|
+
view,
|
|
30
|
+
}: {
|
|
31
|
+
view: IBaseViewModel
|
|
32
|
+
}) {
|
|
33
|
+
const { classes } = useStyles()
|
|
34
|
+
return (
|
|
35
|
+
<Tooltip title="Rename view" arrow>
|
|
36
|
+
<EditableTypography
|
|
37
|
+
value={
|
|
38
|
+
view.displayName ||
|
|
39
|
+
// @ts-expect-error
|
|
40
|
+
`${view.assemblyNames?.join(',') || 'Untitled view'}${
|
|
41
|
+
view.minimized ? ' (minimized)' : ''
|
|
42
|
+
}`
|
|
43
|
+
}
|
|
44
|
+
setValue={val => view.setDisplayName(val)}
|
|
45
|
+
variant="body2"
|
|
46
|
+
classes={{
|
|
47
|
+
input: classes.input,
|
|
48
|
+
inputBase: classes.inputBase,
|
|
49
|
+
inputRoot: classes.inputRoot,
|
|
50
|
+
inputFocused: classes.inputFocused,
|
|
51
|
+
}}
|
|
52
|
+
/>
|
|
53
|
+
</Tooltip>
|
|
54
|
+
)
|
|
55
|
+
})
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import React, { useState } from 'react'
|
|
2
|
+
import {
|
|
3
|
+
Button,
|
|
4
|
+
FormControl,
|
|
5
|
+
MenuItem,
|
|
6
|
+
Paper,
|
|
7
|
+
Select,
|
|
8
|
+
Typography,
|
|
9
|
+
} from '@mui/material'
|
|
10
|
+
import { observer } from 'mobx-react'
|
|
11
|
+
import { makeStyles } from 'tss-react/mui'
|
|
12
|
+
|
|
13
|
+
// locals
|
|
14
|
+
import { getEnv, SessionWithDrawerWidgets } from '@jbrowse/core/util'
|
|
15
|
+
|
|
16
|
+
// ui elements
|
|
17
|
+
import { MenuItem as JBMenuItem } from '@jbrowse/core/ui/Menu'
|
|
18
|
+
import { SnackbarMessage } from '@jbrowse/core/ui/SnackbarModel'
|
|
19
|
+
|
|
20
|
+
type AppSession = SessionWithDrawerWidgets & {
|
|
21
|
+
savedSessionNames: string[]
|
|
22
|
+
menus: { label: string; menuItems: JBMenuItem[] }[]
|
|
23
|
+
renameCurrentSession: (arg: string) => void
|
|
24
|
+
snackbarMessages: SnackbarMessage[]
|
|
25
|
+
popSnackbarMessage: () => unknown
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const useStyles = makeStyles()(theme => ({
|
|
29
|
+
selectPaper: {
|
|
30
|
+
padding: theme.spacing(4),
|
|
31
|
+
},
|
|
32
|
+
}))
|
|
33
|
+
|
|
34
|
+
const ViewLauncher = observer(({ session }: { session: AppSession }) => {
|
|
35
|
+
const { classes } = useStyles()
|
|
36
|
+
const { pluginManager } = getEnv(session)
|
|
37
|
+
const viewTypes = pluginManager.getElementTypeRecord('view').all()
|
|
38
|
+
const [value, setValue] = useState(viewTypes[0]?.name)
|
|
39
|
+
return (
|
|
40
|
+
<Paper className={classes.selectPaper}>
|
|
41
|
+
<Typography>Select a view to launch</Typography>
|
|
42
|
+
<FormControl style={{ margin: 2 }}>
|
|
43
|
+
<Select value={value} onChange={event => setValue(event.target.value)}>
|
|
44
|
+
{viewTypes.map(({ displayName, name }) => (
|
|
45
|
+
<MenuItem key={name} value={name}>
|
|
46
|
+
{displayName}
|
|
47
|
+
</MenuItem>
|
|
48
|
+
))}
|
|
49
|
+
</Select>
|
|
50
|
+
</FormControl>
|
|
51
|
+
<FormControl style={{ margin: 2 }}>
|
|
52
|
+
<Button
|
|
53
|
+
onClick={() => session.addView(value, {})}
|
|
54
|
+
variant="contained"
|
|
55
|
+
color="primary"
|
|
56
|
+
>
|
|
57
|
+
Launch view
|
|
58
|
+
</Button>
|
|
59
|
+
</FormControl>
|
|
60
|
+
</Paper>
|
|
61
|
+
)
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
export default ViewLauncher
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import {
|
|
3
|
+
SvgIconProps,
|
|
4
|
+
IconButtonProps as IconButtonPropsType,
|
|
5
|
+
} from '@mui/material'
|
|
6
|
+
import { observer } from 'mobx-react'
|
|
7
|
+
import { getSession } from '@jbrowse/core/util'
|
|
8
|
+
import CascadingMenuButton from '@jbrowse/core/ui/CascadingMenuButton'
|
|
9
|
+
import { IBaseViewModel } from '@jbrowse/core/pluggableElementTypes/models'
|
|
10
|
+
|
|
11
|
+
// icons
|
|
12
|
+
import MenuIcon from '@mui/icons-material/Menu'
|
|
13
|
+
import ArrowDownward from '@mui/icons-material/ArrowDownward'
|
|
14
|
+
import ArrowUpward from '@mui/icons-material/ArrowUpward'
|
|
15
|
+
|
|
16
|
+
const ViewMenu = observer(function ({
|
|
17
|
+
model,
|
|
18
|
+
IconButtonProps,
|
|
19
|
+
IconProps,
|
|
20
|
+
}: {
|
|
21
|
+
model: IBaseViewModel
|
|
22
|
+
IconButtonProps?: IconButtonPropsType
|
|
23
|
+
IconProps: SvgIconProps
|
|
24
|
+
}) {
|
|
25
|
+
const { menuItems } = model
|
|
26
|
+
const session = getSession(model)
|
|
27
|
+
|
|
28
|
+
const items = [
|
|
29
|
+
...(session.views.length > 1
|
|
30
|
+
? [
|
|
31
|
+
{
|
|
32
|
+
label: 'Move view up',
|
|
33
|
+
icon: ArrowUpward,
|
|
34
|
+
onClick: () => session.moveViewUp(model.id),
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
label: 'Move view down',
|
|
38
|
+
icon: ArrowDownward,
|
|
39
|
+
onClick: () => session.moveViewDown(model.id),
|
|
40
|
+
},
|
|
41
|
+
]
|
|
42
|
+
: []),
|
|
43
|
+
|
|
44
|
+
// <=1.3.3 didn't use a function, so check as value also
|
|
45
|
+
...((typeof menuItems === 'function' ? menuItems() : menuItems) || []),
|
|
46
|
+
]
|
|
47
|
+
|
|
48
|
+
return items.length ? (
|
|
49
|
+
<CascadingMenuButton menuItems={items} data-testid="view_menu_icon">
|
|
50
|
+
<MenuIcon {...IconProps} fontSize="small" />
|
|
51
|
+
</CascadingMenuButton>
|
|
52
|
+
) : null
|
|
53
|
+
})
|
|
54
|
+
export default ViewMenu
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import React, { Suspense } from 'react'
|
|
2
|
+
import { ErrorBoundary } from 'react-error-boundary'
|
|
3
|
+
import { observer } from 'mobx-react'
|
|
4
|
+
|
|
5
|
+
// locals
|
|
6
|
+
import {
|
|
7
|
+
getEnv,
|
|
8
|
+
AbstractViewModel,
|
|
9
|
+
SessionWithDrawerWidgets,
|
|
10
|
+
} from '@jbrowse/core/util'
|
|
11
|
+
import { SnackbarMessage } from '@jbrowse/core/ui/SnackbarModel'
|
|
12
|
+
|
|
13
|
+
// ui elements
|
|
14
|
+
import ErrorMessage from '@jbrowse/core/ui/ErrorMessage'
|
|
15
|
+
import LoadingEllipses from '@jbrowse/core/ui/LoadingEllipses'
|
|
16
|
+
import { MenuItem as JBMenuItem } from '@jbrowse/core/ui/Menu'
|
|
17
|
+
|
|
18
|
+
// locals
|
|
19
|
+
import ViewContainer from './ViewContainer'
|
|
20
|
+
|
|
21
|
+
type AppSession = SessionWithDrawerWidgets & {
|
|
22
|
+
savedSessionNames: string[]
|
|
23
|
+
menus: { label: string; menuItems: JBMenuItem[] }[]
|
|
24
|
+
snackbarMessages: SnackbarMessage[]
|
|
25
|
+
renameCurrentSession: (arg: string) => void
|
|
26
|
+
popSnackbarMessage: () => unknown
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const ViewPanel = observer(function ({
|
|
30
|
+
view,
|
|
31
|
+
session,
|
|
32
|
+
}: {
|
|
33
|
+
view: AbstractViewModel
|
|
34
|
+
session: AppSession
|
|
35
|
+
}) {
|
|
36
|
+
const { pluginManager } = getEnv(session)
|
|
37
|
+
const viewType = pluginManager.getViewType(view.type)
|
|
38
|
+
if (!viewType) {
|
|
39
|
+
throw new Error(`unknown view type ${view.type}`)
|
|
40
|
+
}
|
|
41
|
+
const { ReactComponent } = viewType
|
|
42
|
+
return (
|
|
43
|
+
<ViewContainer
|
|
44
|
+
view={view}
|
|
45
|
+
onClose={() => session.removeView(view)}
|
|
46
|
+
onMinimize={() => view.setMinimized(!view.minimized)}
|
|
47
|
+
>
|
|
48
|
+
{!view.minimized ? (
|
|
49
|
+
<ErrorBoundary
|
|
50
|
+
FallbackComponent={({ error }) => <ErrorMessage error={error} />}
|
|
51
|
+
>
|
|
52
|
+
<Suspense fallback={<LoadingEllipses variant="h6" />}>
|
|
53
|
+
<ReactComponent model={view} session={session} />
|
|
54
|
+
</Suspense>
|
|
55
|
+
</ErrorBoundary>
|
|
56
|
+
) : (
|
|
57
|
+
false
|
|
58
|
+
)}
|
|
59
|
+
</ViewContainer>
|
|
60
|
+
)
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
export default ViewPanel
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './App'
|
package/src/ui/index.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './App'
|