@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.
Files changed (107) hide show
  1. package/LICENSE +201 -0
  2. package/dist/Assemblies/SessionAssembliesMixin.d.ts +72 -0
  3. package/dist/Assemblies/SessionAssembliesMixin.js +44 -0
  4. package/dist/Assemblies/TemporaryAssembliesMixin.d.ts +22 -0
  5. package/dist/Assemblies/TemporaryAssembliesMixin.js +45 -0
  6. package/dist/Assemblies/index.d.ts +2 -0
  7. package/dist/Assemblies/index.js +18 -0
  8. package/dist/HistoryManagement/index.d.ts +30 -0
  9. package/dist/HistoryManagement/index.js +54 -0
  10. package/dist/JBrowseConfig/index.d.ts +158 -0
  11. package/dist/JBrowseConfig/index.js +155 -0
  12. package/dist/JBrowseModel/index.d.ts +144 -0
  13. package/dist/JBrowseModel/index.js +132 -0
  14. package/dist/RootMenu/index.d.ts +68 -0
  15. package/dist/RootMenu/index.js +142 -0
  16. package/dist/index.d.ts +6 -0
  17. package/dist/index.js +22 -0
  18. package/dist/ui/App/App.d.ts +19 -0
  19. package/dist/ui/App/App.js +95 -0
  20. package/dist/ui/App/AppFab.d.ts +6 -0
  21. package/dist/ui/App/AppFab.js +32 -0
  22. package/dist/ui/App/AppToolbar.d.ts +19 -0
  23. package/dist/ui/App/AppToolbar.js +54 -0
  24. package/dist/ui/App/DialogQueue.d.ts +6 -0
  25. package/dist/ui/App/DialogQueue.js +32 -0
  26. package/dist/ui/App/Drawer.d.ts +8 -0
  27. package/dist/ui/App/Drawer.js +34 -0
  28. package/dist/ui/App/DrawerWidget.d.ts +6 -0
  29. package/dist/ui/App/DrawerWidget.js +132 -0
  30. package/dist/ui/App/ViewContainer.d.ts +9 -0
  31. package/dist/ui/App/ViewContainer.js +76 -0
  32. package/dist/ui/App/ViewContainerTitle.d.ts +6 -0
  33. package/dist/ui/App/ViewContainerTitle.js +42 -0
  34. package/dist/ui/App/ViewLauncher.d.ts +18 -0
  35. package/dist/ui/App/ViewLauncher.js +50 -0
  36. package/dist/ui/App/ViewMenu.d.ts +9 -0
  37. package/dist/ui/App/ViewMenu.js +38 -0
  38. package/dist/ui/App/ViewPanel.d.ts +19 -0
  39. package/dist/ui/App/ViewPanel.js +50 -0
  40. package/dist/ui/App/index.d.ts +1 -0
  41. package/dist/ui/App/index.js +17 -0
  42. package/dist/ui/index.d.ts +1 -0
  43. package/dist/ui/index.js +17 -0
  44. package/esm/Assemblies/SessionAssembliesMixin.d.ts +72 -0
  45. package/esm/Assemblies/SessionAssembliesMixin.js +40 -0
  46. package/esm/Assemblies/TemporaryAssembliesMixin.d.ts +22 -0
  47. package/esm/Assemblies/TemporaryAssembliesMixin.js +41 -0
  48. package/esm/Assemblies/index.d.ts +2 -0
  49. package/esm/Assemblies/index.js +2 -0
  50. package/esm/HistoryManagement/index.d.ts +30 -0
  51. package/esm/HistoryManagement/index.js +47 -0
  52. package/esm/JBrowseConfig/index.d.ts +158 -0
  53. package/esm/JBrowseConfig/index.js +148 -0
  54. package/esm/JBrowseModel/index.d.ts +144 -0
  55. package/esm/JBrowseModel/index.js +128 -0
  56. package/esm/RootMenu/index.d.ts +68 -0
  57. package/esm/RootMenu/index.js +138 -0
  58. package/esm/index.d.ts +6 -0
  59. package/esm/index.js +6 -0
  60. package/esm/ui/App/App.d.ts +19 -0
  61. package/esm/ui/App/App.js +66 -0
  62. package/esm/ui/App/AppFab.d.ts +6 -0
  63. package/esm/ui/App/AppFab.js +27 -0
  64. package/esm/ui/App/AppToolbar.d.ts +19 -0
  65. package/esm/ui/App/AppToolbar.js +49 -0
  66. package/esm/ui/App/DialogQueue.d.ts +6 -0
  67. package/esm/ui/App/DialogQueue.js +7 -0
  68. package/esm/ui/App/Drawer.d.ts +8 -0
  69. package/esm/ui/App/Drawer.js +29 -0
  70. package/esm/ui/App/DrawerWidget.d.ts +6 -0
  71. package/esm/ui/App/DrawerWidget.js +104 -0
  72. package/esm/ui/App/ViewContainer.d.ts +9 -0
  73. package/esm/ui/App/ViewContainer.js +48 -0
  74. package/esm/ui/App/ViewContainerTitle.d.ts +6 -0
  75. package/esm/ui/App/ViewContainerTitle.js +37 -0
  76. package/esm/ui/App/ViewLauncher.d.ts +18 -0
  77. package/esm/ui/App/ViewLauncher.js +25 -0
  78. package/esm/ui/App/ViewMenu.d.ts +9 -0
  79. package/esm/ui/App/ViewMenu.js +33 -0
  80. package/esm/ui/App/ViewPanel.d.ts +19 -0
  81. package/esm/ui/App/ViewPanel.js +22 -0
  82. package/esm/ui/App/index.d.ts +1 -0
  83. package/esm/ui/App/index.js +1 -0
  84. package/esm/ui/index.d.ts +1 -0
  85. package/esm/ui/index.js +1 -0
  86. package/package.json +66 -0
  87. package/src/Assemblies/SessionAssembliesMixin.ts +50 -0
  88. package/src/Assemblies/TemporaryAssembliesMixin.ts +51 -0
  89. package/src/Assemblies/index.ts +2 -0
  90. package/src/HistoryManagement/index.ts +56 -0
  91. package/src/JBrowseConfig/index.ts +173 -0
  92. package/src/JBrowseModel/index.ts +150 -0
  93. package/src/RootMenu/index.ts +157 -0
  94. package/src/index.ts +6 -0
  95. package/src/ui/App/App.tsx +117 -0
  96. package/src/ui/App/AppFab.tsx +45 -0
  97. package/src/ui/App/AppToolbar.tsx +89 -0
  98. package/src/ui/App/DialogQueue.tsx +22 -0
  99. package/src/ui/App/Drawer.tsx +56 -0
  100. package/src/ui/App/DrawerWidget.tsx +238 -0
  101. package/src/ui/App/ViewContainer.tsx +76 -0
  102. package/src/ui/App/ViewContainerTitle.tsx +55 -0
  103. package/src/ui/App/ViewLauncher.tsx +64 -0
  104. package/src/ui/App/ViewMenu.tsx +54 -0
  105. package/src/ui/App/ViewPanel.tsx +63 -0
  106. package/src/ui/App/index.ts +1 -0
  107. 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'
@@ -0,0 +1 @@
1
+ export * from './App'