@jbrowse/embedded-core 4.3.0 → 5.0.0-beta.2

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,117 @@
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, } from '@jbrowse/product-core';
6
+ /**
7
+ * #stateModel EmbeddedRootModel
8
+ * #category root
9
+ * Root model shared by the single-view embedded products
10
+ * (react-linear-genome-view, react-circular-genome-view). Each product supplies
11
+ * its own model name, version, and session model, and may `.props()` on extra
12
+ * fields (e.g. the LGV `disableAddTracks`/`drawerViewHeight`). Internet accounts
13
+ * come from the same product-core mixin the web/desktop root models use, so
14
+ * config `internetAccounts` are auto-initialized (no manual wiring needed).
15
+ */
16
+ export function createEmbeddedRootModel({ name, version, pluginManager, sessionModelType, makeWorkerInstance, }) {
17
+ const assemblyConfigSchema = assemblyConfigSchemaFactory(pluginManager);
18
+ return types.compose(name, types
19
+ .model({
20
+ /**
21
+ * #property
22
+ */
23
+ config: createConfigModel(pluginManager, assemblyConfigSchema),
24
+ /**
25
+ * #property
26
+ */
27
+ session: sessionModelType,
28
+ /**
29
+ * #property
30
+ */
31
+ assemblyManager: types.optional(assemblyManagerFactory(assemblyConfigSchema, pluginManager), {}),
32
+ })
33
+ .volatile(self => ({
34
+ /**
35
+ * #volatile
36
+ */
37
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
38
+ error: undefined,
39
+ /**
40
+ * #volatile
41
+ */
42
+ adminMode: false,
43
+ /**
44
+ * #volatile
45
+ */
46
+ version,
47
+ /**
48
+ * #volatile
49
+ */
50
+ rpcManager: new RpcManager(pluginManager, self.config.configuration.rpc, {
51
+ makeWorkerInstance,
52
+ // when a worker factory is supplied, run RPC off the main thread by
53
+ // default; config `defaultDriver` still overrides this
54
+ defaultDriverName: makeWorkerInstance
55
+ ? 'WebWorkerRpcDriver'
56
+ : 'MainThreadRpcDriver',
57
+ }),
58
+ /**
59
+ * #volatile
60
+ */
61
+ textSearchManager: new TextSearchManager(pluginManager),
62
+ }))
63
+ .actions(self => ({
64
+ /**
65
+ * #action
66
+ * Synchronous: an async caller must
67
+ * `await pluginManager.preloadSessionTypes(snapshot)` first.
68
+ */
69
+ setSession(sessionSnapshot) {
70
+ pluginManager.assertSessionTypesLoaded(sessionSnapshot);
71
+ self.session = cast(sessionSnapshot);
72
+ },
73
+ /**
74
+ * #action
75
+ * Load a session whose shape is only known at runtime — decoded from a
76
+ * URL, read back from storage, handed over by a non-TypeScript host.
77
+ *
78
+ * Separate from `setSession` because that one takes the compiler-checked
79
+ * snapshot type, which a value parsed out of JSON can never satisfy.
80
+ * The assertion below is the whole reason this exists: it is the single
81
+ * place the conversion happens, instead of every caller asserting at its
82
+ * own site. Nothing is unchecked at runtime — MST validates the snapshot
83
+ * as it applies it and throws on a mismatch, which is the check that
84
+ * actually matters for a value this app did not author.
85
+ */
86
+ restoreSession(sessionSnapshot) {
87
+ pluginManager.assertSessionTypesLoaded(sessionSnapshot);
88
+ self.session = cast(sessionSnapshot);
89
+ },
90
+ /**
91
+ * #action
92
+ */
93
+ renameCurrentSession(sessionName) {
94
+ self.session.setName(sessionName);
95
+ },
96
+ /**
97
+ * #action
98
+ */
99
+ setError(error) {
100
+ self.error = error;
101
+ },
102
+ }))
103
+ .views(self => ({
104
+ /**
105
+ * #getter
106
+ */
107
+ get jbrowse() {
108
+ return self.config;
109
+ },
110
+ /**
111
+ * #getter
112
+ */
113
+ get pluginManager() {
114
+ return pluginManager;
115
+ },
116
+ })), InternetAccountsRootModelMixin(pluginManager));
117
+ }
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,16 +1,17 @@
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 { 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
+ import { Paper } from '@mui/material';
6
7
  import { observer } from 'mobx-react';
7
- import ViewTitle from "./ViewTitle.js";
8
+ import ViewTitle from './ViewTitle.js';
8
9
  const useStyles = makeStyles()(theme => ({
9
- avoidParentStyle: {
10
- all: 'initial',
11
- },
12
10
  viewContainer: {
13
- overflow: 'hidden',
11
+ width: '100%',
12
+ // clip (not hidden) so sticky descendant headers keep working
13
+ // xref https://stackoverflow.com/questions/43909940/why-does-overflowhidden-prevent-positionsticky-from-working
14
+ overflow: 'clip',
14
15
  background: theme.palette.secondary.main,
15
16
  margin: theme.spacing(0.5),
16
17
  padding: `0 ${theme.spacing(1)} ${theme.spacing(1)}`,
@@ -19,12 +20,7 @@ const useStyles = makeStyles()(theme => ({
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));
23
+ const ref = useWidthSetter(view);
24
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
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 }) }) }));
29
- });
30
- export default ViewContainerWrapper;
26
+ export default ViewContainer;
@@ -2,4 +2,4 @@ export default function AboutDialog({ open, version, onClose, }: {
2
2
  open: boolean;
3
3
  version: string;
4
4
  onClose: () => void;
5
- }): import("react/jsx-runtime").JSX.Element;
5
+ }): import("react").JSX.Element;
@@ -1,7 +1,7 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { Dialog, ExternalLink } from '@jbrowse/core/ui';
2
+ import { ExternalLink, InfoDialog } from '@jbrowse/core/ui';
3
3
  import { makeStyles } from '@jbrowse/core/util/tss-react';
4
- import { DialogContent, Typography } from '@mui/material';
4
+ import { Typography } from '@mui/material';
5
5
  const useStyles = makeStyles()({
6
6
  content: {
7
7
  minWidth: 800,
@@ -9,5 +9,5 @@ const useStyles = makeStyles()({
9
9
  });
10
10
  export default function AboutDialog({ open, version, onClose, }) {
11
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" }) })] })] }) }));
12
+ return (_jsx(InfoDialog, { open: open, onClose: onClose, maxWidth: "xl", title: `JBrowse v${version}`, children: _jsxs("div", { className: classes.content, children: [_jsx(Typography, { children: "JBrowse is a GMOD project \u00A9 2019-2026, 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
13
  }
@@ -1,5 +1,5 @@
1
1
  import type { IBaseViewModel } from '@jbrowse/core/pluggableElementTypes/models/BaseViewModel';
2
2
  declare const ViewTitle: ({ view, }: {
3
3
  view: IBaseViewModel;
4
- }) => import("react/jsx-runtime").JSX.Element;
4
+ }) => import("react").JSX.Element;
5
5
  export default ViewTitle;
@@ -1,12 +1,13 @@
1
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';
2
+ import { Suspense, lazy, useRef, useState } from 'react';
3
+ import { CascadingMenuButton, Logomark, VIEW_HEADER_HEIGHT, } from '@jbrowse/core/ui';
4
4
  import { getSession } from '@jbrowse/core/util';
5
+ import { VIEW_HEADER_HEIGHT_VAR, useChromeHeightVar, } from '@jbrowse/core/util/hooks';
5
6
  import { makeStyles } from '@jbrowse/core/util/tss-react';
7
+ import MenuIcon from '@mui/icons-material/Menu';
6
8
  import { IconButton, Typography, alpha } from '@mui/material';
7
9
  import { observer } from 'mobx-react';
8
- import ViewMenu from "./ViewMenu.js";
9
- const VersionAboutDialog = lazy(() => import("./VersionAboutDialog.js"));
10
+ const VersionAboutDialog = lazy(() => import('./VersionAboutDialog.js'));
10
11
  const useStyles = makeStyles()(theme => ({
11
12
  icon: {
12
13
  color: theme.palette.secondary.contrastText,
@@ -20,6 +21,20 @@ const useStyles = makeStyles()(theme => ({
20
21
  },
21
22
  container: {
22
23
  display: 'flex',
24
+ alignItems: 'center',
25
+ // A fixed height, unlike the web app's view header, which is a floor. Both
26
+ // publish what they measure through `useChromeHeightVar`, so what sticks
27
+ // below follows either — this is a density choice and no longer a
28
+ // load-bearing constant. It stays fixed because the content that would
29
+ // widen the box is the logomark button, whose 22px icon and 5px padding
30
+ // want 32: a floor here spends 4px of an embedder's box to stop that button
31
+ // painting 2px outside a bar nothing clips.
32
+ height: VIEW_HEADER_HEIGHT,
33
+ top: 0,
34
+ zIndex: 900,
35
+ // a sticky element does not carry its parent's background, and what would
36
+ // otherwise show through is the tracks scrolling underneath
37
+ background: theme.palette.secondary.main,
23
38
  },
24
39
  iconRoot: {
25
40
  '&:hover': {
@@ -35,10 +50,13 @@ const ViewTitle = observer(function ViewTitle({ view, }) {
35
50
  const { displayName } = view;
36
51
  const [dialogOpen, setDialogOpen] = useState(false);
37
52
  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: () => {
53
+ // `=== true` because the member is optional on AbstractSessionModel: a
54
+ // session with no such notion reads as "don't pin", which is what the LGV
55
+ // model's own getter does with it too
56
+ const stickyViewHeaders = session.stickyViewHeaders === true;
57
+ const ref = useRef(null);
58
+ useChromeHeightVar(ref, VIEW_HEADER_HEIGHT_VAR);
59
+ return (_jsxs("div", { ref: ref, className: classes.container, style: { position: stickyViewHeaders ? 'sticky' : undefined }, children: [_jsx(CascadingMenuButton, { menuItems: () => view.menuItems(), "data-testid": "view_menu_icon", classes: { root: classes.iconRoot }, edge: "start", children: _jsx(MenuIcon, { 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
60
  setDialogOpen(true);
43
61
  }, 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
62
  setDialogOpen(false);
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.2",
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.5.1",
28
+ "@mui/icons-material": "^9.4.0",
29
+ "@mui/material": "^9.4.0",
30
+ "mobx": "^7.0.3",
31
+ "mobx-react": "^10.0.2",
32
+ "@jbrowse/product-core": "^5.0.0-beta.2",
33
+ "@jbrowse/core": "^5.0.0-beta.2"
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,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;