@jbrowse/embedded-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.
@@ -0,0 +1,126 @@
1
+ import TextSearchManager from '@jbrowse/core/TextSearch/TextSearchManager';
2
+ import assemblyManagerFactory, { assemblyConfigSchemaFactory, } from '@jbrowse/core/assemblyManager';
3
+ import RpcManager from '@jbrowse/core/rpc/RpcManager';
4
+ import { cast, types } from '@jbrowse/mobx-state-tree';
5
+ import { InternetAccountsRootModelMixin, createConfigModel, migrateSessionSnapshot, } from '@jbrowse/product-core';
6
+ /**
7
+ * A session an embedder hands over may be a v4 one: a share link, a stored
8
+ * snapshot, a `?session=` param a reader still holds. It names display types
9
+ * this build retired and carries settings that are config slots now, which the
10
+ * session type refuses outright — so the whole engine fails to build rather
11
+ * than one track opening at its defaults. ADR-168.
12
+ *
13
+ * An app root runs this once, in `setSession`, because that is its only door.
14
+ * An embedded root has three: the `session` prop at create, and the two actions
15
+ * that assign over it afterwards.
16
+ */
17
+ function migrated(snapshot, pluginManager) {
18
+ return snapshot && typeof snapshot === 'object'
19
+ ? migrateSessionSnapshot(snapshot, pluginManager)
20
+ : snapshot;
21
+ }
22
+ /**
23
+ * #stateModel EmbeddedRootModel
24
+ * #category root
25
+ * Root model shared by the single-view embedded products
26
+ * (react-linear-genome-view, react-circular-genome-view). Each product supplies
27
+ * its own model name, version, and session model, and may `.props()` on extra
28
+ * fields (e.g. the LGV `disableAddTracks`/`drawerViewHeight`). Internet accounts
29
+ * come from the same product-core mixin the web/desktop root models use, so
30
+ * config `internetAccounts` are auto-initialized (no manual wiring needed).
31
+ */
32
+ export function createEmbeddedRootModel({ name, version, pluginManager, sessionModelType, makeWorkerInstance, }) {
33
+ const assemblyConfigSchema = assemblyConfigSchemaFactory(pluginManager);
34
+ return types
35
+ .compose(name, types
36
+ .model({
37
+ /**
38
+ * #property
39
+ */
40
+ config: createConfigModel(pluginManager, assemblyConfigSchema),
41
+ /**
42
+ * #property
43
+ */
44
+ session: sessionModelType,
45
+ /**
46
+ * #property
47
+ */
48
+ assemblyManager: types.optional(assemblyManagerFactory(assemblyConfigSchema, pluginManager), {}),
49
+ })
50
+ .volatile(self => ({
51
+ /**
52
+ * #volatile
53
+ */
54
+ adminMode: false,
55
+ /**
56
+ * #volatile
57
+ */
58
+ version,
59
+ /**
60
+ * #volatile
61
+ */
62
+ rpcManager: new RpcManager(pluginManager, self.config.configuration.rpc, {
63
+ makeWorkerInstance,
64
+ // when a worker factory is supplied, run RPC off the main thread by
65
+ // default; config `defaultDriver` still overrides this
66
+ defaultDriverName: makeWorkerInstance
67
+ ? 'WebWorkerRpcDriver'
68
+ : 'MainThreadRpcDriver',
69
+ }),
70
+ /**
71
+ * #volatile
72
+ */
73
+ textSearchManager: new TextSearchManager(pluginManager),
74
+ }))
75
+ .actions(self => ({
76
+ /**
77
+ * #action
78
+ * Synchronous: an async caller must
79
+ * `await pluginManager.preloadSessionTypes(snapshot)` first.
80
+ */
81
+ setSession(sessionSnapshot) {
82
+ pluginManager.assertSessionTypesLoaded(sessionSnapshot);
83
+ self.session = cast(migrated(sessionSnapshot, pluginManager));
84
+ },
85
+ /**
86
+ * #action
87
+ * Load a session whose shape is only known at runtime — decoded from a
88
+ * URL, read back from storage, handed over by a non-TypeScript host.
89
+ *
90
+ * Separate from `setSession` because that one takes the compiler-checked
91
+ * snapshot type, which a value parsed out of JSON can never satisfy.
92
+ * The assertion below is the whole reason this exists: it is the single
93
+ * place the conversion happens, instead of every caller asserting at its
94
+ * own site. Nothing is unchecked at runtime — MST validates the snapshot
95
+ * as it applies it and throws on a mismatch, which is the check that
96
+ * actually matters for a value this app did not author.
97
+ */
98
+ restoreSession(sessionSnapshot) {
99
+ pluginManager.assertSessionTypesLoaded(sessionSnapshot);
100
+ self.session = cast(migrated(sessionSnapshot, pluginManager));
101
+ },
102
+ /**
103
+ * #action
104
+ */
105
+ renameCurrentSession(sessionName) {
106
+ self.session.setName(sessionName);
107
+ },
108
+ }))
109
+ .views(self => ({
110
+ /**
111
+ * #getter
112
+ */
113
+ get jbrowse() {
114
+ return self.config;
115
+ },
116
+ /**
117
+ * #getter
118
+ */
119
+ get pluginManager() {
120
+ return pluginManager;
121
+ },
122
+ })), InternetAccountsRootModelMixin(pluginManager))
123
+ .preProcessSnapshot(snap => snap && typeof snap === 'object' && 'session' in snap
124
+ ? { ...snap, session: migrated(snap.session, pluginManager) }
125
+ : snap);
126
+ }
package/esm/index.d.ts CHANGED
@@ -1,2 +1,5 @@
1
- export { default as ModalWidget } from './ui/ModalWidget.tsx';
2
1
  export { default as EmbeddedViewContainer } from './ui/EmbeddedViewContainer.tsx';
2
+ export { createEmbeddedRootModel } from './createEmbeddedRootModel.ts';
3
+ export { EmbeddedSessionMixin } from './EmbeddedSessionMixin.ts';
4
+ export type { EmbeddedSessionParent } from './EmbeddedSessionMixin.ts';
5
+ export { EmbeddedSessionThemeMixin } from './EmbeddedSessionThemeMixin.ts';
package/esm/index.js CHANGED
@@ -1,2 +1,4 @@
1
- export { default as ModalWidget } from "./ui/ModalWidget.js";
2
- export { default as EmbeddedViewContainer } from "./ui/EmbeddedViewContainer.js";
1
+ export { default as EmbeddedViewContainer } from './ui/EmbeddedViewContainer.js';
2
+ export { createEmbeddedRootModel } from './createEmbeddedRootModel.js';
3
+ export { EmbeddedSessionMixin } from './EmbeddedSessionMixin.js';
4
+ export { EmbeddedSessionThemeMixin } from './EmbeddedSessionThemeMixin.js';
@@ -1,6 +1,6 @@
1
1
  import type { IBaseViewModel } from '@jbrowse/core/pluggableElementTypes/models/BaseViewModel';
2
- declare const ViewContainerWrapper: ({ view, children, }: {
2
+ declare const ViewContainer: ({ view, children, }: {
3
3
  view: IBaseViewModel;
4
4
  children: React.ReactNode;
5
- }) => import("react/jsx-runtime").JSX.Element;
6
- export default ViewContainerWrapper;
5
+ }) => import("react").JSX.Element;
6
+ export default ViewContainer;
@@ -1,30 +1,26 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Suspense } from 'react';
3
- import { getSession, useWidthSetter } from '@jbrowse/core/util';
3
+ import { getSession } from '@jbrowse/core/util';
4
+ import { VIEW_HEADER_HEIGHT_VAR, useWidthSetter, } from '@jbrowse/core/util/hooks';
4
5
  import { makeStyles } from '@jbrowse/core/util/tss-react';
5
- import { Paper, ScopedCssBaseline, useTheme } from '@mui/material';
6
6
  import { observer } from 'mobx-react';
7
- import ViewTitle from "./ViewTitle.js";
8
7
  const useStyles = makeStyles()(theme => ({
9
- avoidParentStyle: {
10
- all: 'initial',
11
- },
12
8
  viewContainer: {
13
- overflow: 'hidden',
14
- background: theme.palette.secondary.main,
15
- margin: theme.spacing(0.5),
16
- padding: `0 ${theme.spacing(1)} ${theme.spacing(1)}`,
9
+ width: '100%',
10
+ // clip (not hidden) so sticky descendant headers keep working
11
+ overflow: 'clip',
12
+ background: theme.palette.background.paper,
13
+ border: `1px solid ${theme.palette.divider}`,
14
+ borderRadius: theme.shape.borderRadius,
15
+ // no title bar above the view, so the sticky chrome that clears one pins
16
+ // at the top edge
17
+ [VIEW_HEADER_HEIGHT_VAR]: '0px',
17
18
  },
18
19
  }));
19
20
  const ViewContainer = observer(function ViewContainer({ view, children, }) {
20
21
  const { classes } = useStyles();
21
22
  const session = getSession(view);
22
- const theme = useTheme();
23
- const ref = useWidthSetter(view, theme.spacing(1));
24
- return (_jsxs(Paper, { elevation: 12, ref: ref, className: classes.viewContainer, children: [session.DialogComponent ? (_jsx(Suspense, { fallback: null, children: _jsx(session.DialogComponent, { ...session.DialogProps }) })) : null, _jsx(ViewTitle, { view: view }), _jsx(Paper, { children: children })] }));
25
- });
26
- const ViewContainerWrapper = observer(function ViewContainerWrapper({ view, children, }) {
27
- const { classes } = useStyles();
28
- return (_jsx("div", { className: classes.avoidParentStyle, children: _jsx(ScopedCssBaseline, { children: _jsx(ViewContainer, { view: view, children: children }) }) }));
23
+ const ref = useWidthSetter(view);
24
+ return (_jsxs("div", { ref: ref, className: classes.viewContainer, children: [session.DialogComponent ? (_jsx(Suspense, { fallback: null, children: _jsx(session.DialogComponent, { ...session.DialogProps }) })) : null, children] }));
29
25
  });
30
- export default ViewContainerWrapper;
26
+ export default ViewContainer;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jbrowse/embedded-core",
3
- "version": "4.3.0",
3
+ "version": "5.0.0-beta.10",
4
4
  "type": "module",
5
5
  "description": "JBrowse 2 code shared between embedded products",
6
6
  "keywords": [
@@ -24,23 +24,24 @@
24
24
  "esm"
25
25
  ],
26
26
  "dependencies": {
27
- "@jbrowse/mobx-state-tree": "^5.6.0",
28
- "@mui/icons-material": "^7.3.11",
29
- "@mui/material": "^7.3.11",
30
- "mobx": "^6.15.3",
31
- "mobx-react": "^9.2.1",
32
- "@jbrowse/core": "^4.3.0"
27
+ "@jbrowse/mobx-state-tree": "^6.6.1",
28
+ "@mui/icons-material": "^9.4.0",
29
+ "@mui/material": "^9.4.0",
30
+ "mobx": "^7.0.5",
31
+ "mobx-react": "^10.0.2",
32
+ "@jbrowse/product-core": "^5.0.0-beta.10",
33
+ "@jbrowse/core": "^5.0.0-beta.10"
33
34
  },
34
35
  "peerDependencies": {
35
- "react": ">=18.0.0",
36
- "react-dom": ">=18.0.0"
36
+ "react": ">=19.0.0",
37
+ "react-dom": ">=19.0.0"
37
38
  },
38
39
  "publishConfig": {
39
40
  "access": "public"
40
41
  },
41
42
  "sideEffects": false,
42
43
  "scripts": {
43
- "build:esm": "tsc -p tsconfig.build.esm.json",
44
+ "build:esm": "node ../../node_modules/typescript7/bin/tsc --build tsconfig.build.esm.json",
44
45
  "build": "pnpm build:esm",
45
46
  "test": "cd ../..; jest --passWithNoTests packages/embedded-core",
46
47
  "clean": "rimraf esm *.tsbuildinfo",
@@ -48,9 +49,6 @@
48
49
  },
49
50
  "types": "esm/index.d.ts",
50
51
  "exports": {
51
- ".": {
52
- "types": "./esm/index.d.ts",
53
- "import": "./esm/index.js"
54
- }
52
+ ".": "./esm/index.js"
55
53
  }
56
54
  }
@@ -1,5 +0,0 @@
1
- import type { SessionWithWidgets } from '@jbrowse/core/util';
2
- declare const ModalWidget: ({ session, }: {
3
- session: SessionWithWidgets;
4
- }) => import("react/jsx-runtime").JSX.Element | null;
5
- export default ModalWidget;
@@ -1,32 +0,0 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
2
- import { Suspense } from 'react';
3
- import { Dialog } from '@jbrowse/core/ui';
4
- import { makeStyles } from '@jbrowse/core/util/tss-react';
5
- import { getEnv } from '@jbrowse/mobx-state-tree';
6
- import { AppBar, Paper, Toolbar, Typography } from '@mui/material';
7
- import { observer } from 'mobx-react';
8
- const useStyles = makeStyles()({
9
- paper: {
10
- overflow: 'auto',
11
- },
12
- });
13
- const ModalWidget = observer(function ModalWidget({ session, }) {
14
- const { classes } = useStyles();
15
- const { visibleWidget } = session;
16
- const { pluginManager } = getEnv(session);
17
- if (!visibleWidget) {
18
- return null;
19
- }
20
- const { ReactComponent, HeadingComponent, heading } = pluginManager.getWidgetType(visibleWidget.type);
21
- const Component = pluginManager.evaluateExtensionPoint('Core-replaceWidget', ReactComponent, {
22
- session,
23
- model: visibleWidget,
24
- });
25
- return (_jsx(Dialog, { open: true, onClose: () => {
26
- session.hideAllWidgets();
27
- }, maxWidth: "xl", header: _jsx(AppBar, { position: "static", children: _jsx(Toolbar, { children: HeadingComponent ? (_jsx(HeadingComponent, { model: visibleWidget })) : (_jsx(Typography, { variant: "h6", children: heading })) }) }), children: _jsx(Suspense, { fallback: _jsx("div", { children: "Loading..." }), children: _jsx(Paper, { className: classes.paper, children: _jsx(Component, { model: visibleWidget, session: session, overrideDimensions: {
28
- height: (window.innerHeight * 5) / 8,
29
- width: 800,
30
- } }) }) }) }));
31
- });
32
- export default ModalWidget;
@@ -1,5 +0,0 @@
1
- export default function AboutDialog({ open, version, onClose, }: {
2
- open: boolean;
3
- version: string;
4
- onClose: () => void;
5
- }): import("react/jsx-runtime").JSX.Element;
@@ -1,13 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { Dialog, ExternalLink } from '@jbrowse/core/ui';
3
- import { makeStyles } from '@jbrowse/core/util/tss-react';
4
- import { DialogContent, Typography } from '@mui/material';
5
- const useStyles = makeStyles()({
6
- content: {
7
- minWidth: 800,
8
- },
9
- });
10
- export default function AboutDialog({ open, version, onClose, }) {
11
- const { classes } = useStyles();
12
- return (_jsx(Dialog, { open: open, onClose: onClose, maxWidth: "xl", title: `JBrowse v${version}`, children: _jsxs(DialogContent, { className: classes.content, children: [_jsx(Typography, { children: "JBrowse is a GMOD project \u00A9 2019-2021, The Evolutionary Software Foundation" }), _jsx(Typography, { children: "Here are some resources and documentation. Please report the version number above when asking questions. Thanks!" }), _jsxs("ul", { children: [_jsx("li", { children: _jsx(ExternalLink, { href: "https://github.com/GMOD/jbrowse-components/discussions", children: "Question & answer forum" }) }), _jsx("li", { children: _jsx(ExternalLink, { href: "https://github.com/GMOD/jbrowse-components/issues/new/choose", children: "Report a bug" }) }), _jsx("li", { children: _jsx(ExternalLink, { href: "https://jbrowse.org/jb2/docs/user_guide", children: "User guide" }) }), _jsx("li", { children: _jsx(ExternalLink, { href: "https://jbrowse.org/jb2/docs/", children: "Documentation" }) })] })] }) }));
13
- }
@@ -1,8 +0,0 @@
1
- import type { IBaseViewModel } from '@jbrowse/core/pluggableElementTypes/models/BaseViewModel';
2
- import type { IconButtonProps, SvgIconProps } from '@mui/material';
3
- declare const ViewMenu: ({ model, IconButtonProps, IconProps, }: {
4
- model: IBaseViewModel;
5
- IconButtonProps?: IconButtonProps;
6
- IconProps?: SvgIconProps;
7
- }) => import("react/jsx-runtime").JSX.Element;
8
- export default ViewMenu;
@@ -1,8 +0,0 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
2
- import { CascadingMenuButton } from '@jbrowse/core/ui';
3
- import MenuIcon from '@mui/icons-material/Menu';
4
- import { observer } from 'mobx-react';
5
- const ViewMenu = observer(function ViewMenu({ model, IconButtonProps, IconProps, }) {
6
- return (_jsx(CascadingMenuButton, { menuItems: () => model.menuItems(), "data-testid": "view_menu_icon", ...IconButtonProps, children: _jsx(MenuIcon, { ...IconProps }) }));
7
- });
8
- export default ViewMenu;
@@ -1,5 +0,0 @@
1
- import type { IBaseViewModel } from '@jbrowse/core/pluggableElementTypes/models/BaseViewModel';
2
- declare const ViewTitle: ({ view, }: {
3
- view: IBaseViewModel;
4
- }) => import("react/jsx-runtime").JSX.Element;
5
- export default ViewTitle;
@@ -1,47 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { Suspense, lazy, useState } from 'react';
3
- import { Logomark } from '@jbrowse/core/ui';
4
- import { getSession } from '@jbrowse/core/util';
5
- import { makeStyles } from '@jbrowse/core/util/tss-react';
6
- import { IconButton, Typography, alpha } from '@mui/material';
7
- import { observer } from 'mobx-react';
8
- import ViewMenu from "./ViewMenu.js";
9
- const VersionAboutDialog = lazy(() => import("./VersionAboutDialog.js"));
10
- const useStyles = makeStyles()(theme => ({
11
- icon: {
12
- color: theme.palette.secondary.contrastText,
13
- },
14
- displayName: {
15
- marginTop: 2,
16
- color: theme.palette.secondary.contrastText,
17
- },
18
- grow: {
19
- flexGrow: 1,
20
- },
21
- container: {
22
- display: 'flex',
23
- },
24
- iconRoot: {
25
- '&:hover': {
26
- backgroundColor: alpha(theme.palette.secondary.contrastText, theme.palette.action.hoverOpacity),
27
- '@media (hover: none)': {
28
- backgroundColor: 'transparent',
29
- },
30
- },
31
- },
32
- }));
33
- const ViewTitle = observer(function ViewTitle({ view, }) {
34
- const { classes } = useStyles();
35
- const { displayName } = view;
36
- const [dialogOpen, setDialogOpen] = useState(false);
37
- const session = getSession(view);
38
- return (_jsxs("div", { className: classes.container, children: [_jsx(ViewMenu, { model: view, IconButtonProps: {
39
- classes: { root: classes.iconRoot },
40
- edge: 'start',
41
- }, IconProps: { className: classes.icon } }), _jsx("div", { className: classes.grow }), displayName ? (_jsx(Typography, { variant: "body2", className: classes.displayName, children: displayName })) : null, _jsx("div", { className: classes.grow }), _jsx(IconButton, { onClick: () => {
42
- setDialogOpen(true);
43
- }, children: _jsx("div", { style: { width: 22, height: 22 }, children: _jsx(Logomark, { variant: "white" }) }) }), dialogOpen ? (_jsx(Suspense, { fallback: null, children: _jsx(VersionAboutDialog, { open: true, onClose: () => {
44
- setDialogOpen(false);
45
- }, version: session.version }) })) : null] }));
46
- });
47
- export default ViewTitle;