@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,157 @@
|
|
|
1
|
+
import { MenuItem } from '@jbrowse/core/ui/Menu'
|
|
2
|
+
import { types } from 'mobx-state-tree'
|
|
3
|
+
|
|
4
|
+
export interface Menu {
|
|
5
|
+
label: string
|
|
6
|
+
menuItems: MenuItem[]
|
|
7
|
+
}
|
|
8
|
+
export function RootAppMenuMixin() {
|
|
9
|
+
return types.model({}).actions(s => {
|
|
10
|
+
const self = s as { menus: Menu[] }
|
|
11
|
+
return {
|
|
12
|
+
/**
|
|
13
|
+
* #action
|
|
14
|
+
*/
|
|
15
|
+
setMenus(newMenus: Menu[]) {
|
|
16
|
+
self.menus = newMenus
|
|
17
|
+
},
|
|
18
|
+
/**
|
|
19
|
+
* #action
|
|
20
|
+
* Add a top-level menu
|
|
21
|
+
* @param menuName - Name of the menu to insert.
|
|
22
|
+
* @returns The new length of the top-level menus array
|
|
23
|
+
*/
|
|
24
|
+
appendMenu(menuName: string) {
|
|
25
|
+
return self.menus.push({ label: menuName, menuItems: [] })
|
|
26
|
+
},
|
|
27
|
+
/**
|
|
28
|
+
* #action
|
|
29
|
+
* Insert a top-level menu
|
|
30
|
+
* @param menuName - Name of the menu to insert.
|
|
31
|
+
* @param position - Position to insert menu. If negative, counts from th
|
|
32
|
+
* end, e.g. `insertMenu('My Menu', -1)` will insert the menu as the
|
|
33
|
+
* second-to-last one.
|
|
34
|
+
* @returns The new length of the top-level menus array
|
|
35
|
+
*/
|
|
36
|
+
insertMenu(menuName: string, position: number) {
|
|
37
|
+
self.menus.splice(
|
|
38
|
+
(position < 0 ? self.menus.length : 0) + position,
|
|
39
|
+
0,
|
|
40
|
+
{
|
|
41
|
+
label: menuName,
|
|
42
|
+
menuItems: [],
|
|
43
|
+
},
|
|
44
|
+
)
|
|
45
|
+
return self.menus.length
|
|
46
|
+
},
|
|
47
|
+
/**
|
|
48
|
+
* #action
|
|
49
|
+
* Add a menu item to a top-level menu
|
|
50
|
+
* @param menuName - Name of the top-level menu to append to.
|
|
51
|
+
* @param menuItem - Menu item to append.
|
|
52
|
+
* @returns The new length of the menu
|
|
53
|
+
*/
|
|
54
|
+
appendToMenu(menuName: string, menuItem: MenuItem) {
|
|
55
|
+
const menu = self.menus.find(m => m.label === menuName)
|
|
56
|
+
if (!menu) {
|
|
57
|
+
self.menus.push({ label: menuName, menuItems: [menuItem] })
|
|
58
|
+
return 1
|
|
59
|
+
}
|
|
60
|
+
return menu.menuItems.push(menuItem)
|
|
61
|
+
},
|
|
62
|
+
/**
|
|
63
|
+
* #action
|
|
64
|
+
* Insert a menu item into a top-level menu
|
|
65
|
+
* @param menuName - Name of the top-level menu to insert into
|
|
66
|
+
* @param menuItem - Menu item to insert
|
|
67
|
+
* @param position - Position to insert menu item. If negative, counts
|
|
68
|
+
* from the end, e.g. `insertMenu('My Menu', -1)` will insert the menu as
|
|
69
|
+
* the second-to-last one.
|
|
70
|
+
* @returns The new length of the menu
|
|
71
|
+
*/
|
|
72
|
+
insertInMenu(menuName: string, menuItem: MenuItem, position: number) {
|
|
73
|
+
const menu = self.menus.find(m => m.label === menuName)
|
|
74
|
+
if (!menu) {
|
|
75
|
+
self.menus.push({ label: menuName, menuItems: [menuItem] })
|
|
76
|
+
return 1
|
|
77
|
+
}
|
|
78
|
+
const insertPosition =
|
|
79
|
+
position < 0 ? menu.menuItems.length + position : position
|
|
80
|
+
menu.menuItems.splice(insertPosition, 0, menuItem)
|
|
81
|
+
return menu.menuItems.length
|
|
82
|
+
},
|
|
83
|
+
/**
|
|
84
|
+
* #action
|
|
85
|
+
* Add a menu item to a sub-menu
|
|
86
|
+
* @param menuPath - Path to the sub-menu to add to, starting with the
|
|
87
|
+
* top-level menu (e.g. `['File', 'Insert']`).
|
|
88
|
+
* @param menuItem - Menu item to append.
|
|
89
|
+
* @returns The new length of the sub-menu
|
|
90
|
+
*/
|
|
91
|
+
appendToSubMenu(menuPath: string[], menuItem: MenuItem) {
|
|
92
|
+
let topMenu = self.menus.find(m => m.label === menuPath[0])
|
|
93
|
+
if (!topMenu) {
|
|
94
|
+
const idx = this.appendMenu(menuPath[0])
|
|
95
|
+
topMenu = self.menus[idx - 1]
|
|
96
|
+
}
|
|
97
|
+
let { menuItems: subMenu } = topMenu
|
|
98
|
+
const pathSoFar = [menuPath[0]]
|
|
99
|
+
menuPath.slice(1).forEach(menuName => {
|
|
100
|
+
pathSoFar.push(menuName)
|
|
101
|
+
let sm = subMenu.find(mi => 'label' in mi && mi.label === menuName)
|
|
102
|
+
if (!sm) {
|
|
103
|
+
const idx = subMenu.push({ label: menuName, subMenu: [] })
|
|
104
|
+
sm = subMenu[idx - 1]
|
|
105
|
+
}
|
|
106
|
+
if (!('subMenu' in sm)) {
|
|
107
|
+
throw new Error(
|
|
108
|
+
`"${menuName}" in path "${pathSoFar}" is not a subMenu`,
|
|
109
|
+
)
|
|
110
|
+
}
|
|
111
|
+
subMenu = sm.subMenu
|
|
112
|
+
})
|
|
113
|
+
return subMenu.push(menuItem)
|
|
114
|
+
},
|
|
115
|
+
/**
|
|
116
|
+
* #action
|
|
117
|
+
* Insert a menu item into a sub-menu
|
|
118
|
+
* @param menuPath - Path to the sub-menu to add to, starting with the
|
|
119
|
+
* top-level menu (e.g. `['File', 'Insert']`).
|
|
120
|
+
* @param menuItem - Menu item to insert.
|
|
121
|
+
* @param position - Position to insert menu item. If negative, counts
|
|
122
|
+
* from the end, e.g. `insertMenu('My Menu', -1)` will insert the menu as
|
|
123
|
+
* the second-to-last one.
|
|
124
|
+
* @returns The new length of the sub-menu
|
|
125
|
+
*/
|
|
126
|
+
insertInSubMenu(
|
|
127
|
+
menuPath: string[],
|
|
128
|
+
menuItem: MenuItem,
|
|
129
|
+
position: number,
|
|
130
|
+
) {
|
|
131
|
+
let topMenu = self.menus.find(m => m.label === menuPath[0])
|
|
132
|
+
if (!topMenu) {
|
|
133
|
+
const idx = this.appendMenu(menuPath[0])
|
|
134
|
+
topMenu = self.menus[idx - 1]
|
|
135
|
+
}
|
|
136
|
+
let { menuItems: subMenu } = topMenu
|
|
137
|
+
const pathSoFar = [menuPath[0]]
|
|
138
|
+
menuPath.slice(1).forEach(menuName => {
|
|
139
|
+
pathSoFar.push(menuName)
|
|
140
|
+
let sm = subMenu.find(mi => 'label' in mi && mi.label === menuName)
|
|
141
|
+
if (!sm) {
|
|
142
|
+
const idx = subMenu.push({ label: menuName, subMenu: [] })
|
|
143
|
+
sm = subMenu[idx - 1]
|
|
144
|
+
}
|
|
145
|
+
if (!('subMenu' in sm)) {
|
|
146
|
+
throw new Error(
|
|
147
|
+
`"${menuName}" in path "${pathSoFar}" is not a subMenu`,
|
|
148
|
+
)
|
|
149
|
+
}
|
|
150
|
+
subMenu = sm.subMenu
|
|
151
|
+
})
|
|
152
|
+
subMenu.splice(position, 0, menuItem)
|
|
153
|
+
return subMenu.length
|
|
154
|
+
},
|
|
155
|
+
}
|
|
156
|
+
})
|
|
157
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
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 }
|
|
@@ -0,0 +1,45 @@
|
|
|
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
|
+
})
|
|
@@ -0,0 +1,89 @@
|
|
|
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
|
|
@@ -0,0 +1,22 @@
|
|
|
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
|
+
})
|
|
@@ -0,0 +1,56 @@
|
|
|
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)
|