jbrowse-plugin-protein3d 0.4.12 → 0.4.14
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/dist/LaunchProteinView/components/LaunchSettingsDialog.d.ts +5 -0
- package/dist/LaunchProteinView/components/LaunchSettingsDialog.js +23 -0
- package/dist/LaunchProteinView/components/ProteinViewActions.js +13 -2
- package/dist/LaunchProteinView/utils/launchViewUtils.d.ts +2 -1
- package/dist/LaunchProteinView/utils/launchViewUtils.js +7 -2
- package/dist/LaunchProteinView/utils/sideBySide.d.ts +11 -0
- package/dist/LaunchProteinView/utils/sideBySide.js +33 -0
- package/dist/LaunchProteinViewExtensionPoint/index.js +9 -2
- package/dist/ProteinView/components/FeatureBar.js +1 -1
- package/dist/config.json +1 -1
- package/dist/jbrowse-plugin-protein3d.umd.production.min.js +15 -15
- package/dist/jbrowse-plugin-protein3d.umd.production.min.js.map +4 -4
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +2 -2
- package/src/LaunchProteinView/components/LaunchSettingsDialog.tsx +63 -0
- package/src/LaunchProteinView/components/ProteinViewActions.tsx +21 -1
- package/src/LaunchProteinView/utils/launchViewUtils.ts +10 -1
- package/src/LaunchProteinView/utils/sideBySide.ts +55 -0
- package/src/LaunchProteinViewExtensionPoint/index.ts +17 -1
- package/src/ProteinView/components/FeatureBar.tsx +4 -0
- package/src/version.ts +1 -1
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
import { Button, Checkbox, Dialog, DialogActions, DialogContent, DialogTitle, FormControlLabel, FormGroup, Typography, } from '@mui/material';
|
|
3
|
+
import { getLaunchSideBySide, setLaunchSideBySide } from '../utils/sideBySide';
|
|
4
|
+
// Small, self-contained launch settings (NOT the global preferences dialog):
|
|
5
|
+
// just the options that affect how this protein view opens.
|
|
6
|
+
export default function LaunchSettingsDialog({ open, onClose, }) {
|
|
7
|
+
const [sideBySide, setSideBySide] = useState(() => getLaunchSideBySide());
|
|
8
|
+
return (React.createElement(Dialog, { open: open, onClose: () => {
|
|
9
|
+
onClose();
|
|
10
|
+
} },
|
|
11
|
+
React.createElement(DialogTitle, null, "Launch settings"),
|
|
12
|
+
React.createElement(DialogContent, null,
|
|
13
|
+
React.createElement(FormGroup, null,
|
|
14
|
+
React.createElement(FormControlLabel, { control: React.createElement(Checkbox, { checked: sideBySide }), label: "Open protein view side-by-side with the genome view", onChange: (_, checked) => {
|
|
15
|
+
setSideBySide(checked);
|
|
16
|
+
setLaunchSideBySide(checked);
|
|
17
|
+
} })),
|
|
18
|
+
React.createElement(Typography, { variant: "body2", color: "text.secondary" }, "When enabled, launching a protein view places it to the right of the connected genome view in a split layout instead of stacking it below.")),
|
|
19
|
+
React.createElement(DialogActions, null,
|
|
20
|
+
React.createElement(Button, { variant: "contained", onClick: () => {
|
|
21
|
+
onClose();
|
|
22
|
+
} }, "Close"))));
|
|
23
|
+
}
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import React, { useState } from 'react';
|
|
2
2
|
import { ErrorMessage } from '@jbrowse/core/ui';
|
|
3
3
|
import ArrowDropDownIcon from '@mui/icons-material/ArrowDropDown';
|
|
4
|
-
import
|
|
4
|
+
import SettingsIcon from '@mui/icons-material/Settings';
|
|
5
|
+
import { Button, ButtonGroup, IconButton, Tooltip, Typography } from '@mui/material';
|
|
5
6
|
import LaunchOptionsDialog from './LaunchOptionsDialog';
|
|
7
|
+
import LaunchSettingsDialog from './LaunchSettingsDialog';
|
|
6
8
|
import SequenceMismatchNotice from './SequenceMismatchNotice';
|
|
7
9
|
import { getLaunchMissingReasons, safeLaunch } from '../utils/launchHelpers';
|
|
8
10
|
import { hasMsaViewPlugin, launch1DProteinView, launch3DProteinView, launch3DProteinViewWithMsa, launchMsaView, } from '../utils/launchViewUtils';
|
|
9
11
|
export default function ProteinViewActions({ handleClose, uniprotId, userSelectedProteinSequence, selectedTranscript, url, confidenceUrl, feature, view, session, alignmentAlgorithm, onAlignmentAlgorithmChange, sequencesMatch, isLoading, error, }) {
|
|
10
12
|
const [dialogOpen, setDialogOpen] = useState(false);
|
|
13
|
+
const [settingsOpen, setSettingsOpen] = useState(false);
|
|
11
14
|
const [launchError, setLaunchError] = useState();
|
|
12
15
|
// Disable launch while loading — SWR's keepPreviousData would otherwise let
|
|
13
16
|
// a user click Launch on stale results (wrong UniProt ID) during a refetch.
|
|
@@ -87,6 +90,11 @@ export default function ProteinViewActions({ handleClose, uniprotId, userSelecte
|
|
|
87
90
|
return (React.createElement(React.Fragment, null,
|
|
88
91
|
launchError ? React.createElement(ErrorMessage, { error: launchError }) : null,
|
|
89
92
|
sequencesMatch === false ? (React.createElement(SequenceMismatchNotice, { alignmentAlgorithm: alignmentAlgorithm, onAlignmentAlgorithmChange: onAlignmentAlgorithmChange })) : null,
|
|
93
|
+
React.createElement(Tooltip, { title: "Launch settings" },
|
|
94
|
+
React.createElement(IconButton, { size: "small", "aria-label": "Launch settings", onClick: () => {
|
|
95
|
+
setSettingsOpen(true);
|
|
96
|
+
} },
|
|
97
|
+
React.createElement(SettingsIcon, { fontSize: "small" }))),
|
|
90
98
|
React.createElement(Button, { variant: "contained", color: "secondary", size: "small", onClick: () => {
|
|
91
99
|
handleClose();
|
|
92
100
|
} }, "Cancel"),
|
|
@@ -97,5 +105,8 @@ export default function ProteinViewActions({ handleClose, uniprotId, userSelecte
|
|
|
97
105
|
setDialogOpen(true);
|
|
98
106
|
}, "aria-label": "More launch options" },
|
|
99
107
|
React.createElement(ArrowDropDownIcon, null))),
|
|
100
|
-
React.createElement(LaunchOptionsDialog, { open: dialogOpen, onClose: closeMenu, options: launchOptions })
|
|
108
|
+
React.createElement(LaunchOptionsDialog, { open: dialogOpen, onClose: closeMenu, options: launchOptions }),
|
|
109
|
+
React.createElement(LaunchSettingsDialog, { open: settingsOpen, onClose: () => {
|
|
110
|
+
setSettingsOpen(false);
|
|
111
|
+
} })));
|
|
101
112
|
}
|
|
@@ -21,13 +21,14 @@ interface LaunchViewParams {
|
|
|
21
21
|
uniprotId?: string;
|
|
22
22
|
}
|
|
23
23
|
export declare function formatViewName(prefix: string, feature: Feature, selectedTranscript?: Feature, uniprotId?: string): string;
|
|
24
|
-
export declare function launch3DProteinView({ session, view, feature, selectedTranscript, uniprotId, url, data, userProvidedTranscriptSequence, alignmentAlgorithm, displayName, connectedMsaViewId, }: LaunchViewParams & {
|
|
24
|
+
export declare function launch3DProteinView({ session, view, feature, selectedTranscript, uniprotId, url, data, userProvidedTranscriptSequence, alignmentAlgorithm, displayName, connectedMsaViewId, sideBySide, }: LaunchViewParams & {
|
|
25
25
|
url?: string;
|
|
26
26
|
data?: string;
|
|
27
27
|
userProvidedTranscriptSequence?: string;
|
|
28
28
|
alignmentAlgorithm?: string;
|
|
29
29
|
displayName?: string;
|
|
30
30
|
connectedMsaViewId?: string;
|
|
31
|
+
sideBySide?: boolean;
|
|
31
32
|
}): import("@jbrowse/core/util").AbstractViewModel;
|
|
32
33
|
export declare function launch1DProteinView({ session, view, feature, selectedTranscript, uniprotId, confidenceUrl, }: LaunchViewParams & {
|
|
33
34
|
confidenceUrl?: string;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { isSessionWithAddTracks } from '@jbrowse/core/util';
|
|
2
|
+
import { getLaunchSideBySide, launchViewSideBySide } from './sideBySide';
|
|
2
3
|
import { getGeneDisplayName, getTranscriptDisplayName } from './util';
|
|
3
4
|
import { launchProteinAnnotationView } from '../components/launchProteinAnnotationView';
|
|
4
5
|
export const ALPHAFOLD_VERSION = 'v6';
|
|
@@ -58,7 +59,7 @@ export function formatViewName(prefix, feature, selectedTranscript, uniprotId) {
|
|
|
58
59
|
.filter(s => !!s)
|
|
59
60
|
.join(' - ');
|
|
60
61
|
}
|
|
61
|
-
export function launch3DProteinView({ session, view, feature, selectedTranscript, uniprotId, url, data, userProvidedTranscriptSequence, alignmentAlgorithm, displayName, connectedMsaViewId, }) {
|
|
62
|
+
export function launch3DProteinView({ session, view, feature, selectedTranscript, uniprotId, url, data, userProvidedTranscriptSequence, alignmentAlgorithm, displayName, connectedMsaViewId, sideBySide, }) {
|
|
62
63
|
const snap = {
|
|
63
64
|
type: 'ProteinView',
|
|
64
65
|
alignmentAlgorithm,
|
|
@@ -75,7 +76,11 @@ export function launch3DProteinView({ session, view, feature, selectedTranscript
|
|
|
75
76
|
displayName: displayName ??
|
|
76
77
|
formatViewName('Protein view', feature, selectedTranscript, uniprotId),
|
|
77
78
|
};
|
|
78
|
-
|
|
79
|
+
const proteinView = session.addView('ProteinView', snap);
|
|
80
|
+
if (sideBySide ?? getLaunchSideBySide()) {
|
|
81
|
+
launchViewSideBySide(session, proteinView.id);
|
|
82
|
+
}
|
|
83
|
+
return proteinView;
|
|
79
84
|
}
|
|
80
85
|
export async function launch1DProteinView({ session, view, feature, selectedTranscript, uniprotId, confidenceUrl, }) {
|
|
81
86
|
if (!uniprotId || !isSessionWithAddTracks(session)) {
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { AbstractSessionModel } from '@jbrowse/core/util';
|
|
2
|
+
export declare function getLaunchSideBySide(): boolean;
|
|
3
|
+
export declare function setLaunchSideBySide(value: boolean): void;
|
|
4
|
+
/**
|
|
5
|
+
* Place a freshly-added view to the right of the others in a workspaces (tiled)
|
|
6
|
+
* layout. Mirrors the "Move to split view" view-menu action: queue a splitRight
|
|
7
|
+
* pending move for this view, then enable workspaces so TiledViewsContainer
|
|
8
|
+
* consumes the move on mount (other views land in the left panel, this one in a
|
|
9
|
+
* new right panel). No-op on sessions without workspaces support.
|
|
10
|
+
*/
|
|
11
|
+
export declare function launchViewSideBySide(session: AbstractSessionModel, viewId: string): void;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// Self-contained launch preference (NOT the global/core preferences system):
|
|
2
|
+
// whether a protein view launched from a genome feature opens side-by-side with
|
|
3
|
+
// its connected genome view (left genome | right protein) instead of stacked.
|
|
4
|
+
const SIDE_BY_SIDE_KEY = 'proteinView-launchSideBySide';
|
|
5
|
+
// Default to side-by-side: a connected genome+protein pair reads best as a
|
|
6
|
+
// left/right split. Users can turn it off in the launch dialog's settings.
|
|
7
|
+
const DEFAULT_SIDE_BY_SIDE = true;
|
|
8
|
+
export function getLaunchSideBySide() {
|
|
9
|
+
const stored = localStorage.getItem(SIDE_BY_SIDE_KEY);
|
|
10
|
+
return stored === null ? DEFAULT_SIDE_BY_SIDE : stored === 'true';
|
|
11
|
+
}
|
|
12
|
+
export function setLaunchSideBySide(value) {
|
|
13
|
+
localStorage.setItem(SIDE_BY_SIDE_KEY, value ? 'true' : 'false');
|
|
14
|
+
}
|
|
15
|
+
function isSessionWithWorkspaces(session) {
|
|
16
|
+
return ('setUseWorkspaces' in session &&
|
|
17
|
+
typeof session.setUseWorkspaces === 'function' &&
|
|
18
|
+
'setPendingMove' in session &&
|
|
19
|
+
typeof session.setPendingMove === 'function');
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Place a freshly-added view to the right of the others in a workspaces (tiled)
|
|
23
|
+
* layout. Mirrors the "Move to split view" view-menu action: queue a splitRight
|
|
24
|
+
* pending move for this view, then enable workspaces so TiledViewsContainer
|
|
25
|
+
* consumes the move on mount (other views land in the left panel, this one in a
|
|
26
|
+
* new right panel). No-op on sessions without workspaces support.
|
|
27
|
+
*/
|
|
28
|
+
export function launchViewSideBySide(session, viewId) {
|
|
29
|
+
if (isSessionWithWorkspaces(session)) {
|
|
30
|
+
session.setPendingMove({ type: 'splitRight', viewId });
|
|
31
|
+
session.setUseWorkspaces(true);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { resolveShortLaunch } from './resolveShortLaunch';
|
|
2
|
+
import { getLaunchSideBySide, launchViewSideBySide, } from '../LaunchProteinView/utils/sideBySide';
|
|
2
3
|
export default function LaunchProteinViewExtensionPointF(pluginManager) {
|
|
3
4
|
pluginManager.addToExtensionPoint('LaunchView-ProteinView',
|
|
4
5
|
// LaunchView extension points are typed as transformers `(extendee, props)
|
|
@@ -6,7 +7,7 @@ export default function LaunchProteinViewExtensionPointF(pluginManager) {
|
|
|
6
7
|
// handlers and ignores the return value. Casting away the signature
|
|
7
8
|
// mismatch rather than fabricating a fake return.
|
|
8
9
|
// @ts-expect-error
|
|
9
|
-
async ({ session, url, uniprotId, transcriptId, userProvidedTranscriptSequence, feature, connectedViewId, connectedView, alignmentAlgorithm, displayName, height, showControls, showHighlight, zoomToBaseLevel, }) => {
|
|
10
|
+
async ({ session, url, uniprotId, transcriptId, userProvidedTranscriptSequence, feature, connectedViewId, connectedView, alignmentAlgorithm, displayName, height, showControls, showHighlight, zoomToBaseLevel, sideBySide, }) => {
|
|
10
11
|
// Short-URL form: `uniprotId` + `transcriptId` + `connectedView` (no
|
|
11
12
|
// explicit `url`/`feature`/sequence). Derive the structure URL, the
|
|
12
13
|
// transcript feature, and the translated sequence from the connected
|
|
@@ -37,6 +38,9 @@ export default function LaunchProteinViewExtensionPointF(pluginManager) {
|
|
|
37
38
|
// `connectedView` is supplied we create the LinearGenomeView here and wire
|
|
38
39
|
// its id, letting a single spec entry produce a connected genome+protein
|
|
39
40
|
// pair (e.g. hover a variant to highlight the residue).
|
|
41
|
+
// a connected view this launch created itself can be split beside the
|
|
42
|
+
// protein view; a pre-existing connectedViewId is left in place
|
|
43
|
+
const ownsConnectedView = !connectedViewId && !!connectedView;
|
|
40
44
|
const resolvedConnectedViewId = connectedViewId ??
|
|
41
45
|
(connectedView
|
|
42
46
|
? session.addView('LinearGenomeView', {
|
|
@@ -44,7 +48,7 @@ export default function LaunchProteinViewExtensionPointF(pluginManager) {
|
|
|
44
48
|
init: connectedView,
|
|
45
49
|
}).id
|
|
46
50
|
: undefined);
|
|
47
|
-
session.addView('ProteinView', {
|
|
51
|
+
const proteinView = session.addView('ProteinView', {
|
|
48
52
|
type: 'ProteinView',
|
|
49
53
|
alignmentAlgorithm,
|
|
50
54
|
displayName,
|
|
@@ -63,5 +67,8 @@ export default function LaunchProteinViewExtensionPointF(pluginManager) {
|
|
|
63
67
|
},
|
|
64
68
|
],
|
|
65
69
|
});
|
|
70
|
+
if (ownsConnectedView && (sideBySide ?? getLaunchSideBySide())) {
|
|
71
|
+
launchViewSideBySide(session, proteinView.id);
|
|
72
|
+
}
|
|
66
73
|
});
|
|
67
74
|
}
|
|
@@ -84,7 +84,7 @@ const FeatureBar = observer(function FeatureBar({ feature, model, }) {
|
|
|
84
84
|
const { left, width } = getFeatureGeometry(feature, structurePositionToAlignmentMap);
|
|
85
85
|
const color = getFeatureColor(feature.type);
|
|
86
86
|
return (React.createElement(Tooltip, { title: React.createElement(FeatureTooltipContent, { feature: feature }), followCursor: true },
|
|
87
|
-
React.createElement("div", { onClick: () => {
|
|
87
|
+
React.createElement("div", { "data-testid": `protein-feature-${feature.type}`, "data-feature-id": feature.uniqueId, "data-feature-start": feature.start, "data-feature-end": feature.end, onClick: () => {
|
|
88
88
|
handleClick();
|
|
89
89
|
}, onMouseEnter: () => {
|
|
90
90
|
handleMouseEnter();
|
package/dist/config.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"plugins": [
|
|
3
3
|
{
|
|
4
4
|
"name": "Protein3d",
|
|
5
|
-
"url": "https://jbrowse.org/plugins/jbrowse-plugin-protein3d/
|
|
5
|
+
"url": "https://jbrowse.org/plugins/jbrowse-plugin-protein3d/latest/dist/jbrowse-plugin-protein3d.umd.production.min.js"
|
|
6
6
|
}
|
|
7
7
|
],
|
|
8
8
|
"assemblies": [
|