@jbrowse/app-core 2.6.1 → 2.6.2

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.
@@ -1,117 +0,0 @@
1
- import React, { Suspense, lazy } from 'react'
2
- import { AppBar } from '@mui/material'
3
- import { makeStyles } from 'tss-react/mui'
4
- import { observer } from 'mobx-react'
5
- import { SessionWithDrawerWidgets } from '@jbrowse/core/util'
6
- import Snackbar from '@jbrowse/core/ui/Snackbar'
7
- import { SnackbarMessage } from '@jbrowse/core/ui/SnackbarModel'
8
- import { MenuItem as JBMenuItem } from '@jbrowse/core/ui/Menu'
9
-
10
- // locals
11
- import AppToolbar from './AppToolbar'
12
- import ViewLauncher from './ViewLauncher'
13
- import ViewPanel from './ViewPanel'
14
- import DialogQueue from './DialogQueue'
15
- import AppFab from './AppFab'
16
-
17
- const DrawerWidget = lazy(() => import('./DrawerWidget'))
18
-
19
- const useStyles = makeStyles()(theme => ({
20
- root: {
21
- display: 'grid',
22
- height: '100vh',
23
- width: '100%',
24
- colorScheme: theme.palette.mode,
25
- },
26
- appContainer: {
27
- gridColumn: 'main',
28
- display: 'grid',
29
- gridTemplateRows: '[menubar] min-content [components] auto',
30
- height: '100vh',
31
- },
32
- viewContainer: {
33
- overflowY: 'auto',
34
- gridRow: 'components',
35
- },
36
- appBar: {
37
- flexGrow: 1,
38
- gridRow: 'menubar',
39
- },
40
- }))
41
-
42
- type Props = {
43
- HeaderButtons?: React.ReactElement
44
- session: SessionWithDrawerWidgets & {
45
- savedSessionNames: string[]
46
- menus: { label: string; menuItems: JBMenuItem[] }[]
47
- renameCurrentSession: (arg: string) => void
48
- snackbarMessages: SnackbarMessage[]
49
- popSnackbarMessage: () => unknown
50
- }
51
- }
52
-
53
- const LazyDrawerWidget = observer(function (props: Props) {
54
- const { session } = props
55
- return (
56
- <Suspense fallback={<React.Fragment />}>
57
- <DrawerWidget session={session} />
58
- </Suspense>
59
- )
60
- })
61
-
62
- const ViewContainer = observer(function (props: Props) {
63
- const { session } = props
64
- const { views } = session
65
- const { classes } = useStyles()
66
- return (
67
- <div className={classes.viewContainer}>
68
- {views.length > 0 ? (
69
- views.map(view => (
70
- <ViewPanel key={`view-${view.id}`} view={view} session={session} />
71
- ))
72
- ) : (
73
- <ViewLauncher {...props} />
74
- )}
75
-
76
- {/* blank space at the bottom of screen allows scroll */}
77
- <div style={{ height: 300 }} />
78
- </div>
79
- )
80
- })
81
-
82
- const App = observer(function (props: Props) {
83
- const { session } = props
84
- const { classes } = useStyles()
85
- const { minimized, visibleWidget, drawerWidth, drawerPosition } = session
86
- const drawerVisible = visibleWidget && !minimized
87
- const d = drawerVisible ? `[drawer] ${drawerWidth}px` : undefined
88
- const grid =
89
- drawerPosition === 'right' ? ['[main] 1fr', d] : [d, '[main] 1fr']
90
-
91
- return (
92
- <div
93
- className={classes.root}
94
- style={{ gridTemplateColumns: grid?.filter(f => !!f).join(' ') }}
95
- >
96
- {drawerVisible && drawerPosition === 'left' ? (
97
- <LazyDrawerWidget session={session} />
98
- ) : null}
99
- <DialogQueue session={session} />
100
- <div className={classes.appContainer}>
101
- <AppBar className={classes.appBar} position="static">
102
- <AppToolbar {...props} />
103
- </AppBar>
104
- <ViewContainer {...props} />
105
- </div>
106
- <AppFab session={session} />
107
-
108
- {drawerVisible && drawerPosition === 'right' ? (
109
- <LazyDrawerWidget session={session} />
110
- ) : null}
111
-
112
- <Snackbar session={session} />
113
- </div>
114
- )
115
- })
116
-
117
- export { App }
@@ -1,45 +0,0 @@
1
- import React from 'react'
2
- import { Fab, Tooltip } from '@mui/material'
3
- import { makeStyles } from 'tss-react/mui'
4
- import { observer } from 'mobx-react'
5
- import { SessionWithDrawerWidgets } from '@jbrowse/core/util'
6
-
7
- // icons
8
- import LaunchIcon from '@mui/icons-material/Launch'
9
-
10
- const useStyles = makeStyles()(theme => ({
11
- left: {
12
- zIndex: 10000,
13
- position: 'fixed',
14
- bottom: theme.spacing(2),
15
- left: theme.spacing(2),
16
- },
17
- right: {
18
- zIndex: 10000,
19
- position: 'fixed',
20
- bottom: theme.spacing(2),
21
- right: theme.spacing(2),
22
- },
23
- }))
24
-
25
- export default observer(function AppFab({
26
- session,
27
- }: {
28
- session: SessionWithDrawerWidgets
29
- }) {
30
- const { minimized, activeWidgets, drawerPosition } = session
31
- const { classes } = useStyles()
32
-
33
- return activeWidgets.size > 0 && minimized ? (
34
- <Tooltip title="Open drawer widget">
35
- <Fab
36
- className={drawerPosition === 'right' ? classes.right : classes.left}
37
- color="primary"
38
- data-testid="drawer-maximize"
39
- onClick={() => session.showWidgetDrawer()}
40
- >
41
- <LaunchIcon />
42
- </Fab>
43
- </Tooltip>
44
- ) : null
45
- })
@@ -1,89 +0,0 @@
1
- import React from 'react'
2
- import { Toolbar, Tooltip } from '@mui/material'
3
- import { makeStyles } from 'tss-react/mui'
4
- import { observer } from 'mobx-react'
5
- import { SessionWithDrawerWidgets } from '@jbrowse/core/util'
6
- import DropDownMenu from '@jbrowse/core/ui/DropDownMenu'
7
- import EditableTypography from '@jbrowse/core/ui/EditableTypography'
8
- import AppLogo from '@jbrowse/core/ui/AppLogo'
9
- import { MenuItem as JBMenuItem } from '@jbrowse/core/ui/Menu'
10
- import { SnackbarMessage } from '@jbrowse/core/ui/SnackbarModel'
11
-
12
- const useStyles = makeStyles()(theme => ({
13
- grow: {
14
- flexGrow: 1,
15
- },
16
- inputBase: {
17
- color: theme.palette.primary.contrastText,
18
- },
19
- inputRoot: {
20
- '&:hover': {
21
- backgroundColor: theme.palette.primary.light,
22
- },
23
- },
24
- inputFocused: {
25
- borderColor: theme.palette.secondary.main,
26
- backgroundColor: theme.palette.primary.light,
27
- },
28
- }))
29
-
30
- type AppSession = SessionWithDrawerWidgets & {
31
- savedSessionNames: string[]
32
- menus: { label: string; menuItems: JBMenuItem[] }[]
33
- renameCurrentSession: (arg: string) => void
34
- snackbarMessages: SnackbarMessage[]
35
- popSnackbarMessage: () => unknown
36
- }
37
-
38
- const AppToolbar = observer(function ({
39
- session,
40
- HeaderButtons = <div />,
41
- }: {
42
- HeaderButtons?: React.ReactElement
43
- session: AppSession
44
- }) {
45
- const { classes } = useStyles()
46
- const { savedSessionNames, name, menus } = session
47
-
48
- return (
49
- <Toolbar>
50
- {menus.map(menu => (
51
- <DropDownMenu
52
- key={menu.label}
53
- menuTitle={menu.label}
54
- menuItems={menu.menuItems}
55
- session={session}
56
- />
57
- ))}
58
- <div className={classes.grow} />
59
- <Tooltip title="Rename Session" arrow>
60
- <EditableTypography
61
- value={name}
62
- setValue={newName => {
63
- if (savedSessionNames?.includes(newName)) {
64
- session.notify(
65
- `Cannot rename session to "${newName}", a saved session with that name already exists`,
66
- 'warning',
67
- )
68
- } else {
69
- session.renameCurrentSession(newName)
70
- }
71
- }}
72
- variant="body1"
73
- classes={{
74
- inputBase: classes.inputBase,
75
- inputRoot: classes.inputRoot,
76
- inputFocused: classes.inputFocused,
77
- }}
78
- />
79
- </Tooltip>
80
- {HeaderButtons}
81
- <div className={classes.grow} />
82
- <div style={{ width: 150, maxHeight: 48 }}>
83
- <AppLogo session={session} />
84
- </div>
85
- </Toolbar>
86
- )
87
- })
88
-
89
- export default AppToolbar
@@ -1,22 +0,0 @@
1
- import React, { Suspense } from 'react'
2
- import { observer } from 'mobx-react'
3
-
4
- // locals
5
- import { SessionWithDrawerWidgets } from '@jbrowse/core/util'
6
-
7
- export default observer(function ({
8
- session,
9
- }: {
10
- session: SessionWithDrawerWidgets
11
- }) {
12
- const { DialogComponent, DialogProps } = session
13
- return (
14
- <>
15
- {DialogComponent ? (
16
- <Suspense fallback={<React.Fragment />}>
17
- <DialogComponent {...DialogProps} />
18
- </Suspense>
19
- ) : null}
20
- </>
21
- )
22
- })
@@ -1,56 +0,0 @@
1
- import React from 'react'
2
- import { Paper } from '@mui/material'
3
- import { makeStyles } from 'tss-react/mui'
4
- import { observer } from 'mobx-react'
5
- import ResizeHandle from '@jbrowse/core/ui/ResizeHandle'
6
- import { SessionWithDrawerWidgets } from '@jbrowse/core/util/types'
7
-
8
- const useStyles = makeStyles()(theme => ({
9
- paper: {
10
- overflowY: 'auto',
11
- height: '100%',
12
- zIndex: theme.zIndex.drawer,
13
- outline: 'none',
14
- background: theme.palette.background.default,
15
- },
16
- resizeHandle: {
17
- width: 4,
18
- position: 'fixed',
19
- top: 0,
20
- zIndex: theme.zIndex.drawer + 1,
21
- },
22
- }))
23
-
24
- function Drawer({
25
- children,
26
- session,
27
- }: {
28
- children: React.ReactNode
29
- session: SessionWithDrawerWidgets
30
- }) {
31
- const { drawerPosition, drawerWidth } = session
32
- const { classes } = useStyles()
33
-
34
- return (
35
- <Paper className={classes.paper} elevation={16} square>
36
- {drawerPosition === 'right' ? (
37
- <ResizeHandle
38
- onDrag={session.resizeDrawer}
39
- className={classes.resizeHandle}
40
- vertical
41
- />
42
- ) : null}
43
- {children}
44
- {drawerPosition === 'left' ? (
45
- <ResizeHandle
46
- onDrag={session.resizeDrawer}
47
- className={classes.resizeHandle}
48
- style={{ left: drawerWidth }}
49
- vertical
50
- />
51
- ) : null}
52
- </Paper>
53
- )
54
- }
55
-
56
- export default observer(Drawer)
@@ -1,238 +0,0 @@
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
@@ -1,76 +0,0 @@
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
- })
@@ -1,55 +0,0 @@
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
- })