@jbrowse/product-core 4.2.1 → 5.0.0-beta.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/esm/RootModel/BaseRootModel.d.ts +629 -304
- package/esm/RootModel/BaseRootModel.js +138 -14
- package/esm/RootModel/HierarchicalConfig.d.ts +57 -21
- package/esm/RootModel/HierarchicalConfig.js +36 -0
- package/esm/RootModel/InternetAccounts.d.ts +18 -1
- package/esm/RootModel/InternetAccounts.js +120 -22
- package/esm/RootModel/PreferencesConfig.d.ts +53 -0
- package/esm/RootModel/PreferencesConfig.js +61 -0
- package/esm/RootModel/createConfigModel.d.ts +232 -0
- package/esm/RootModel/createConfigModel.js +56 -0
- package/esm/RootModel/index.d.ts +10 -5
- package/esm/RootModel/index.js +10 -5
- package/esm/RootModel/menuItems.d.ts +24 -0
- package/esm/RootModel/menuItems.js +143 -0
- package/esm/Session/BaseSession.d.ts +310 -11
- package/esm/Session/BaseSession.js +448 -14
- package/esm/Session/Connections.d.ts +114 -8
- package/esm/Session/Connections.js +195 -74
- package/esm/Session/DrawerWidgets.d.ts +115 -12
- package/esm/Session/DrawerWidgets.js +161 -42
- package/esm/Session/MultipleViews.d.ts +1266 -1128
- package/esm/Session/MultipleViews.js +357 -66
- package/esm/Session/Preferences.d.ts +1665 -0
- package/esm/Session/Preferences.js +54 -0
- package/esm/Session/ReferenceManagement.d.ts +28 -3
- package/esm/Session/ReferenceManagement.js +56 -41
- package/esm/Session/SessionTracks.d.ts +1275 -1138
- package/esm/Session/SessionTracks.js +532 -35
- package/esm/Session/Themes.d.ts +87 -2
- package/esm/Session/Themes.js +156 -23
- package/esm/Session/TrackMenu.d.ts +112 -0
- package/esm/Session/TrackMenu.js +200 -0
- package/esm/Session/TrackMenuSessionMixin.d.ts +27 -0
- package/esm/Session/TrackMenuSessionMixin.js +41 -0
- package/esm/Session/Tracks.d.ts +1169 -1117
- package/esm/Session/Tracks.js +160 -20
- package/esm/Session/assertSessionModel.d.ts +21 -0
- package/esm/Session/assertSessionModel.js +1 -0
- package/esm/Session/finalizeSession.d.ts +16 -0
- package/esm/Session/finalizeSession.js +29 -0
- package/esm/Session/index.d.ts +22 -10
- package/esm/Session/index.js +13 -10
- package/esm/Session/shareableSnapshot.d.ts +77 -0
- package/esm/Session/shareableSnapshot.js +196 -0
- package/esm/Session/temporaryAssemblyTracks.d.ts +23 -0
- package/esm/Session/temporaryAssemblyTracks.js +38 -0
- package/esm/assertExtends.d.ts +55 -0
- package/esm/assertExtends.js +1 -0
- package/esm/controllerTracks.d.ts +56 -0
- package/esm/controllerTracks.js +77 -0
- package/esm/destroyViewState.d.ts +21 -0
- package/esm/destroyViewState.js +29 -0
- package/esm/index.d.ts +31 -4
- package/esm/index.js +31 -4
- package/esm/loadPlugins.d.ts +33 -0
- package/esm/loadPlugins.js +38 -0
- package/esm/localFiles.d.ts +74 -0
- package/esm/localFiles.js +156 -0
- package/esm/observeSession.d.ts +56 -0
- package/esm/observeSession.js +91 -0
- package/esm/pluginInput.d.ts +32 -0
- package/esm/pluginInput.js +19 -0
- package/esm/pruneUnbuildableNodes.d.ts +109 -0
- package/esm/pruneUnbuildableNodes.js +568 -0
- package/esm/resolveAssemblies.d.ts +44 -0
- package/esm/resolveAssemblies.js +55 -0
- package/esm/rpcWorker.d.ts +26 -0
- package/esm/rpcWorker.js +96 -15
- package/esm/scheduleDetachedDestroy.d.ts +18 -0
- package/esm/scheduleDetachedDestroy.js +24 -0
- package/esm/sessionMigrations/index.d.ts +53 -0
- package/esm/sessionMigrations/index.js +432 -0
- package/esm/sessionUrl.d.ts +41 -0
- package/esm/sessionUrl.js +53 -0
- package/esm/sessionUtils.d.ts +69 -0
- package/esm/sessionUtils.js +613 -0
- package/esm/siblingCast.d.ts +14 -0
- package/esm/siblingCast.js +16 -0
- package/esm/snapshotUtils.d.ts +8 -0
- package/esm/snapshotUtils.js +12 -0
- package/esm/ui/AboutDialog.d.ts +3 -6
- package/esm/ui/AboutDialog.js +4 -4
- package/esm/ui/AboutDialogContents.d.ts +2 -6
- package/esm/ui/AboutDialogContents.js +34 -13
- package/esm/ui/AssemblyInfoPanel.d.ts +5 -0
- package/esm/ui/AssemblyInfoPanel.js +69 -0
- package/esm/ui/DisplayDefaultsSection.d.ts +36 -0
- package/esm/ui/DisplayDefaultsSection.js +65 -0
- package/esm/ui/Drawer.d.ts +21 -0
- package/esm/ui/Drawer.js +60 -0
- package/esm/ui/DrawerControls.d.ts +5 -0
- package/esm/ui/DrawerControls.js +28 -0
- package/esm/ui/DrawerHeader.d.ts +6 -0
- package/esm/ui/DrawerHeader.js +36 -0
- package/esm/ui/DrawerWidget.d.ts +5 -0
- package/esm/ui/DrawerWidget.js +12 -0
- package/esm/ui/DrawerWidgetSelector.d.ts +5 -0
- package/esm/ui/DrawerWidgetSelector.js +40 -0
- package/esm/ui/FileInfoPanel.d.ts +3 -6
- package/esm/ui/FileInfoPanel.js +35 -26
- package/esm/ui/HeaderButtons.d.ts +7 -1
- package/esm/ui/HeaderButtons.js +23 -18
- package/esm/ui/ModalWidget.d.ts +12 -0
- package/esm/ui/ModalWidget.js +33 -0
- package/esm/ui/ModalWidgetAppBar.d.ts +8 -0
- package/esm/ui/ModalWidgetAppBar.js +9 -0
- package/esm/ui/PreferencesDialog.d.ts +56 -0
- package/esm/ui/PreferencesDialog.js +145 -0
- package/esm/ui/PreferencesResetDialog.d.ts +8 -0
- package/esm/ui/PreferencesResetDialog.js +19 -0
- package/esm/ui/RefNameAliasesDialog.d.ts +9 -0
- package/esm/ui/RefNameAliasesDialog.js +65 -0
- package/esm/ui/RefNameInfoDialog.d.ts +3 -6
- package/esm/ui/RefNameInfoDialog.js +57 -63
- package/esm/ui/WidgetBody.d.ts +18 -0
- package/esm/ui/WidgetBody.js +22 -0
- package/esm/ui/WidgetHeading.d.ts +7 -0
- package/esm/ui/WidgetHeading.js +7 -0
- package/esm/ui/drawerLayout.d.ts +18 -0
- package/esm/ui/drawerLayout.js +22 -0
- package/esm/ui/index.d.ts +13 -1
- package/esm/ui/index.js +12 -1
- package/esm/ui/util.d.ts +37 -4
- package/esm/ui/util.js +35 -38
- package/esm/useEngineLifecycle.d.ts +13 -0
- package/esm/useEngineLifecycle.js +22 -0
- package/package.json +11 -15
- package/esm/RootModel/FormatAbout.d.ts +0 -12
- package/esm/RootModel/FormatAbout.js +0 -15
- package/esm/RootModel/FormatDetails.d.ts +0 -24
- package/esm/RootModel/FormatDetails.js +0 -27
- package/esm/Session/DialogQueue.d.ts +0 -17
- package/esm/Session/DialogQueue.js +0 -31
package/esm/ui/HeaderButtons.js
CHANGED
|
@@ -1,26 +1,31 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import {
|
|
2
|
+
import { CopyToClipboardButton } from '@jbrowse/core/ui';
|
|
3
|
+
import { pluralize } from '@jbrowse/core/util';
|
|
4
|
+
import { stripBaseUris } from '@jbrowse/core/util/addRelativeUris';
|
|
3
5
|
import { makeStyles } from '@jbrowse/core/util/tss-react';
|
|
4
|
-
import { Button } from '@mui/material';
|
|
5
|
-
|
|
6
|
-
const useStyles = makeStyles()({
|
|
6
|
+
import { Button, Tooltip, Typography } from '@mui/material';
|
|
7
|
+
const useStyles = makeStyles()(theme => ({
|
|
7
8
|
button: {
|
|
8
9
|
float: 'right',
|
|
9
10
|
},
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
// JSX drops the whitespace between two elements on separate lines, so the two
|
|
12
|
+
// contained buttons rendered edge to edge, reading as one two-tone control
|
|
13
|
+
buttons: {
|
|
14
|
+
display: 'flex',
|
|
15
|
+
gap: theme.spacing(1),
|
|
16
|
+
justifyContent: 'flex-end',
|
|
17
|
+
},
|
|
18
|
+
note: {
|
|
19
|
+
clear: 'both',
|
|
20
|
+
display: 'block',
|
|
21
|
+
textAlign: 'right',
|
|
22
|
+
},
|
|
23
|
+
}));
|
|
24
|
+
function HeaderButtons({ conf, hideUris, fromDisplayTypeDefaults = [], setShowRefNames, }) {
|
|
13
25
|
const { classes } = useStyles();
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
const snap = removeAttr(structuredClone(conf), 'baseUri');
|
|
19
|
-
copy(JSON.stringify(snap, null, 2));
|
|
20
|
-
setCopied(true);
|
|
21
|
-
setTimeout(() => {
|
|
22
|
-
setCopied(false);
|
|
23
|
-
}, 1000);
|
|
24
|
-
}, children: copied ? 'Copied to clipboard!' : 'Copy config' })] }));
|
|
26
|
+
const n = fromDisplayTypeDefaults.length;
|
|
27
|
+
return (_jsxs("span", { className: classes.button, children: [_jsxs("span", { className: classes.buttons, children: [_jsx(Button, { variant: "contained", color: "secondary", onClick: () => {
|
|
28
|
+
setShowRefNames(true);
|
|
29
|
+
}, children: "Show ref names" }), hideUris ? null : (_jsx(CopyToClipboardButton, { variant: "contained", value: () => JSON.stringify(stripBaseUris(structuredClone(conf)), null, 2), children: "Copy config" }))] }), hideUris || !n ? null : (_jsx(Tooltip, { title: fromDisplayTypeDefaults.join(', '), children: _jsxs(Typography, { variant: "caption", color: "textSecondary", className: classes.note, children: ["includes ", n, " ", pluralize(n, 'setting'), " from your session-wide defaults"] }) }))] }));
|
|
25
30
|
}
|
|
26
31
|
export default HeaderButtons;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { SessionWithWidgets } from '@jbrowse/core/util';
|
|
2
|
+
/**
|
|
3
|
+
* Renders the session's visible widget in a modal dialog rather than a drawer.
|
|
4
|
+
* `onClose` is caller-owned: in the app the modal is a pop-out of the drawer and
|
|
5
|
+
* closing returns the widget to it, while in embedded products the modal is the
|
|
6
|
+
* only widget surface and closing must dismiss the widget outright.
|
|
7
|
+
*/
|
|
8
|
+
declare const ModalWidget: ({ session, onClose, }: {
|
|
9
|
+
session: SessionWithWidgets;
|
|
10
|
+
onClose: () => void;
|
|
11
|
+
}) => import("react").JSX.Element | null;
|
|
12
|
+
export default ModalWidget;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Dialog } from '@jbrowse/core/ui';
|
|
3
|
+
import { makeStyles } from '@jbrowse/core/util/tss-react';
|
|
4
|
+
import { getEnv } from '@jbrowse/mobx-state-tree';
|
|
5
|
+
import { Paper } from '@mui/material';
|
|
6
|
+
import { observer } from 'mobx-react';
|
|
7
|
+
import ModalWidgetAppBar from './ModalWidgetAppBar.js';
|
|
8
|
+
import WidgetBody from './WidgetBody.js';
|
|
9
|
+
const useStyles = makeStyles()({
|
|
10
|
+
paper: {
|
|
11
|
+
overflow: 'auto',
|
|
12
|
+
// the track selector's fab positions against this
|
|
13
|
+
position: 'relative',
|
|
14
|
+
width: 800,
|
|
15
|
+
maxWidth: '100%',
|
|
16
|
+
// matches the drawer's sense of "tall but not full screen", and unlike a
|
|
17
|
+
// window.innerHeight read it keeps up with a window resize
|
|
18
|
+
height: '62.5vh',
|
|
19
|
+
},
|
|
20
|
+
});
|
|
21
|
+
/**
|
|
22
|
+
* Renders the session's visible widget in a modal dialog rather than a drawer.
|
|
23
|
+
* `onClose` is caller-owned: in the app the modal is a pop-out of the drawer and
|
|
24
|
+
* closing returns the widget to it, while in embedded products the modal is the
|
|
25
|
+
* only widget surface and closing must dismiss the widget outright.
|
|
26
|
+
*/
|
|
27
|
+
const ModalWidget = observer(function ModalWidget({ session, onClose, }) {
|
|
28
|
+
const { classes } = useStyles();
|
|
29
|
+
const { visibleWidget } = session;
|
|
30
|
+
const { pluginManager } = getEnv(session);
|
|
31
|
+
return visibleWidget ? (_jsx(Dialog, { open: true, onClose: onClose, maxWidth: "xl", header: _jsx(ModalWidgetAppBar, { widget: visibleWidget, pluginManager: pluginManager, onClose: onClose }), children: _jsx(Paper, { className: classes.paper, children: _jsx(WidgetBody, { session: session, widget: visibleWidget }) }) })) : null;
|
|
32
|
+
});
|
|
33
|
+
export default ModalWidget;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type PluginManager from '@jbrowse/core/PluginManager';
|
|
2
|
+
import type { Widget } from '@jbrowse/core/util';
|
|
3
|
+
declare const ModalWidgetAppBar: ({ widget, pluginManager, onClose, }: {
|
|
4
|
+
widget: Widget;
|
|
5
|
+
pluginManager: PluginManager;
|
|
6
|
+
onClose: () => void;
|
|
7
|
+
}) => import("react").JSX.Element;
|
|
8
|
+
export default ModalWidgetAppBar;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import CloseIcon from '@mui/icons-material/Close';
|
|
3
|
+
import { AppBar, Box, IconButton, Toolbar } from '@mui/material';
|
|
4
|
+
import { observer } from 'mobx-react';
|
|
5
|
+
import WidgetHeading from './WidgetHeading.js';
|
|
6
|
+
const ModalWidgetAppBar = observer(function ModalWidgetAppBar({ widget, pluginManager, onClose, }) {
|
|
7
|
+
return (_jsx(AppBar, { position: "static", children: _jsxs(Toolbar, { children: [_jsx(WidgetHeading, { widget: widget, pluginManager: pluginManager }), _jsx(Box, { sx: { flexGrow: 1 } }), _jsx(IconButton, { color: "inherit", onClick: onClose, "data-testid": "modal-close", children: _jsx(CloseIcon, {}) })] }) }));
|
|
8
|
+
});
|
|
9
|
+
export default ModalWidgetAppBar;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type PluginManager from '@jbrowse/core/PluginManager';
|
|
2
|
+
import type { ThemeMap } from '@jbrowse/core/ui';
|
|
3
|
+
import type { AnimationMode, TrackConfigChange } from '@jbrowse/core/util';
|
|
4
|
+
import type React from 'react';
|
|
5
|
+
export interface PreferencesDialogSession {
|
|
6
|
+
allThemes: () => ThemeMap;
|
|
7
|
+
themeName?: string;
|
|
8
|
+
setThemeName: (arg: string) => void;
|
|
9
|
+
stickyViewHeaders: boolean;
|
|
10
|
+
setStickyViewHeaders: (sticky: boolean) => void;
|
|
11
|
+
effectiveUseWorkspaces: boolean;
|
|
12
|
+
defaultUseWorkspaces: boolean;
|
|
13
|
+
setUseWorkspacesPreference: (useWorkspaces: boolean) => void;
|
|
14
|
+
resetUseWorkspaces: () => void;
|
|
15
|
+
animationMode: AnimationMode;
|
|
16
|
+
numberGrouping: boolean;
|
|
17
|
+
scrollZoom: boolean;
|
|
18
|
+
setScrollZoom: (flag: boolean) => void;
|
|
19
|
+
setPreferenceOverride: (key: string, value: unknown) => void;
|
|
20
|
+
clearPreferenceOverrides: () => void;
|
|
21
|
+
getPreferenceChanges: () => TrackConfigChange[];
|
|
22
|
+
resetPreferenceChange: (path: string[]) => void;
|
|
23
|
+
getDisplayTypeDefaults: () => {
|
|
24
|
+
displayType: string;
|
|
25
|
+
slot: string;
|
|
26
|
+
value: unknown;
|
|
27
|
+
}[];
|
|
28
|
+
setDisplayTypeDefault: (displayType: string, slot: string, value: unknown) => void;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Descriptor returned from the `Core-preferencesDialogPanels` extension point.
|
|
32
|
+
* Each panel renders as its own labeled section in the dialog.
|
|
33
|
+
*/
|
|
34
|
+
export interface PreferencesPanelDescriptor {
|
|
35
|
+
name: string;
|
|
36
|
+
Component: React.ComponentType<{
|
|
37
|
+
session: PreferencesDialogSession;
|
|
38
|
+
}>;
|
|
39
|
+
}
|
|
40
|
+
declare module '@jbrowse/core/PluginManager' {
|
|
41
|
+
interface ExtensionPointRegistry {
|
|
42
|
+
'Core-preferencesDialogPanels': {
|
|
43
|
+
args: PreferencesPanelDescriptor[];
|
|
44
|
+
result: PreferencesPanelDescriptor[];
|
|
45
|
+
props: {
|
|
46
|
+
session: PreferencesDialogSession;
|
|
47
|
+
};
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
declare const PreferencesDialog: ({ handleClose, session, pluginManager, }: {
|
|
52
|
+
handleClose: () => void;
|
|
53
|
+
session: PreferencesDialogSession;
|
|
54
|
+
pluginManager: PluginManager;
|
|
55
|
+
}) => React.JSX.Element;
|
|
56
|
+
export default PreferencesDialog;
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Suspense, useState } from 'react';
|
|
3
|
+
import { Dialog, LabeledCheckbox } from '@jbrowse/core/ui';
|
|
4
|
+
import { SCROLL_ZOOM_HELP, SCROLL_ZOOM_LABEL, } from '@jbrowse/core/ui/scrollZoomLabels';
|
|
5
|
+
import { makeStyles } from '@jbrowse/core/util/tss-react';
|
|
6
|
+
import { Button, DialogActions, DialogContent, Divider, FormGroup, MenuItem, TextField, Typography, } from '@mui/material';
|
|
7
|
+
import { observer } from 'mobx-react';
|
|
8
|
+
import DisplayDefaultsSection from './DisplayDefaultsSection.js';
|
|
9
|
+
import PreferencesResetDialog from './PreferencesResetDialog.js';
|
|
10
|
+
const useStyles = makeStyles()({
|
|
11
|
+
container: {
|
|
12
|
+
width: 800,
|
|
13
|
+
},
|
|
14
|
+
panelHeading: {
|
|
15
|
+
marginTop: 16,
|
|
16
|
+
},
|
|
17
|
+
field: {
|
|
18
|
+
marginTop: 16,
|
|
19
|
+
display: 'block',
|
|
20
|
+
},
|
|
21
|
+
});
|
|
22
|
+
const PREFERENCE_SUBSYSTEMS = [
|
|
23
|
+
{
|
|
24
|
+
head: 'theme',
|
|
25
|
+
change: s => s.themeName && s.themeName !== 'default'
|
|
26
|
+
? { path: ['theme'], from: 'default', to: s.themeName }
|
|
27
|
+
: undefined,
|
|
28
|
+
reset: s => {
|
|
29
|
+
s.setThemeName('default');
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
head: 'stickyViewHeaders',
|
|
34
|
+
change: s => s.stickyViewHeaders
|
|
35
|
+
? undefined
|
|
36
|
+
: { path: ['stickyViewHeaders'], from: true, to: false },
|
|
37
|
+
reset: s => {
|
|
38
|
+
s.setStickyViewHeaders(true);
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
head: 'useWorkspaces',
|
|
43
|
+
// the resolved flag against the admin default, not the override map's view
|
|
44
|
+
// of it: a session-scoped value (a spec `layout`, "move view to a tab")
|
|
45
|
+
// writes no override, so the map reported nothing to reset while
|
|
46
|
+
// `resetUseWorkspaces` would plainly have turned workspaces back off —
|
|
47
|
+
// leaving the confirmation empty and its Reset button disabled
|
|
48
|
+
change: s => s.effectiveUseWorkspaces === s.defaultUseWorkspaces
|
|
49
|
+
? undefined
|
|
50
|
+
: {
|
|
51
|
+
path: ['useWorkspaces'],
|
|
52
|
+
from: s.defaultUseWorkspaces,
|
|
53
|
+
to: s.effectiveUseWorkspaces,
|
|
54
|
+
},
|
|
55
|
+
reset: s => {
|
|
56
|
+
s.resetUseWorkspaces();
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
];
|
|
60
|
+
// heads the subsystems above own outright: their row states the resolved value,
|
|
61
|
+
// so the session's override-map row for the same key would double-report it
|
|
62
|
+
// (and disagree with it whenever the two layers differ)
|
|
63
|
+
const SUBSYSTEM_HEADS = new Set(PREFERENCE_SUBSYSTEMS.map(p => p.head));
|
|
64
|
+
// every preference that currently differs from its default, so the confirmation
|
|
65
|
+
// dialog shows the full effect of a reset: the session override map plus each
|
|
66
|
+
// non-map subsystem above.
|
|
67
|
+
function collectPreferenceChanges(session) {
|
|
68
|
+
return [
|
|
69
|
+
...session
|
|
70
|
+
.getPreferenceChanges()
|
|
71
|
+
.filter(c => !SUBSYSTEM_HEADS.has(c.path[0])),
|
|
72
|
+
...PREFERENCE_SUBSYSTEMS.map(p => p.change(session)).filter(c => c !== undefined),
|
|
73
|
+
];
|
|
74
|
+
}
|
|
75
|
+
// Reset every preference this dialog exposes back to its default: clear the
|
|
76
|
+
// whole override map (scrollZoom, animationMode, all promoted display-type
|
|
77
|
+
// defaults) at once, then reset each non-map subsystem through its own setter.
|
|
78
|
+
function resetAllPreferences(session) {
|
|
79
|
+
session.clearPreferenceOverrides();
|
|
80
|
+
for (const p of PREFERENCE_SUBSYSTEMS) {
|
|
81
|
+
p.reset(session);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
// Revert a single change row (see `collectPreferenceChanges`) to its default:
|
|
85
|
+
// a non-map subsystem by matching `head`, everything else back through the
|
|
86
|
+
// session that produced the row. The override map's own path shapes are the
|
|
87
|
+
// session's business — it owns the composite-key layout a promoted
|
|
88
|
+
// display-type default is stored under, so it also owns undoing one.
|
|
89
|
+
function resetPreferenceChange(session, change) {
|
|
90
|
+
const subsystem = PREFERENCE_SUBSYSTEMS.find(p => p.head === change.path[0]);
|
|
91
|
+
if (subsystem) {
|
|
92
|
+
subsystem.reset(session);
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
session.resetPreferenceChange(change.path);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
// declarative user-preference rows backed by the session preferences-override
|
|
99
|
+
// system (BaseSession getPreference/setPreferenceOverride). Add a row here to
|
|
100
|
+
// surface a new preference; resolution + persistence are already handled.
|
|
101
|
+
const PREFERENCE_SELECTS = [
|
|
102
|
+
{
|
|
103
|
+
key: 'animationMode',
|
|
104
|
+
label: 'Animations',
|
|
105
|
+
options: [
|
|
106
|
+
{ value: 'system', label: 'Follow system (reduced motion)' },
|
|
107
|
+
{ value: 'enabled', label: 'Always on' },
|
|
108
|
+
{ value: 'disabled', label: 'Off' },
|
|
109
|
+
],
|
|
110
|
+
get: session => session.animationMode,
|
|
111
|
+
},
|
|
112
|
+
];
|
|
113
|
+
const PreferencesDialog = observer(function PreferencesDialog({ handleClose, session, pluginManager, }) {
|
|
114
|
+
const { classes } = useStyles();
|
|
115
|
+
// a confirmation dialog (showing the exact diff) guards the destructive reset,
|
|
116
|
+
// instead of an accidental single click wiping every preference
|
|
117
|
+
const [resetDialogOpen, setResetDialogOpen] = useState(false);
|
|
118
|
+
const extraPanels = pluginManager.evaluateExtensionPoint(
|
|
119
|
+
/** #extensionPoint Core-preferencesDialogPanels | sync | Add panels to the preferences dialog */
|
|
120
|
+
'Core-preferencesDialogPanels', [], { session });
|
|
121
|
+
return (_jsxs(Dialog, { title: "Preferences", open: true, onClose: handleClose, maxWidth: "xl", children: [_jsxs(DialogContent, { className: classes.container, children: [_jsx(TextField, { select: true, variant: "outlined", label: "Theme", value: session.themeName, onChange: event => {
|
|
122
|
+
session.setThemeName(event.target.value);
|
|
123
|
+
}, children: Object.entries(session.allThemes()).map(([key, val]) => (_jsx(MenuItem, { value: key, children: val.name || '(Unknown name)' }, key))) }), PREFERENCE_SELECTS.map(row => (_jsx(TextField, { select: true, variant: "outlined", className: classes.field, label: row.label, value: row.get(session), onChange: event => {
|
|
124
|
+
session.setPreferenceOverride(row.key, event.target.value);
|
|
125
|
+
}, children: row.options.map(opt => (_jsx(MenuItem, { value: opt.value, children: opt.label }, opt.value))) }, row.key))), _jsxs(FormGroup, { children: [_jsx(LabeledCheckbox, { checked: session.scrollZoom, label: `${SCROLL_ZOOM_LABEL}: ${SCROLL_ZOOM_HELP}`, onChange: checked => {
|
|
126
|
+
session.setScrollZoom(checked);
|
|
127
|
+
} }), _jsx(LabeledCheckbox, { checked: session.stickyViewHeaders, label: "Keep view header visible", onChange: checked => {
|
|
128
|
+
session.setStickyViewHeaders(checked);
|
|
129
|
+
} }), _jsx(LabeledCheckbox, { checked: session.effectiveUseWorkspaces, label: "Use workspaces (tabbed/tiled view layout)", onChange: checked => {
|
|
130
|
+
session.setUseWorkspacesPreference(checked);
|
|
131
|
+
} }), _jsx(LabeledCheckbox, { checked: session.numberGrouping, label: "Show thousand separators in numbers, e.g. chr1:1,234,567 vs chr1:1234567 (reload app/refresh browser page to apply)", onChange: checked => {
|
|
132
|
+
session.setPreferenceOverride('numberGrouping', checked);
|
|
133
|
+
} })] }), _jsx(Divider, {}), _jsx(Typography, { variant: "subtitle1", className: classes.panelHeading, children: "Display defaults" }), _jsx(DisplayDefaultsSection, { session: session, pluginManager: pluginManager }), extraPanels.map(({ name, Component }) => (_jsxs("div", { children: [_jsx(Divider, {}), _jsx(Typography, { variant: "subtitle1", className: classes.panelHeading, children: name }), _jsx(Suspense, { fallback: null, children: _jsx(Component, { session: session }) })] }, name)))] }), _jsxs(DialogActions, { children: [_jsx(Button, { variant: "contained", color: "secondary", onClick: () => {
|
|
134
|
+
setResetDialogOpen(true);
|
|
135
|
+
}, children: "Reset to defaults\u2026" }), _jsx(Button, { variant: "contained", onClick: () => {
|
|
136
|
+
handleClose();
|
|
137
|
+
}, children: "Close" })] }), resetDialogOpen ? (_jsx(PreferencesResetDialog, { changes: collectPreferenceChanges(session), onReset: () => {
|
|
138
|
+
resetAllPreferences(session);
|
|
139
|
+
}, onResetRow: change => {
|
|
140
|
+
resetPreferenceChange(session, change);
|
|
141
|
+
}, onClose: () => {
|
|
142
|
+
setResetDialogOpen(false);
|
|
143
|
+
} })) : null] }));
|
|
144
|
+
});
|
|
145
|
+
export default PreferencesDialog;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { TrackConfigChange } from '@jbrowse/core/util';
|
|
2
|
+
declare const PreferencesResetDialog: ({ changes, onReset, onResetRow, onClose, }: {
|
|
3
|
+
changes: TrackConfigChange[];
|
|
4
|
+
onReset: () => void;
|
|
5
|
+
onResetRow: (change: TrackConfigChange) => void;
|
|
6
|
+
onClose: () => void;
|
|
7
|
+
}) => import("react").JSX.Element;
|
|
8
|
+
export default PreferencesResetDialog;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { SettingsChangesTable, SubmitDialog } from '@jbrowse/core/ui';
|
|
3
|
+
import { DialogContentText, Typography } from '@mui/material';
|
|
4
|
+
import { observer } from 'mobx-react';
|
|
5
|
+
// Confirmation shown before "Reset to defaults" wipes every customized
|
|
6
|
+
// preference. Lists exactly what will change as a Setting/Default/Current diff
|
|
7
|
+
// (the same table the per-track reset uses) so the reset is never a blind
|
|
8
|
+
// action. A standard modal — focus-trapped, Enter to confirm, Escape to cancel
|
|
9
|
+
// — replaces the former label-swapping two-click button, which screen readers
|
|
10
|
+
// and keyboard users could not follow.
|
|
11
|
+
const PreferencesResetDialog = observer(function PreferencesResetDialog({ changes, onReset, onResetRow, onClose, }) {
|
|
12
|
+
return (_jsx(SubmitDialog, { open: true, title: "Reset preferences to defaults", maxWidth: "md", submitText: "Reset to defaults", submitDisabled: changes.length === 0, cancelText: "Cancel", onCancel: () => {
|
|
13
|
+
onClose();
|
|
14
|
+
}, onSubmit: () => {
|
|
15
|
+
onReset();
|
|
16
|
+
onClose();
|
|
17
|
+
}, children: changes.length ? (_jsxs(_Fragment, { children: [_jsx(DialogContentText, { children: "These preferences differ from their defaults. Reset an individual entry with its revert button, or reset them all below:" }), _jsx(SettingsChangesTable, { changes: changes, onResetRow: onResetRow })] })) : (_jsx(Typography, { children: "All preferences are already at their defaults." })) }));
|
|
18
|
+
});
|
|
19
|
+
export default PreferencesResetDialog;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { AbstractSessionModel } from '@jbrowse/core/util';
|
|
2
|
+
export declare function formatRows(rows: [string, string[]][]): string;
|
|
3
|
+
export declare function alignRows(rows: [string, string[]][]): string;
|
|
4
|
+
declare const RefNameAliasesDialog: ({ assemblyName, session, onClose, }: {
|
|
5
|
+
assemblyName: string;
|
|
6
|
+
session: AbstractSessionModel;
|
|
7
|
+
onClose: () => void;
|
|
8
|
+
}) => import("react").JSX.Element;
|
|
9
|
+
export default RefNameAliasesDialog;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState } from 'react';
|
|
3
|
+
import { CopyToClipboardButton, Dialog, ErrorBanner, LoadingEllipses, } from '@jbrowse/core/ui';
|
|
4
|
+
import { makeStyles } from '@jbrowse/core/util/tss-react';
|
|
5
|
+
import { DialogContent, TextField, Typography } from '@mui/material';
|
|
6
|
+
import { observer } from 'mobx-react';
|
|
7
|
+
const MAX_ROWS = 10_000;
|
|
8
|
+
const MAX_NAME_COLUMN = 30;
|
|
9
|
+
const useStyles = makeStyles()(theme => ({
|
|
10
|
+
container: {
|
|
11
|
+
minWidth: 800,
|
|
12
|
+
},
|
|
13
|
+
rows: {
|
|
14
|
+
maxHeight: 300,
|
|
15
|
+
overflow: 'auto',
|
|
16
|
+
background: theme.palette.background.default,
|
|
17
|
+
},
|
|
18
|
+
header: {
|
|
19
|
+
display: 'flex',
|
|
20
|
+
alignItems: 'center',
|
|
21
|
+
gap: theme.spacing(2),
|
|
22
|
+
marginBottom: theme.spacing(1),
|
|
23
|
+
},
|
|
24
|
+
}));
|
|
25
|
+
// what the copy button puts on the clipboard: tab-separated, so it pastes into
|
|
26
|
+
// a spreadsheet as two columns
|
|
27
|
+
export function formatRows(rows) {
|
|
28
|
+
return rows
|
|
29
|
+
.map(([refName, names]) => `${refName}\t${names.slice(1).join(', ')}`)
|
|
30
|
+
.join('\n');
|
|
31
|
+
}
|
|
32
|
+
// what the dialog shows. A tab is 8 columns wide in a <pre>, so an assembly
|
|
33
|
+
// whose names straddle that (hg38's `chr7` and `chr7_GL383534v2_alt`) reads as
|
|
34
|
+
// two ragged columns; padding to the widest name on screen is one. Capped, or
|
|
35
|
+
// a single 60-character GenArk contig indents every alias off the right edge
|
|
36
|
+
export function alignRows(rows) {
|
|
37
|
+
let width = 0;
|
|
38
|
+
for (const [refName] of rows) {
|
|
39
|
+
width = Math.max(width, refName.length);
|
|
40
|
+
}
|
|
41
|
+
const pad = Math.min(width, MAX_NAME_COLUMN);
|
|
42
|
+
return rows
|
|
43
|
+
.map(([refName, names]) => `${refName.padEnd(pad)} ${names.slice(1).join(', ')}`)
|
|
44
|
+
.join('\n');
|
|
45
|
+
}
|
|
46
|
+
const RefNameAliasesDialog = observer(function RefNameAliasesDialog({ assemblyName, session, onClose, }) {
|
|
47
|
+
const { classes } = useStyles();
|
|
48
|
+
const [filter, setFilter] = useState('');
|
|
49
|
+
const assembly = session.assemblyManager.get(assemblyName);
|
|
50
|
+
// reading this is also what starts the load: assembly.afterAttach kicks
|
|
51
|
+
// load() when refNameAliases is first observed, so the dialog fills in for an
|
|
52
|
+
// assembly nothing has looked at yet instead of showing an empty table
|
|
53
|
+
const names = assembly?.namesByCanonicalRefName;
|
|
54
|
+
const query = filter.toLowerCase();
|
|
55
|
+
const rows = names
|
|
56
|
+
? [...names].filter(([, group]) => group.some(name => name.toLowerCase().includes(query)))
|
|
57
|
+
: [];
|
|
58
|
+
const truncated = rows.length > MAX_ROWS;
|
|
59
|
+
return (_jsx(Dialog, { open: true, title: `Reference name aliases for ${assemblyName}`, onClose: () => {
|
|
60
|
+
onClose();
|
|
61
|
+
}, maxWidth: "xl", children: _jsx(DialogContent, { className: classes.container, children: assembly?.error ? (_jsx(ErrorBanner, { error: assembly.error })) : !assembly ? (_jsxs(Typography, { children: ["No assembly named ", assemblyName, " is loaded in this session"] })) : !names ? (_jsx(LoadingEllipses, { message: "Loading assembly" })) : (_jsxs(_Fragment, { children: [_jsxs("div", { className: classes.header, children: [_jsx(TextField, { label: "Filter", helperText: "Matches a reference name or any of its aliases", value: filter, onChange: event => {
|
|
62
|
+
setFilter(event.target.value);
|
|
63
|
+
} }), _jsx(CopyToClipboardButton, { variant: "contained", value: () => formatRows(rows), children: "Copy ref name aliases" })] }), _jsx("pre", { className: classes.rows, children: alignRows(truncated ? rows.slice(0, MAX_ROWS) : rows) }), truncated ? (_jsxs(Typography, { variant: "caption", children: ["Showing the first ", MAX_ROWS, " of ", rows.length, " reference names; filter, or use the copy button, for the rest"] })) : null] })) }) }));
|
|
64
|
+
});
|
|
65
|
+
export default RefNameAliasesDialog;
|
|
@@ -1,8 +1,5 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
3
|
-
declare const RefNameInfoDialog: ({ config, session, onClose, }: {
|
|
4
|
-
config: AnyConfigurationModel | Record<string, unknown>;
|
|
5
|
-
session: AbstractSessionModel;
|
|
1
|
+
import type { AboutPanelProps } from './util.ts';
|
|
2
|
+
declare const RefNameInfoDialog: ({ config, session, onClose, }: AboutPanelProps & {
|
|
6
3
|
onClose: () => void;
|
|
7
|
-
}) => import("react
|
|
4
|
+
}) => import("react").JSX.Element;
|
|
8
5
|
export default RefNameInfoDialog;
|
|
@@ -1,10 +1,13 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { Dialog,
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { readConfSlot } from '@jbrowse/core/configuration';
|
|
3
|
+
import { CopyToClipboardButton, Dialog, ErrorBanner, LoadingEllipses, } from '@jbrowse/core/ui';
|
|
4
|
+
import { createStatusFanOut, statusProgressLabel } from '@jbrowse/core/util';
|
|
5
|
+
import { getConfAssemblyNames } from '@jbrowse/core/util/tracks';
|
|
4
6
|
import { makeStyles } from '@jbrowse/core/util/tss-react';
|
|
5
|
-
import {
|
|
7
|
+
import { useFetch } from '@jbrowse/core/util/useFetch';
|
|
8
|
+
import { isStateTreeNode } from '@jbrowse/mobx-state-tree';
|
|
9
|
+
import { DialogContent } from '@mui/material';
|
|
6
10
|
import { observer } from 'mobx-react';
|
|
7
|
-
import { readConf } from "./util.js";
|
|
8
11
|
const MAX_REF_NAMES = 10_000;
|
|
9
12
|
const useStyles = makeStyles()(theme => ({
|
|
10
13
|
container: {
|
|
@@ -17,66 +20,57 @@ const useStyles = makeStyles()(theme => ({
|
|
|
17
20
|
background: theme.palette.background.default,
|
|
18
21
|
},
|
|
19
22
|
}));
|
|
23
|
+
function formatRefNames(data, truncate) {
|
|
24
|
+
return data
|
|
25
|
+
.flatMap(([assemblyName, names]) => [
|
|
26
|
+
`--- ${assemblyName} ---`,
|
|
27
|
+
...(truncate ? names.slice(0, MAX_REF_NAMES) : names),
|
|
28
|
+
truncate && names.length > MAX_REF_NAMES
|
|
29
|
+
? `\nToo many refNames to show in browser for ${assemblyName}, use "Copy ref names" button to copy to clipboard`
|
|
30
|
+
: '',
|
|
31
|
+
])
|
|
32
|
+
.filter(s => s !== '')
|
|
33
|
+
.join('\n');
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* A `ReferenceSequenceTrack` config declares no `assemblyNames` slot at all —
|
|
37
|
+
* `createReferenceSeqTrackConfig` omits it deliberately, because such a track's
|
|
38
|
+
* assembly is the config node holding it. `getConfAssemblyNames` is the shared
|
|
39
|
+
* resolver that walks to that parent, and reading the slot directly instead is
|
|
40
|
+
* what left "Show ref names" on every reference sequence track loading forever:
|
|
41
|
+
* `undefined` serialized into the fetch key, which `useFetch` reads as "don't
|
|
42
|
+
* fetch". It throws when a config has neither, which is why this is called
|
|
43
|
+
* inside the fetcher — an unanswerable question belongs in the error banner
|
|
44
|
+
* rather than thrown out of a render.
|
|
45
|
+
*/
|
|
46
|
+
function aboutAssemblyNames(config) {
|
|
47
|
+
return isStateTreeNode(config)
|
|
48
|
+
? getConfAssemblyNames(config)
|
|
49
|
+
: (readConfSlot(config, 'assemblyNames') ?? []);
|
|
50
|
+
}
|
|
20
51
|
const RefNameInfoDialog = observer(function RefNameInfoDialog({ config, session, onClose, }) {
|
|
21
52
|
const { classes } = useStyles();
|
|
22
|
-
const [error, setError] = useState();
|
|
23
|
-
const [refNames, setRefNames] = useState();
|
|
24
|
-
const [copied, setCopied] = useState(false);
|
|
25
53
|
const { rpcManager } = session;
|
|
26
|
-
const trackId =
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
}
|
|
48
|
-
catch (e) {
|
|
49
|
-
console.error(e);
|
|
50
|
-
setError(e);
|
|
51
|
-
}
|
|
52
|
-
})();
|
|
53
|
-
}, [config, rpcManager, trackId, assemblyNames]);
|
|
54
|
-
const names = refNames ? Object.entries(refNames) : [];
|
|
55
|
-
const result = names
|
|
56
|
-
.flatMap(([assemblyName, refNames]) => {
|
|
57
|
-
return [
|
|
58
|
-
`--- ${assemblyName} ---`,
|
|
59
|
-
...refNames.slice(0, MAX_REF_NAMES),
|
|
60
|
-
refNames.length > MAX_REF_NAMES
|
|
61
|
-
? `\nToo many refNames to show in browser for ${assemblyName}, use "Copy ref names" button to copy to clipboard`
|
|
62
|
-
: '',
|
|
63
|
-
];
|
|
64
|
-
})
|
|
65
|
-
.filter(f => !!f)
|
|
66
|
-
.join('\n');
|
|
67
|
-
return (_jsx(Dialog, { open: true, title: "Reference sequence names used in track", onClose: onClose, maxWidth: "xl", children: _jsx(DialogContent, { className: classes.container, children: error ? (_jsx(ErrorMessage, { error: error })) : refNames === undefined ? (_jsx(LoadingEllipses, { message: "Loading refNames" })) : (_jsxs(_Fragment, { children: [_jsx(Button, { variant: "contained", onClick: async () => {
|
|
68
|
-
const { default: copy } = await import('copy-to-clipboard');
|
|
69
|
-
copy(names
|
|
70
|
-
.flatMap(([assemblyName, refNames]) => [
|
|
71
|
-
`--- ${assemblyName} ---`,
|
|
72
|
-
...refNames,
|
|
73
|
-
])
|
|
74
|
-
.filter(f => !!f)
|
|
75
|
-
.join('\n'));
|
|
76
|
-
setCopied(true);
|
|
77
|
-
setTimeout(() => {
|
|
78
|
-
setCopied(false);
|
|
79
|
-
}, 1000);
|
|
80
|
-
}, children: copied ? 'Copied to clipboard!' : 'Copy ref names' }), _jsx("pre", { className: classes.refNames, children: result })] })) }) }));
|
|
54
|
+
const trackId = readConfSlot(config, 'trackId');
|
|
55
|
+
const { data, error, isLoading, status } = useFetch(['CoreGetRefNames', trackId], (_name, _trackId, stopToken, statusCallback) => {
|
|
56
|
+
// one status slot per assembly, so N concurrent reads aggregate into one
|
|
57
|
+
// bar instead of the last writer winning
|
|
58
|
+
const slot = createStatusFanOut(statusCallback);
|
|
59
|
+
return Promise.all([...new Set(aboutAssemblyNames(config))].map(async (assemblyName) => [
|
|
60
|
+
assemblyName,
|
|
61
|
+
await rpcManager.call(trackId, 'CoreGetRefNames', {
|
|
62
|
+
adapterConfig: readConfSlot(config, 'adapter'),
|
|
63
|
+
assemblyName,
|
|
64
|
+
stopToken,
|
|
65
|
+
statusCallback: slot(),
|
|
66
|
+
}),
|
|
67
|
+
]));
|
|
68
|
+
});
|
|
69
|
+
// undefined here means the key was incomplete and the fetch never ran, not
|
|
70
|
+
// that one is still in flight — `isLoading` is what says that. Treating the
|
|
71
|
+
// two as the same thing is what left the dialog spinning with nothing behind
|
|
72
|
+
// it; an empty list at least says so
|
|
73
|
+
const refNames = data ?? [];
|
|
74
|
+
return (_jsx(Dialog, { open: true, title: "Reference sequence names used in track", onClose: onClose, maxWidth: "xl", children: _jsx(DialogContent, { className: classes.container, children: error ? (_jsx(ErrorBanner, { error: error })) : isLoading ? (_jsx(LoadingEllipses, { message: statusProgressLabel(status) || 'Loading refNames' })) : (_jsxs(_Fragment, { children: [_jsx(CopyToClipboardButton, { variant: "contained", value: () => formatRefNames(refNames, false), children: "Copy ref names" }), _jsx("pre", { className: classes.refNames, children: formatRefNames(refNames, true) })] })) }) }));
|
|
81
75
|
});
|
|
82
76
|
export default RefNameInfoDialog;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { SessionWithWidgets, Widget } from '@jbrowse/core/util';
|
|
2
|
+
/**
|
|
3
|
+
* Renders one widget: resolve its React component, run it through the
|
|
4
|
+
* `Core-replaceWidget` fold, and contain both its lazy chunk and its errors.
|
|
5
|
+
*
|
|
6
|
+
* Only the chrome around a widget differs between surfaces — the drawer's
|
|
7
|
+
* resizable paper and header, the modal's dialog — so this is the part all of
|
|
8
|
+
* them share, spelled once. The error boundary in particular was previously
|
|
9
|
+
* only on the drawer path: popping the same widget out to a modal turned a
|
|
10
|
+
* widget that throws during render from a banner in the widget's own area into
|
|
11
|
+
* an unhandled throw that took the app down with it.
|
|
12
|
+
*/
|
|
13
|
+
declare const WidgetBody: ({ session, widget, toolbarHeight, }: {
|
|
14
|
+
session: SessionWithWidgets;
|
|
15
|
+
widget: Widget;
|
|
16
|
+
toolbarHeight?: number;
|
|
17
|
+
}) => import("react").JSX.Element;
|
|
18
|
+
export default WidgetBody;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Suspense } from 'react';
|
|
3
|
+
import { ErrorBanner, LoadingEllipses, PluggableComponent, } from '@jbrowse/core/ui';
|
|
4
|
+
import { ErrorBoundary } from '@jbrowse/core/ui/ErrorBoundary';
|
|
5
|
+
import { getEnv } from '@jbrowse/core/util';
|
|
6
|
+
import { observer } from 'mobx-react';
|
|
7
|
+
/**
|
|
8
|
+
* Renders one widget: resolve its React component, run it through the
|
|
9
|
+
* `Core-replaceWidget` fold, and contain both its lazy chunk and its errors.
|
|
10
|
+
*
|
|
11
|
+
* Only the chrome around a widget differs between surfaces — the drawer's
|
|
12
|
+
* resizable paper and header, the modal's dialog — so this is the part all of
|
|
13
|
+
* them share, spelled once. The error boundary in particular was previously
|
|
14
|
+
* only on the drawer path: popping the same widget out to a modal turned a
|
|
15
|
+
* widget that throws during render from a banner in the widget's own area into
|
|
16
|
+
* an unhandled throw that took the app down with it.
|
|
17
|
+
*/
|
|
18
|
+
const WidgetBody = observer(function WidgetBody({ session, widget, toolbarHeight = 0, }) {
|
|
19
|
+
const { pluginManager } = getEnv(session);
|
|
20
|
+
return (_jsx(Suspense, { fallback: _jsx(LoadingEllipses, {}), children: _jsx(ErrorBoundary, { FallbackComponent: ({ error }) => _jsx(ErrorBanner, { error: error }), children: _jsx(PluggableComponent, { pluginManager: pluginManager, name: "Core-replaceWidget", component: pluginManager.getWidgetType(widget.type).ReactComponent, props: { model: widget, session, toolbarHeight } }) }) }));
|
|
21
|
+
});
|
|
22
|
+
export default WidgetBody;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type PluginManager from '@jbrowse/core/PluginManager';
|
|
2
|
+
import type { Widget } from '@jbrowse/core/util';
|
|
3
|
+
declare function WidgetHeading({ widget, pluginManager, }: {
|
|
4
|
+
widget: Widget;
|
|
5
|
+
pluginManager: PluginManager;
|
|
6
|
+
}): import("react").JSX.Element;
|
|
7
|
+
export default WidgetHeading;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Typography } from '@mui/material';
|
|
3
|
+
function WidgetHeading({ widget, pluginManager, }) {
|
|
4
|
+
const { HeadingComponent, heading } = pluginManager.getWidgetType(widget.type);
|
|
5
|
+
return HeadingComponent ? (_jsx(HeadingComponent, { model: widget })) : (_jsx(Typography, { variant: "h6", color: "inherit", children: heading }));
|
|
6
|
+
}
|
|
7
|
+
export default WidgetHeading;
|