@jbrowse/app-core 4.3.0 → 5.0.0-beta.10
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/esm/AppSession/AppRootModel.d.ts +30 -0
- package/esm/AppSession/AppRootModel.js +1 -0
- package/esm/AppSession/AppSessionMixin.d.ts +1324 -0
- package/esm/AppSession/AppSessionMixin.js +62 -0
- package/esm/AppSession/index.d.ts +2 -0
- package/esm/AppSession/index.js +1 -0
- package/esm/Assemblies/AssembliesMixin.d.ts +294 -0
- package/esm/Assemblies/AssembliesMixin.js +154 -0
- package/esm/Assemblies/index.d.ts +1 -2
- package/esm/Assemblies/index.js +1 -2
- package/esm/HistoryManagement/index.d.ts +11 -0
- package/esm/HistoryManagement/index.js +43 -10
- package/esm/JBrowseConfig/RootConfiguration.d.ts +123 -78
- package/esm/JBrowseConfig/RootConfiguration.js +79 -7
- package/esm/JBrowseConfig/defaultShareUrl.d.ts +1 -0
- package/esm/JBrowseConfig/defaultShareUrl.js +5 -0
- package/esm/JBrowseConfig/index.d.ts +175 -84
- package/esm/JBrowseConfig/index.js +64 -2
- package/esm/JBrowseModel/index.d.ts +175 -97
- package/esm/JBrowseModel/index.js +132 -31
- package/esm/JbApi/index.d.ts +2 -0
- package/esm/JbApi/index.js +1 -0
- package/esm/JbApi/jbApi.d.ts +2767 -0
- package/esm/JbApi/jbApi.js +1383 -0
- package/esm/RootMenu/index.d.ts +69 -3
- package/esm/RootMenu/index.js +96 -61
- package/esm/SessionSpec/index.d.ts +6 -0
- package/esm/SessionSpec/index.js +3 -0
- package/esm/SessionSpec/lgvUrlInit.d.ts +79 -0
- package/esm/SessionSpec/lgvUrlInit.js +161 -0
- package/esm/SessionSpec/loadSessionSpec.d.ts +75 -0
- package/esm/SessionSpec/loadSessionSpec.js +473 -0
- package/esm/SessionSpec/parseSessionSpecUrl.d.ts +7 -0
- package/esm/SessionSpec/parseSessionSpecUrl.js +181 -0
- package/esm/SessionSpec/types.d.ts +37 -0
- package/esm/SessionSpec/types.js +1 -0
- package/esm/WorkspaceLayout/LayoutRenderer.d.ts +24 -0
- package/esm/WorkspaceLayout/LayoutRenderer.js +185 -0
- package/esm/WorkspaceLayout/PanelView.d.ts +13 -0
- package/esm/WorkspaceLayout/PanelView.js +99 -0
- package/esm/WorkspaceLayout/TabStrip.d.ts +14 -0
- package/esm/WorkspaceLayout/TabStrip.js +226 -0
- package/esm/WorkspaceLayout/WorkspaceContainer.d.ts +16 -0
- package/esm/WorkspaceLayout/WorkspaceContainer.js +107 -0
- package/esm/WorkspaceLayout/WorkspacePanelActions.d.ts +24 -0
- package/esm/WorkspaceLayout/WorkspacePanelActions.js +120 -0
- package/esm/WorkspaceLayout/WorkspaceTab.d.ts +27 -0
- package/esm/WorkspaceLayout/WorkspaceTab.js +105 -0
- package/esm/WorkspaceLayout/dockviewTheme.d.ts +69 -0
- package/esm/WorkspaceLayout/dockviewTheme.js +82 -0
- package/esm/WorkspaceLayout/dropZone.d.ts +105 -0
- package/esm/WorkspaceLayout/dropZone.js +113 -0
- package/esm/WorkspaceLayout/index.d.ts +8 -0
- package/esm/WorkspaceLayout/index.js +4 -0
- package/esm/WorkspaceLayout/model.d.ts +260 -0
- package/esm/WorkspaceLayout/model.js +586 -0
- package/esm/WorkspaceLayout/panelChrome.d.ts +19 -0
- package/esm/WorkspaceLayout/panelChrome.js +4 -0
- package/esm/WorkspaceLayout/spec.d.ts +88 -0
- package/esm/WorkspaceLayout/spec.js +226 -0
- package/esm/WorkspaceLayout/splitter.d.ts +25 -0
- package/esm/WorkspaceLayout/splitter.js +37 -0
- package/esm/WorkspaceLayout/tree.d.ts +158 -0
- package/esm/WorkspaceLayout/tree.js +379 -0
- package/esm/WorkspaceLayout/useLayoutDrag.d.ts +34 -0
- package/esm/WorkspaceLayout/useLayoutDrag.js +232 -0
- package/esm/index.d.ts +18 -9
- package/esm/index.js +16 -9
- package/esm/menus.d.ts +55 -68
- package/esm/menus.js +117 -86
- package/esm/queryParams.d.ts +9 -0
- package/esm/queryParams.js +93 -0
- package/esm/sessionModelFactory.d.ts +18 -0
- package/esm/sessionModelFactory.js +1 -0
- package/esm/ui/App/App.d.ts +3 -14
- package/esm/ui/App/App.js +39 -19
- package/esm/ui/App/AppFab.d.ts +12 -3
- package/esm/ui/App/AppFab.js +15 -8
- package/esm/ui/App/AppToolbar.d.ts +2 -14
- package/esm/ui/App/AppToolbar.js +45 -31
- package/esm/ui/App/ClassicViewsContainer.d.ts +4 -10
- package/esm/ui/App/ClassicViewsContainer.js +9 -8
- package/esm/ui/App/DialogQueue.d.ts +17 -1
- package/esm/ui/App/DialogQueue.js +30 -1
- package/esm/ui/App/Drawer.d.ts +1 -1
- package/esm/ui/App/Drawer.js +10 -39
- package/esm/ui/App/DrawerHeader.d.ts +2 -3
- package/esm/ui/App/DrawerHeader.js +24 -11
- package/esm/ui/App/DrawerHeaderHelpButton.d.ts +1 -1
- package/esm/ui/App/DrawerHeaderHelpButton.js +3 -3
- package/esm/ui/App/DrawerHeaderHelpDialog.d.ts +2 -2
- package/esm/ui/App/DrawerHeaderHelpDialog.js +3 -4
- package/esm/ui/App/DrawerWidget.d.ts +1 -1
- package/esm/ui/App/DrawerWidget.js +9 -21
- package/esm/ui/App/JBrowseTabMenu.d.ts +2 -3
- package/esm/ui/App/JBrowseTabMenu.js +24 -5
- package/esm/ui/App/ThemeModeButton.d.ts +12 -0
- package/esm/ui/App/ThemeModeButton.js +34 -0
- package/esm/ui/App/ViewButtons.d.ts +2 -4
- package/esm/ui/App/ViewButtons.js +15 -3
- package/esm/ui/App/ViewContainer.d.ts +3 -2
- package/esm/ui/App/ViewContainer.js +128 -24
- package/esm/ui/App/ViewContainerTitle.d.ts +1 -1
- package/esm/ui/App/ViewContainerTitle.js +21 -11
- package/esm/ui/App/ViewErrorFallback.d.ts +18 -0
- package/esm/ui/App/ViewErrorFallback.js +42 -0
- package/esm/ui/App/ViewHeader.d.ts +3 -4
- package/esm/ui/App/ViewHeader.js +28 -17
- package/esm/ui/App/ViewLauncher.d.ts +2 -8
- package/esm/ui/App/ViewLauncher.js +7 -5
- package/esm/ui/App/ViewMenu.d.ts +3 -5
- package/esm/ui/App/ViewMenu.js +87 -92
- package/esm/ui/App/ViewStack.d.ts +7 -0
- package/esm/ui/App/ViewStack.js +48 -0
- package/esm/ui/App/ViewWrapper.d.ts +15 -1
- package/esm/ui/App/ViewWrapper.js +26 -10
- package/esm/ui/App/ViewsContainer.d.ts +4 -11
- package/esm/ui/App/ViewsContainer.js +6 -9
- package/esm/ui/App/index.d.ts +2 -1
- package/esm/ui/App/index.js +3 -2
- package/esm/ui/App/lazyChunk.d.ts +13 -0
- package/esm/ui/App/lazyChunk.js +91 -0
- package/esm/ui/App/lazyParts.d.ts +32 -0
- package/esm/ui/App/lazyParts.js +8 -0
- package/esm/ui/App/preloadAppShell.d.ts +14 -0
- package/esm/ui/App/preloadAppShell.js +41 -0
- package/esm/ui/App/types.d.ts +17 -3
- package/esm/ui/App/types.js +1 -1
- package/esm/ui/App/useViewVisibility.d.ts +35 -0
- package/esm/ui/App/useViewVisibility.js +88 -0
- package/esm/ui/App/viewTitle.d.ts +12 -0
- package/esm/ui/App/viewTitle.js +17 -0
- package/esm/ui/index.d.ts +1 -1
- package/esm/ui/index.js +1 -1
- package/package.json +12 -17
- package/esm/AppFocus/index.d.ts +0 -5
- package/esm/AppFocus/index.js +0 -12
- package/esm/Assemblies/SessionAssembliesMixin.d.ts +0 -92
- package/esm/Assemblies/SessionAssembliesMixin.js +0 -53
- package/esm/Assemblies/TemporaryAssembliesMixin.d.ts +0 -10
- package/esm/Assemblies/TemporaryAssembliesMixin.js +0 -37
- package/esm/DockviewLayout/index.d.ts +0 -35
- package/esm/DockviewLayout/index.js +0 -111
- package/esm/ui/App/DockviewContext.d.ts +0 -22
- package/esm/ui/App/DockviewContext.js +0 -29
- package/esm/ui/App/DockviewLeftHeaderActions.d.ts +0 -2
- package/esm/ui/App/DockviewLeftHeaderActions.js +0 -58
- package/esm/ui/App/DockviewRightHeaderActions.d.ts +0 -2
- package/esm/ui/App/DockviewRightHeaderActions.js +0 -100
- package/esm/ui/App/DrawerControls.d.ts +0 -5
- package/esm/ui/App/DrawerControls.js +0 -25
- package/esm/ui/App/DrawerWidgetSelector.d.ts +0 -5
- package/esm/ui/App/DrawerWidgetSelector.js +0 -47
- package/esm/ui/App/JBrowseViewPanel.d.ts +0 -8
- package/esm/ui/App/JBrowseViewPanel.js +0 -40
- package/esm/ui/App/JBrowseViewTab.d.ts +0 -8
- package/esm/ui/App/JBrowseViewTab.js +0 -97
- package/esm/ui/App/ModalWidget.d.ts +0 -6
- package/esm/ui/App/ModalWidget.js +0 -48
- package/esm/ui/App/TiledViewsContainer.d.ts +0 -6
- package/esm/ui/App/TiledViewsContainer.js +0 -366
- package/esm/ui/App/copyView.d.ts +0 -1
- package/esm/ui/App/copyView.js +0 -29
- package/esm/ui/App/dockviewUtils.d.ts +0 -46
- package/esm/ui/App/dockviewUtils.js +0 -55
package/esm/ui/App/AppFab.js
CHANGED
|
@@ -1,26 +1,33 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { FAB_Z_INDEX } from '@jbrowse/core/ui/zIndexes';
|
|
2
3
|
import { makeStyles } from '@jbrowse/core/util/tss-react';
|
|
3
4
|
import LaunchIcon from '@mui/icons-material/Launch';
|
|
4
5
|
import { Fab, Tooltip } from '@mui/material';
|
|
5
6
|
import { observer } from 'mobx-react';
|
|
7
|
+
// `absolute`, against the app root, rather than the `fixed` this used to
|
|
8
|
+
// carry. Fixed measures against the window, and the app root is only the
|
|
9
|
+
// window in a full-window app: under `--jbrowse-app-height` the app is a box
|
|
10
|
+
// somewhere on a host's page (see react-app's fit-to-container example), and
|
|
11
|
+
// this button was pinned to the page's corner instead of the app's. It is the
|
|
12
|
+
// only way back from a minimized drawer, so it cannot be somewhere else on the
|
|
13
|
+
// page.
|
|
6
14
|
const useStyles = makeStyles()(theme => ({
|
|
7
|
-
|
|
8
|
-
zIndex:
|
|
9
|
-
position: '
|
|
15
|
+
fab: {
|
|
16
|
+
zIndex: FAB_Z_INDEX,
|
|
17
|
+
position: 'absolute',
|
|
10
18
|
bottom: theme.spacing(2),
|
|
19
|
+
},
|
|
20
|
+
left: {
|
|
11
21
|
left: theme.spacing(2),
|
|
12
22
|
},
|
|
13
23
|
right: {
|
|
14
|
-
zIndex: 10000,
|
|
15
|
-
position: 'fixed',
|
|
16
|
-
bottom: theme.spacing(2),
|
|
17
24
|
right: theme.spacing(2),
|
|
18
25
|
},
|
|
19
26
|
}));
|
|
20
27
|
const AppFab = observer(function AppFab({ session, }) {
|
|
21
28
|
const { minimized, activeWidgets, drawerPosition } = session;
|
|
22
|
-
const { classes } = useStyles();
|
|
23
|
-
return activeWidgets.size > 0 && minimized ? (_jsx(Tooltip, { title: "Open drawer widget", children: _jsx(Fab, { className: drawerPosition === 'right' ? classes.right : classes.left, color: "primary", "data-testid": "drawer-maximize", onClick: () => {
|
|
29
|
+
const { classes, cx } = useStyles();
|
|
30
|
+
return activeWidgets.size > 0 && minimized ? (_jsx(Tooltip, { title: "Open drawer widget", children: _jsx(Fab, { className: cx(classes.fab, drawerPosition === 'right' ? classes.right : classes.left), color: "primary", "data-testid": "drawer-maximize", onClick: () => {
|
|
24
31
|
session.showWidgetDrawer();
|
|
25
32
|
}, children: _jsx(LaunchIcon, {}) }) })) : null;
|
|
26
33
|
});
|
|
@@ -1,18 +1,6 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type { SnackbarMessage } from '@jbrowse/core/ui/SnackbarModel';
|
|
3
|
-
import type { SessionWithDrawerWidgets } from '@jbrowse/core/util';
|
|
4
|
-
interface Menu {
|
|
5
|
-
label: string;
|
|
6
|
-
menuItems: JBMenuItem[] | (() => JBMenuItem[]);
|
|
7
|
-
}
|
|
8
|
-
type AppSession = SessionWithDrawerWidgets & {
|
|
9
|
-
menus: () => Menu[];
|
|
10
|
-
snackbarMessages: SnackbarMessage[];
|
|
11
|
-
renameCurrentSession: (arg: string) => void;
|
|
12
|
-
popSnackbarMessage: () => unknown;
|
|
13
|
-
};
|
|
1
|
+
import type { AppSession } from './types.ts';
|
|
14
2
|
declare const AppToolbar: ({ session, HeaderButtons, }: {
|
|
15
3
|
HeaderButtons?: React.ReactElement;
|
|
16
4
|
session: AppSession;
|
|
17
|
-
}) => import("react
|
|
5
|
+
}) => import("react").JSX.Element;
|
|
18
6
|
export default AppToolbar;
|
package/esm/ui/App/AppToolbar.js
CHANGED
|
@@ -3,14 +3,24 @@ import AppLogo from '@jbrowse/core/ui/AppLogo';
|
|
|
3
3
|
import DropDownMenu from '@jbrowse/core/ui/DropDownMenu';
|
|
4
4
|
import EditableTypography from '@jbrowse/core/ui/EditableTypography';
|
|
5
5
|
import { makeStyles } from '@jbrowse/core/util/tss-react';
|
|
6
|
-
import { Toolbar, Tooltip } from '@mui/material';
|
|
6
|
+
import { Toolbar, Tooltip, useMediaQuery, useTheme } from '@mui/material';
|
|
7
7
|
import { observer } from 'mobx-react';
|
|
8
|
+
import ThemeModeButton from './ThemeModeButton.js';
|
|
8
9
|
const useStyles = makeStyles()(theme => ({
|
|
9
10
|
grow: {
|
|
10
11
|
flexGrow: 1,
|
|
11
12
|
},
|
|
13
|
+
// a long session name ellipsizes rather than pushing Share off a phone
|
|
14
|
+
sessionName: {
|
|
15
|
+
minWidth: 0,
|
|
16
|
+
},
|
|
12
17
|
inputBase: {
|
|
13
18
|
color: theme.palette.primary.contrastText,
|
|
19
|
+
maxWidth: '100%',
|
|
20
|
+
},
|
|
21
|
+
input: {
|
|
22
|
+
maxWidth: '100%',
|
|
23
|
+
textOverflow: 'ellipsis',
|
|
14
24
|
},
|
|
15
25
|
inputRoot: {
|
|
16
26
|
'&:hover': {
|
|
@@ -21,39 +31,43 @@ const useStyles = makeStyles()(theme => ({
|
|
|
21
31
|
borderColor: theme.palette.secondary.main,
|
|
22
32
|
backgroundColor: theme.palette.primary.light,
|
|
23
33
|
},
|
|
34
|
+
logo: {
|
|
35
|
+
// stretch to the toolbar height so the logo's height follows the bar
|
|
36
|
+
// rather than a hardcoded pixel value
|
|
37
|
+
alignSelf: 'stretch',
|
|
38
|
+
display: 'flex',
|
|
39
|
+
alignItems: 'center',
|
|
40
|
+
// autofit any logo (custom <img> or the default svg): full toolbar height
|
|
41
|
+
// with auto width preserves aspect ratio, maxWidth caps horizontal room
|
|
42
|
+
'& img, & svg': {
|
|
43
|
+
height: '100%',
|
|
44
|
+
width: 'auto',
|
|
45
|
+
maxWidth: 150,
|
|
46
|
+
objectFit: 'contain',
|
|
47
|
+
},
|
|
48
|
+
},
|
|
24
49
|
}));
|
|
25
|
-
function wrapMenuItems(items, session) {
|
|
26
|
-
return items.map(item => ({
|
|
27
|
-
...item,
|
|
28
|
-
...('onClick' in item
|
|
29
|
-
? {
|
|
30
|
-
onClick: () => {
|
|
31
|
-
item.onClick(session);
|
|
32
|
-
},
|
|
33
|
-
}
|
|
34
|
-
: {}),
|
|
35
|
-
...('subMenu' in item
|
|
36
|
-
? { subMenu: wrapMenuItems(item.subMenu, session) }
|
|
37
|
-
: {}),
|
|
38
|
-
}));
|
|
39
|
-
}
|
|
40
50
|
const AppToolbar = observer(function AppToolbar({ session, HeaderButtons = _jsx("div", {}), }) {
|
|
41
51
|
const { classes } = useStyles();
|
|
42
52
|
const { name, menus } = session;
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
53
|
+
// a phone needs the logo's 150px for the menus and Share
|
|
54
|
+
const narrow = useMediaQuery(useTheme().breakpoints.down('sm'), {
|
|
55
|
+
noSsr: true,
|
|
56
|
+
});
|
|
57
|
+
return (_jsxs(Toolbar, { children: [menus().map(menu => (_jsx(DropDownMenu, { menuTitle: menu.label, menuItems: menu.menuItems,
|
|
58
|
+
// a root model's menu items take the session as their argument, and
|
|
59
|
+
// the renderer decides how to invoke onClick — so this is the whole
|
|
60
|
+
// binding. It used to be a recursive rewrite of every row of every
|
|
61
|
+
// menu, rebuilt on each open, to arrive at the same call
|
|
62
|
+
onMenuItemClick: callback => {
|
|
63
|
+
callback(session);
|
|
64
|
+
} }, menu.label))), _jsx("div", { className: classes.grow }), _jsx(Tooltip, { title: "Rename session", arrow: true, children: _jsx("div", { className: classes.sessionName, children: _jsx(EditableTypography, { value: name, variant: "body1", classes: {
|
|
65
|
+
input: classes.input,
|
|
66
|
+
inputBase: classes.inputBase,
|
|
67
|
+
inputRoot: classes.inputRoot,
|
|
68
|
+
inputFocused: classes.inputFocused,
|
|
69
|
+
}, setValue: newName => {
|
|
70
|
+
session.renameCurrentSession(newName);
|
|
71
|
+
} }) }) }), HeaderButtons, _jsx(ThemeModeButton, { session: session }), _jsx("div", { className: classes.grow }), narrow ? null : (_jsx("div", { className: classes.logo, children: _jsx(AppLogo, { session: session }) }))] }));
|
|
58
72
|
});
|
|
59
73
|
export default AppToolbar;
|
|
@@ -1,12 +1,6 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
renameCurrentSession: (arg: string) => void;
|
|
5
|
-
snackbarMessages: SnackbarMessage[];
|
|
6
|
-
popSnackbarMessage: () => unknown;
|
|
7
|
-
};
|
|
8
|
-
interface Props {
|
|
9
|
-
session: SessionType;
|
|
1
|
+
import type { SessionWithFocusedViewAndDrawerWidgets } from '@jbrowse/core/util';
|
|
2
|
+
export interface ClassicViewsContainerProps {
|
|
3
|
+
session: SessionWithFocusedViewAndDrawerWidgets;
|
|
10
4
|
}
|
|
11
|
-
declare const ClassicViewsContainer: ({ session, }:
|
|
5
|
+
declare const ClassicViewsContainer: ({ session, }: ClassicViewsContainerProps) => import("react").JSX.Element;
|
|
12
6
|
export default ClassicViewsContainer;
|
|
@@ -1,21 +1,22 @@
|
|
|
1
|
-
import { jsx as _jsx
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { persistentScrollbarStyle } from '@jbrowse/core/ui/persistentScrollbarStyle';
|
|
3
|
+
import { useScrollPortHeightVar } from '@jbrowse/core/util/hooks';
|
|
2
4
|
import { makeStyles } from '@jbrowse/core/util/tss-react';
|
|
3
5
|
import { observer } from 'mobx-react';
|
|
4
|
-
import
|
|
5
|
-
const useStyles = makeStyles()({
|
|
6
|
+
import ViewStack from './ViewStack.js';
|
|
7
|
+
const useStyles = makeStyles()(theme => ({
|
|
6
8
|
container: {
|
|
7
9
|
height: '100%',
|
|
8
10
|
width: '100%',
|
|
9
11
|
gridRow: 'components',
|
|
10
12
|
overflowY: 'auto',
|
|
13
|
+
...persistentScrollbarStyle(theme),
|
|
11
14
|
},
|
|
12
|
-
|
|
13
|
-
height: 300,
|
|
14
|
-
},
|
|
15
|
-
});
|
|
15
|
+
}));
|
|
16
16
|
const ClassicViewsContainer = observer(function ClassicViewsContainer({ session, }) {
|
|
17
17
|
const { classes } = useStyles();
|
|
18
18
|
const { views } = session;
|
|
19
|
-
|
|
19
|
+
const ref = useScrollPortHeightVar();
|
|
20
|
+
return (_jsx("div", { ref: ref, className: classes.container, children: _jsx(ViewStack, { views: views, session: session }) }));
|
|
20
21
|
});
|
|
21
22
|
export default ClassicViewsContainer;
|
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
import type { AbstractSessionModel } from '@jbrowse/core/util';
|
|
2
|
+
/**
|
|
3
|
+
* The session's dialog, and the boundary that keeps a dialog that throws from
|
|
4
|
+
* being an application-wide failure.
|
|
5
|
+
*
|
|
6
|
+
* `core/ui/Dialog` already wraps its own CHILDREN, which covers a dialog body
|
|
7
|
+
* that throws. It cannot cover the dialog component's own render, and that is
|
|
8
|
+
* where the reachable case is: a launch dialog resolves its source with
|
|
9
|
+
* `getSession` / `getContainingView` off a track, and "Replace current view"
|
|
10
|
+
* destroys the view that track lives in. This queue renders under `App`, above
|
|
11
|
+
* every per-view boundary, so without this the next one up is the product's —
|
|
12
|
+
* jbrowse-web's fatal-error dialog, which takes every view the user had open.
|
|
13
|
+
* ADR-069, and `cancer_sv/multihop_split_view` is the figure it took.
|
|
14
|
+
*
|
|
15
|
+
* Reset on the queue entry, so the queue advancing clears a banner belonging to
|
|
16
|
+
* the dialog before it, including one of the same component.
|
|
17
|
+
*/
|
|
2
18
|
declare const DialogQueue: ({ session, }: {
|
|
3
19
|
session: AbstractSessionModel;
|
|
4
|
-
}) => import("react
|
|
20
|
+
}) => import("react").JSX.Element | null;
|
|
5
21
|
export default DialogQueue;
|
|
@@ -1,8 +1,37 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { Suspense } from 'react';
|
|
3
|
+
import { Dialog, ErrorMessage } from '@jbrowse/core/ui';
|
|
4
|
+
import { ErrorBoundary } from '@jbrowse/core/ui/ErrorBoundary';
|
|
5
|
+
import { DialogContent } from '@mui/material';
|
|
3
6
|
import { observer } from 'mobx-react';
|
|
7
|
+
// `removeActiveDialog` is BaseSession's, and every product session composes it,
|
|
8
|
+
// but `AbstractSessionModel` does not declare it — so it is asked for rather
|
|
9
|
+
// than assumed. A session without it leaves the fallback's close button doing
|
|
10
|
+
// nothing, which is still better than the page it replaces.
|
|
11
|
+
function dismissActiveDialog(session) {
|
|
12
|
+
;
|
|
13
|
+
session.removeActiveDialog?.();
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* The session's dialog, and the boundary that keeps a dialog that throws from
|
|
17
|
+
* being an application-wide failure.
|
|
18
|
+
*
|
|
19
|
+
* `core/ui/Dialog` already wraps its own CHILDREN, which covers a dialog body
|
|
20
|
+
* that throws. It cannot cover the dialog component's own render, and that is
|
|
21
|
+
* where the reachable case is: a launch dialog resolves its source with
|
|
22
|
+
* `getSession` / `getContainingView` off a track, and "Replace current view"
|
|
23
|
+
* destroys the view that track lives in. This queue renders under `App`, above
|
|
24
|
+
* every per-view boundary, so without this the next one up is the product's —
|
|
25
|
+
* jbrowse-web's fatal-error dialog, which takes every view the user had open.
|
|
26
|
+
* ADR-069, and `cancer_sv/multihop_split_view` is the figure it took.
|
|
27
|
+
*
|
|
28
|
+
* Reset on the queue entry, so the queue advancing clears a banner belonging to
|
|
29
|
+
* the dialog before it, including one of the same component.
|
|
30
|
+
*/
|
|
4
31
|
const DialogQueue = observer(function DialogQueue({ session, }) {
|
|
5
32
|
const { DialogComponent, DialogProps } = session;
|
|
6
|
-
return DialogComponent ? (_jsx(
|
|
33
|
+
return DialogComponent ? (_jsx(ErrorBoundary, { resetKeys: [DialogComponent, DialogProps], FallbackComponent: ({ error }) => (_jsx(Dialog, { open: true, title: "Error", onClose: () => {
|
|
34
|
+
dismissActiveDialog(session);
|
|
35
|
+
}, children: _jsx(DialogContent, { children: _jsx(ErrorMessage, { error: error }) }) })), children: _jsx(Suspense, { fallback: null, children: _jsx(DialogComponent, { ...DialogProps }) }) })) : null;
|
|
7
36
|
});
|
|
8
37
|
export default DialogQueue;
|
package/esm/ui/App/Drawer.d.ts
CHANGED
|
@@ -2,5 +2,5 @@ import type { SessionWithFocusedViewAndDrawerWidgets } from '@jbrowse/core/util/
|
|
|
2
2
|
declare const Drawer: ({ children, session, }: {
|
|
3
3
|
children: React.ReactNode;
|
|
4
4
|
session: SessionWithFocusedViewAndDrawerWidgets;
|
|
5
|
-
}) => import("react
|
|
5
|
+
}) => import("react").JSX.Element;
|
|
6
6
|
export default Drawer;
|
package/esm/ui/App/Drawer.js
CHANGED
|
@@ -1,45 +1,16 @@
|
|
|
1
|
-
import { jsx as _jsx
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
5
|
-
import { Paper } from '@mui/material';
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useRef } from 'react';
|
|
3
|
+
import { useFocusOnInteraction } from '@jbrowse/core/util/hooks';
|
|
4
|
+
import { Drawer as DrawerShell } from '@jbrowse/product-core';
|
|
6
5
|
import { observer } from 'mobx-react';
|
|
7
|
-
const useStyles = makeStyles()(theme => ({
|
|
8
|
-
paper: {
|
|
9
|
-
overflowY: 'auto',
|
|
10
|
-
height: '100%',
|
|
11
|
-
position: 'relative',
|
|
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
6
|
const Drawer = observer(function Drawer({ children, session, }) {
|
|
24
|
-
const { drawerPosition, drawerWidth } = session;
|
|
25
|
-
const { classes } = useStyles();
|
|
26
7
|
const ref = useRef(null);
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
if (visibleWidgetId) {
|
|
32
|
-
session.setFocusedViewId(visibleWidgetId);
|
|
33
|
-
}
|
|
34
|
-
}
|
|
8
|
+
useFocusOnInteraction(ref, () => {
|
|
9
|
+
const visibleWidgetId = session.visibleWidget?.view?.id;
|
|
10
|
+
if (visibleWidgetId) {
|
|
11
|
+
session.setFocusedViewId(visibleWidgetId);
|
|
35
12
|
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
return () => {
|
|
39
|
-
document.removeEventListener('mousedown', handleSelectView);
|
|
40
|
-
document.removeEventListener('keydown', handleSelectView);
|
|
41
|
-
};
|
|
42
|
-
}, [session]);
|
|
43
|
-
return (_jsxs(Paper, { ref: ref, className: classes.paper, elevation: 16, square: true, children: [drawerPosition === 'right' ? (_jsx(ResizeHandle, { onDrag: session.resizeDrawer, className: classes.resizeHandle, vertical: true })) : null, children, drawerPosition === 'left' ? (_jsx(ResizeHandle, { onDrag: session.resizeDrawer, className: classes.resizeHandle, style: { left: drawerWidth }, vertical: true })) : null] }));
|
|
13
|
+
});
|
|
14
|
+
return (_jsx(DrawerShell, { session: session, ref: ref, children: children }));
|
|
44
15
|
});
|
|
45
16
|
export default Drawer;
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import type { SessionWithFocusedViewAndDrawerWidgets } from '@jbrowse/core/util/types';
|
|
2
|
-
declare const DrawerHeader: ({ session, setToolbarHeight,
|
|
2
|
+
declare const DrawerHeader: ({ session, setToolbarHeight, }: {
|
|
3
3
|
session: SessionWithFocusedViewAndDrawerWidgets;
|
|
4
4
|
setToolbarHeight: (arg: number) => void;
|
|
5
|
-
|
|
6
|
-
}) => import("react/jsx-runtime").JSX.Element;
|
|
5
|
+
}) => import("react").JSX.Element;
|
|
7
6
|
export default DrawerHeader;
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { Suspense, lazy } from 'react';
|
|
2
|
+
import { Suspense, lazy, useCallback } from 'react';
|
|
3
3
|
import { getEnv } from '@jbrowse/core/util';
|
|
4
4
|
import { makeStyles } from '@jbrowse/core/util/tss-react';
|
|
5
|
+
import { DrawerControls, DrawerWidgetSelector } from '@jbrowse/product-core';
|
|
5
6
|
import LaunchIcon from '@mui/icons-material/Launch';
|
|
6
7
|
import { AppBar, IconButton, Toolbar, Tooltip } from '@mui/material';
|
|
7
8
|
import { observer } from 'mobx-react';
|
|
8
|
-
|
|
9
|
-
import DrawerWidgetSelector from "./DrawerWidgetSelector.js";
|
|
10
|
-
const DrawerHeaderHelpButton = lazy(() => import("./DrawerHeaderHelpButton.js"));
|
|
9
|
+
const DrawerHeaderHelpButton = lazy(() => import('./DrawerHeaderHelpButton.js'));
|
|
11
10
|
const useStyles = makeStyles()(theme => ({
|
|
12
11
|
spacer: {
|
|
13
12
|
flexGrow: 1,
|
|
@@ -19,7 +18,7 @@ const useStyles = makeStyles()(theme => ({
|
|
|
19
18
|
background: theme.palette.secondary.dark,
|
|
20
19
|
},
|
|
21
20
|
}));
|
|
22
|
-
const DrawerHeader = observer(function DrawerHeader({ session, setToolbarHeight,
|
|
21
|
+
const DrawerHeader = observer(function DrawerHeader({ session, setToolbarHeight, }) {
|
|
23
22
|
const { classes } = useStyles();
|
|
24
23
|
const focusedViewId = session.focusedViewId;
|
|
25
24
|
const { visibleWidget } = session;
|
|
@@ -28,13 +27,27 @@ const DrawerHeader = observer(function DrawerHeader({ session, setToolbarHeight,
|
|
|
28
27
|
const widgetType = visibleWidget
|
|
29
28
|
? pluginManager.getWidgetType(visibleWidget.type)
|
|
30
29
|
: undefined;
|
|
31
|
-
const { helpText } = widgetType
|
|
32
|
-
|
|
30
|
+
const { helpText } = widgetType ?? {};
|
|
31
|
+
// Measured, and re-measured: the toolbar's height is the room a virtualized
|
|
32
|
+
// widget subtracts from the box it is given, and it changes without this
|
|
33
|
+
// component re-rendering -- MUI's Toolbar is 56px under the `sm` breakpoint
|
|
34
|
+
// and 64 above it, and grows again with the root font size. Read once at
|
|
35
|
+
// mount, a widget's list was that much too tall for the rest of the session.
|
|
36
|
+
const appBarRef = useCallback((node) => {
|
|
37
|
+
const publish = () => {
|
|
38
|
+
setToolbarHeight(node.getBoundingClientRect().height);
|
|
39
|
+
};
|
|
40
|
+
publish();
|
|
41
|
+
const observer = new ResizeObserver(publish);
|
|
42
|
+
observer.observe(node);
|
|
43
|
+
return () => {
|
|
44
|
+
observer.disconnect();
|
|
45
|
+
};
|
|
46
|
+
}, [setToolbarHeight]);
|
|
47
|
+
return (_jsx(AppBar, { position: "sticky", className: viewWidgetId !== undefined && focusedViewId === viewWidgetId
|
|
33
48
|
? classes.headerFocused
|
|
34
|
-
: classes.headerUnfocused, ref:
|
|
35
|
-
|
|
36
|
-
}, children: _jsxs(Toolbar, { disableGutters: true, children: [_jsx(DrawerWidgetSelector, { session: session }), _jsx(Tooltip, { title: "Open drawer in dialog", children: _jsx(IconButton, { color: "inherit", onClick: () => {
|
|
37
|
-
onPopoutDrawer();
|
|
49
|
+
: classes.headerUnfocused, ref: appBarRef, children: _jsxs(Toolbar, { disableGutters: true, children: [_jsx(DrawerWidgetSelector, { session: session }), _jsx(Tooltip, { title: "Open drawer in dialog", children: _jsx(IconButton, { color: "inherit", "data-testid": "drawer-popout", onClick: () => {
|
|
50
|
+
session.popoutWidget();
|
|
38
51
|
}, children: _jsx(LaunchIcon, {}) }) }), helpText ? (_jsx(Suspense, { fallback: null, children: _jsx(DrawerHeaderHelpButton, { helpText: helpText, session: session }) })) : null, _jsx("div", { className: classes.spacer }), _jsx(DrawerControls, { session: session })] }) }));
|
|
39
52
|
});
|
|
40
53
|
export default DrawerHeader;
|
|
@@ -2,5 +2,5 @@ import type { SessionWithFocusedViewAndDrawerWidgets } from '@jbrowse/core/util/
|
|
|
2
2
|
declare const DrawerHeaderHelpButton: ({ session, helpText, }: {
|
|
3
3
|
session: SessionWithFocusedViewAndDrawerWidgets;
|
|
4
4
|
helpText: React.ReactNode;
|
|
5
|
-
}) => import("react
|
|
5
|
+
}) => import("react").JSX.Element;
|
|
6
6
|
export default DrawerHeaderHelpButton;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { lazy } from 'react';
|
|
3
|
-
import
|
|
3
|
+
import HelpOutlined from '@mui/icons-material/HelpOutlined';
|
|
4
4
|
import { IconButton, Tooltip } from '@mui/material';
|
|
5
5
|
import { observer } from 'mobx-react';
|
|
6
|
-
const DrawerHeaderHelpDialog = lazy(() => import(
|
|
6
|
+
const DrawerHeaderHelpDialog = lazy(() => import('./DrawerHeaderHelpDialog.js'));
|
|
7
7
|
const DrawerHeaderHelpButton = observer(function DrawerHeaderHelpButton({ session, helpText, }) {
|
|
8
8
|
return (_jsx(Tooltip, { title: "Help", children: _jsx(IconButton, { color: "inherit", onClick: () => {
|
|
9
9
|
session.queueDialog(handleClose => [
|
|
@@ -13,6 +13,6 @@ const DrawerHeaderHelpButton = observer(function DrawerHeaderHelpButton({ sessio
|
|
|
13
13
|
helpText,
|
|
14
14
|
},
|
|
15
15
|
]);
|
|
16
|
-
}, children: _jsx(
|
|
16
|
+
}, children: _jsx(HelpOutlined, {}) }) }));
|
|
17
17
|
});
|
|
18
18
|
export default DrawerHeaderHelpButton;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export default function DrawerHeaderHelpDialog({ onClose, helpText, }: {
|
|
2
|
-
onClose: (
|
|
2
|
+
onClose: () => void;
|
|
3
3
|
helpText: React.ReactNode;
|
|
4
|
-
}): import("react
|
|
4
|
+
}): import("react").JSX.Element;
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import
|
|
2
|
+
import InfoDialog from '@jbrowse/core/ui/InfoDialog';
|
|
3
3
|
import { makeStyles } from '@jbrowse/core/util/tss-react';
|
|
4
|
-
import { DialogContent } from '@mui/material';
|
|
5
4
|
const useStyles = makeStyles()({
|
|
6
5
|
max: {
|
|
7
6
|
minWidth: '40em',
|
|
@@ -9,7 +8,7 @@ const useStyles = makeStyles()({
|
|
|
9
8
|
});
|
|
10
9
|
export default function DrawerHeaderHelpDialog({ onClose, helpText, }) {
|
|
11
10
|
const { classes } = useStyles();
|
|
12
|
-
return (_jsx(
|
|
11
|
+
return (_jsx(InfoDialog, { open: true, onClose: onClose, title: "Help", maxWidth: "xl", onClick: e => {
|
|
13
12
|
e.stopPropagation();
|
|
14
|
-
}, children: _jsx(
|
|
13
|
+
}, children: _jsx("div", { className: classes.max, children: helpText }) }));
|
|
15
14
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { SessionWithFocusedViewAndDrawerWidgets } from '@jbrowse/core/util/types';
|
|
2
2
|
declare const DrawerWidget: ({ session, }: {
|
|
3
3
|
session: SessionWithFocusedViewAndDrawerWidgets;
|
|
4
|
-
}) => import("react
|
|
4
|
+
}) => import("react").JSX.Element;
|
|
5
5
|
export default DrawerWidget;
|
|
@@ -1,27 +1,15 @@
|
|
|
1
|
-
import { jsx as _jsx,
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { ErrorBoundary } from '@jbrowse/core/ui/ErrorBoundary';
|
|
5
|
-
import { getEnv } from '@jbrowse/core/util';
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState } from 'react';
|
|
3
|
+
import { WidgetBody } from '@jbrowse/product-core';
|
|
6
4
|
import { observer } from 'mobx-react';
|
|
7
|
-
import Drawer from
|
|
8
|
-
import DrawerHeader from
|
|
9
|
-
const ModalWidget = lazy(() => import("./ModalWidget.js"));
|
|
5
|
+
import Drawer from './Drawer.js';
|
|
6
|
+
import DrawerHeader from './DrawerHeader.js';
|
|
10
7
|
const DrawerWidget = observer(function DrawerWidget({ session, }) {
|
|
11
8
|
const { visibleWidget } = session;
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
session,
|
|
16
|
-
model: visibleWidget,
|
|
17
|
-
})
|
|
18
|
-
: null;
|
|
9
|
+
// we track the toolbar height because components that use virtualized
|
|
10
|
+
// height want to be able to fill the contained, minus the toolbar height
|
|
11
|
+
// (the position static/sticky is included in AutoSizer estimates)
|
|
19
12
|
const [toolbarHeight, setToolbarHeight] = useState(0);
|
|
20
|
-
|
|
21
|
-
return (_jsxs(Drawer, { session: session, children: [_jsx(DrawerHeader, { onPopoutDrawer: () => {
|
|
22
|
-
setPopoutDrawer(true);
|
|
23
|
-
}, session: session, setToolbarHeight: setToolbarHeight }), _jsx(Suspense, { fallback: _jsx(LoadingEllipses, {}), children: _jsx(ErrorBoundary, { FallbackComponent: ({ error }) => _jsx(ErrorMessage, { error: error }), children: DrawerComponent ? (popoutDrawer ? (_jsxs(_Fragment, { children: [_jsx("div", { children: "Opened in dialog..." }), _jsx(ModalWidget, { session: session, onClose: () => {
|
|
24
|
-
setPopoutDrawer(false);
|
|
25
|
-
} })] })) : (_jsxs(_Fragment, { children: [_jsx(DrawerComponent, { model: visibleWidget, session: session, toolbarHeight: toolbarHeight }), _jsx("div", { style: { height: 300 } })] }))) : null }) })] }));
|
|
13
|
+
return (_jsxs(Drawer, { session: session, children: [_jsx(DrawerHeader, { session: session, setToolbarHeight: setToolbarHeight }), visibleWidget ? (_jsx(WidgetBody, { session: session, widget: visibleWidget, toolbarHeight: toolbarHeight })) : null] }));
|
|
26
14
|
});
|
|
27
15
|
export default DrawerWidget;
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
export default function JBrowseTabMenu({
|
|
2
|
-
isHovered: boolean;
|
|
1
|
+
export default function JBrowseTabMenu({ onRename, onClose, }: {
|
|
3
2
|
onRename: () => void;
|
|
4
3
|
onClose: () => void;
|
|
5
|
-
}): import("react
|
|
4
|
+
}): import("react").JSX.Element;
|
|
@@ -1,16 +1,24 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useState } from 'react';
|
|
2
3
|
import CascadingMenuButton from '@jbrowse/core/ui/CascadingMenuButton';
|
|
3
4
|
import { makeStyles } from '@jbrowse/core/util/tss-react';
|
|
4
5
|
import CloseIcon from '@mui/icons-material/Close';
|
|
5
6
|
import EditIcon from '@mui/icons-material/Edit';
|
|
6
7
|
import MoreVertIcon from '@mui/icons-material/MoreVert';
|
|
7
8
|
const useStyles = makeStyles()({
|
|
9
|
+
// hidden until the tab is hovered or focused, by a rule in `TabStrip` keyed
|
|
10
|
+
// on the `jbrowse-tab-menu` class — hence the plain class name beside the
|
|
11
|
+
// generated one. `visibility` rather than `display`, so the tab does not
|
|
12
|
+
// change width under the pointer as it arrives.
|
|
8
13
|
tabIcons: {
|
|
9
14
|
display: 'flex',
|
|
10
15
|
alignItems: 'center',
|
|
11
16
|
visibility: 'hidden',
|
|
12
17
|
},
|
|
13
|
-
|
|
18
|
+
// the pointer is over the menu, not the tab, for as long as the menu is open,
|
|
19
|
+
// so the hover rule stops applying the moment it opens — leaving the popover
|
|
20
|
+
// anchored to an invisible button
|
|
21
|
+
open: {
|
|
14
22
|
visibility: 'visible',
|
|
15
23
|
},
|
|
16
24
|
tabIcon: {
|
|
@@ -22,9 +30,20 @@ const useStyles = makeStyles()({
|
|
|
22
30
|
fontSize: 14,
|
|
23
31
|
},
|
|
24
32
|
});
|
|
25
|
-
export default function JBrowseTabMenu({
|
|
26
|
-
const { classes } = useStyles();
|
|
27
|
-
|
|
33
|
+
export default function JBrowseTabMenu({ onRename, onClose, }) {
|
|
34
|
+
const { classes, cx } = useStyles();
|
|
35
|
+
const [open, setOpen] = useState(false);
|
|
36
|
+
return (_jsx("div", { className: cx('jbrowse-tab-menu', classes.tabIcons, open && classes.open),
|
|
37
|
+
// The tab strip starts a tab drag on pointerdown and takes POINTER
|
|
38
|
+
// CAPTURE for it, and a captured pointer retargets the compatibility
|
|
39
|
+
// mouse events with it — so `click` is delivered to the tab rather than
|
|
40
|
+
// to this button, and the menu silently never opens. Stopping the
|
|
41
|
+
// pointerdown here means a press that starts on the menu is not a drag
|
|
42
|
+
// and never captures, which is the same reason the rename input in
|
|
43
|
+
// `WorkspaceTab` stops it.
|
|
44
|
+
onPointerDown: event => {
|
|
45
|
+
event.stopPropagation();
|
|
46
|
+
}, children: _jsx(CascadingMenuButton, { menuItems: [
|
|
28
47
|
{
|
|
29
48
|
label: 'Rename tab',
|
|
30
49
|
icon: EditIcon,
|
|
@@ -35,5 +54,5 @@ export default function JBrowseTabMenu({ isHovered, onRename, onClose, }) {
|
|
|
35
54
|
icon: CloseIcon,
|
|
36
55
|
onClick: onClose,
|
|
37
56
|
},
|
|
38
|
-
], size: "small", className: classes.tabIcon, stopPropagation: true, children: _jsx(MoreVertIcon, { className: classes.smallIcon }) }) }));
|
|
57
|
+
], size: "small", className: classes.tabIcon, setOpen: setOpen, stopPropagation: true, children: _jsx(MoreVertIcon, { className: classes.smallIcon }) }) }));
|
|
39
58
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { ThemeSwitchSession } from './types.ts';
|
|
2
|
+
/**
|
|
3
|
+
* A light/dark switch for a session that follows the OS, showing the mode
|
|
4
|
+
* drawn. A click holds the other mode, a second click follows the OS again. A
|
|
5
|
+
* session on an explicit light or dark gets nothing, which is every session
|
|
6
|
+
* that has not asked for this in Preferences, so a light reader is offered no
|
|
7
|
+
* route into dark.
|
|
8
|
+
*/
|
|
9
|
+
declare const ThemeModeButton: ({ session, }: {
|
|
10
|
+
session: ThemeSwitchSession;
|
|
11
|
+
}) => import("react").JSX.Element | null;
|
|
12
|
+
export default ThemeModeButton;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import DarkModeIcon from '@mui/icons-material/DarkMode';
|
|
3
|
+
import LightModeIcon from '@mui/icons-material/LightMode';
|
|
4
|
+
import { IconButton, Tooltip } from '@mui/material';
|
|
5
|
+
import { observer } from 'mobx-react';
|
|
6
|
+
/**
|
|
7
|
+
* A light/dark switch for a session that follows the OS, showing the mode
|
|
8
|
+
* drawn. A click holds the other mode, a second click follows the OS again. A
|
|
9
|
+
* session on an explicit light or dark gets nothing, which is every session
|
|
10
|
+
* that has not asked for this in Preferences, so a light reader is offered no
|
|
11
|
+
* route into dark.
|
|
12
|
+
*/
|
|
13
|
+
const ThemeModeButton = observer(function ThemeModeButton({ session, }) {
|
|
14
|
+
const dark = session.themeIsDark;
|
|
15
|
+
const drawnMode = dark ? 'dark' : 'light';
|
|
16
|
+
// A palette pinned to its own mode draws dark whatever the OS says, so the
|
|
17
|
+
// two disagree and the system is steering nothing to report. Everything else
|
|
18
|
+
// that hides this control is `themeMode`.
|
|
19
|
+
const followsSystem = session.themeMode === 'system' && drawnMode === session.effectiveThemeMode;
|
|
20
|
+
if (!followsSystem) {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
const held = session.systemThemeOverride;
|
|
24
|
+
const otherMode = dark ? 'light' : 'dark';
|
|
25
|
+
const Icon = dark ? DarkModeIcon : LightModeIcon;
|
|
26
|
+
const label = held
|
|
27
|
+
? `${dark ? 'Dark' : 'Light'} until your system theme changes`
|
|
28
|
+
: `Following your system theme (${drawnMode})`;
|
|
29
|
+
const action = held ? `to follow it (${otherMode})` : `for ${otherMode}`;
|
|
30
|
+
return (_jsx(Tooltip, { title: `${label}. Click ${action}.`, arrow: true, children: _jsx(IconButton, { "data-testid": "theme-mode-button", "aria-label": label, color: "inherit", size: "small", onClick: () => {
|
|
31
|
+
session.setSystemThemeOverride(held ? undefined : otherMode);
|
|
32
|
+
}, children: _jsx(Icon, { fontSize: "small" }) }) }));
|
|
33
|
+
});
|
|
34
|
+
export default ThemeModeButton;
|