molstar
Version:
A comprehensive macromolecular library.
249 lines (248 loc) • 13.6 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Log = exports.DefaultViewport = exports.ControlsWrapper = exports.PluginContextContainer = void 0;
exports.Plugin = Plugin;
const tslib_1 = require("tslib");
const jsx_runtime_1 = require("react/jsx-runtime");
const React = tslib_1.__importStar(require("react"));
const mol_util_1 = require("../mol-util");
const base_1 = require("./base");
const controls_1 = require("./controls");
const left_panel_1 = require("./left-panel");
const sequence_1 = require("./sequence");
const task_1 = require("./task");
const toast_1 = require("./toast");
const viewport_1 = require("./viewport");
const commands_1 = require("../mol-plugin/commands");
const file_1 = require("../mol-plugin-state/actions/file");
const assets_1 = require("../mol-util/assets");
const rxjs_1 = require("rxjs");
const use_behavior_1 = require("./hooks/use-behavior");
function Plugin({ plugin }) {
if (plugin.isInitialized) {
return (0, jsx_runtime_1.jsx)(base_1.PluginReactContext.Provider, { value: plugin, children: (0, jsx_runtime_1.jsx)(Layout, {}) });
}
return (0, jsx_runtime_1.jsx)(PluginInitWrapper, { plugin: plugin });
}
function PluginInitWrapper({ plugin }) {
const [state, setState] = React.useState({ kind: 'pending' });
React.useEffect(() => {
setState({ kind: 'pending' });
let mounted = true;
plugin.initialized.then(() => {
if (mounted)
setState({ kind: 'initialized' });
}).catch(err => {
if (mounted)
setState({ kind: 'error', message: `${err}` });
});
return () => { mounted = false; };
}, [plugin]);
if (state.kind === 'pending')
return null;
if (state.kind === 'error') {
return (0, jsx_runtime_1.jsx)("div", { className: 'msp-plugin', children: (0, jsx_runtime_1.jsxs)("div", { className: 'msp-plugin-init-error', children: ["Initialization error: ", state.message] }) });
}
return (0, jsx_runtime_1.jsx)(base_1.PluginReactContext.Provider, { value: plugin, children: (0, jsx_runtime_1.jsx)(Layout, {}) });
}
class PluginContextContainer extends React.Component {
render() {
return (0, jsx_runtime_1.jsx)(base_1.PluginReactContext.Provider, { value: this.props.plugin, children: (0, jsx_runtime_1.jsx)("div", { className: 'msp-plugin', children: this.props.children }) });
}
}
exports.PluginContextContainer = PluginContextContainer;
class Layout extends base_1.PluginUIComponent {
constructor() {
super(...arguments);
this.onDrop = (ev) => {
ev.preventDefault();
const files = [];
if (ev.dataTransfer.items) {
// Use DataTransferItemList interface to access the file(s)
for (let i = 0; i < ev.dataTransfer.items.length; i++) {
if (ev.dataTransfer.items[i].kind !== 'file')
continue;
const file = ev.dataTransfer.items[i].getAsFile();
if (file)
files.push(file);
}
}
else {
for (let i = 0; i < ev.dataTransfer.files.length; i++) {
const file = ev.dataTransfer.files[i];
if (file)
files.push(file);
}
}
const sessions = files.filter(f => {
const fn = f.name.toLowerCase();
return fn.endsWith('.molx') || fn.endsWith('.molj');
});
if (sessions.length > 0) {
commands_1.PluginCommands.State.Snapshots.OpenFile(this.plugin, { file: sessions[0] });
}
else {
this.plugin.runTask(this.plugin.state.data.applyAction(file_1.OpenFiles, {
files: files.map(f => assets_1.Asset.File(f)),
format: { name: 'auto', params: {} },
visuals: true
}));
}
};
this.onDragOver = (ev) => {
ev.preventDefault();
};
this.showDragOverlay = new rxjs_1.BehaviorSubject(false);
this.onDragEnter = (ev) => {
let hasFile = false;
if (ev.dataTransfer.items && ev.dataTransfer.items.length > 0) {
for (let i = 0; i < ev.dataTransfer.items.length; i++) {
if (ev.dataTransfer.items[i].kind !== 'file')
continue;
hasFile = true;
break;
}
}
else {
for (let i = 0; i < ev.dataTransfer.types.length; i++) {
if (ev.dataTransfer.types[i] !== 'Files')
continue;
hasFile = true;
break;
}
}
if (hasFile) {
this.showDragOverlay.next(true);
}
};
}
componentDidMount() {
this.subscribe(this.plugin.layout.events.updated, () => this.forceUpdate());
}
region(kind, Element) {
return (0, jsx_runtime_1.jsx)("div", { className: `msp-layout-region msp-layout-${kind}`, children: (0, jsx_runtime_1.jsx)("div", { className: 'msp-layout-static', children: Element ? (0, jsx_runtime_1.jsx)(Element, {}) : null }) });
}
get layoutVisibilityClassName() {
var _a, _b;
const layout = this.plugin.layout.state;
const controls = (_b = (_a = this.plugin.spec.components) === null || _a === void 0 ? void 0 : _a.controls) !== null && _b !== void 0 ? _b : {};
const classList = [];
if (controls.top === 'none' || !layout.showControls || layout.regionState.top === 'hidden') {
classList.push('msp-layout-hide-top');
}
if (controls.left === 'none' || !layout.showControls || layout.regionState.left === 'hidden') {
classList.push('msp-layout-hide-left');
}
else if (layout.regionState.left === 'collapsed') {
classList.push('msp-layout-collapse-left');
}
if (controls.right === 'none' || !layout.showControls || layout.regionState.right === 'hidden') {
classList.push('msp-layout-hide-right');
}
if (controls.bottom === 'none' || !layout.showControls || layout.regionState.bottom === 'hidden') {
classList.push('msp-layout-hide-bottom');
}
return classList.join(' ');
}
get layoutClassName() {
const layout = this.plugin.layout.state;
const classList = ['msp-plugin-content'];
if (layout.isExpanded) {
classList.push('msp-layout-expanded');
}
else {
classList.push('msp-layout-standard', `msp-layout-standard-${layout.controlsDisplay}`);
}
return classList.join(' ');
}
render() {
var _a, _b, _c, _d, _e, _f, _g;
const layout = this.plugin.layout.state;
const controls = ((_a = this.plugin.spec.components) === null || _a === void 0 ? void 0 : _a.controls) || {};
const viewport = ((_c = (_b = this.plugin.spec.components) === null || _b === void 0 ? void 0 : _b.viewport) === null || _c === void 0 ? void 0 : _c.view) || DefaultViewport;
const sequenceView = ((_e = (_d = this.plugin.spec.components) === null || _d === void 0 ? void 0 : _d.sequenceViewer) === null || _e === void 0 ? void 0 : _e.view) || sequence_1.SequenceView;
return (0, jsx_runtime_1.jsx)("div", { className: 'msp-plugin', children: (0, jsx_runtime_1.jsxs)("div", { className: this.layoutClassName, onDragEnter: this.onDragEnter, children: [(0, jsx_runtime_1.jsxs)("div", { className: this.layoutVisibilityClassName, children: [this.region('main', viewport), layout.showControls && controls.top !== 'none' && this.region('top', controls.top || sequenceView), layout.showControls && controls.left !== 'none' && this.region('left', controls.left || left_panel_1.LeftPanelControls), layout.showControls && controls.right !== 'none' && this.region('right', controls.right || ControlsWrapper), layout.showControls && controls.bottom !== 'none' && this.region('bottom', controls.bottom || Log)] }), !((_f = this.plugin.spec.components) === null || _f === void 0 ? void 0 : _f.hideTaskOverlay) && (0, jsx_runtime_1.jsx)(task_1.OverlayTaskProgress, {}), !((_g = this.plugin.spec.components) === null || _g === void 0 ? void 0 : _g.disableDragOverlay) && (0, jsx_runtime_1.jsx)(DragOverlay, { plugin: this.plugin, showDragOverlay: this.showDragOverlay })] }) });
}
}
function dropFiles(ev, plugin, showDragOverlay) {
ev.preventDefault();
ev.stopPropagation();
showDragOverlay.next(false);
const files = [];
if (ev.dataTransfer.items) {
// Use DataTransferItemList interface to access the file(s)
for (let i = 0; i < ev.dataTransfer.items.length; i++) {
if (ev.dataTransfer.items[i].kind !== 'file')
continue;
const file = ev.dataTransfer.items[i].getAsFile();
if (file)
files.push(file);
}
}
else {
for (let i = 0; i < ev.dataTransfer.files.length; i++) {
const file = ev.dataTransfer.files[i];
if (file)
files.push(file);
}
}
plugin.managers.dragAndDrop.handle(files);
}
function DragOverlay({ plugin, showDragOverlay }) {
const show = (0, use_behavior_1.useBehavior)(showDragOverlay);
const preventDrag = (e) => {
e.dataTransfer.dropEffect = 'copy';
e.preventDefault();
e.stopPropagation();
};
return (0, jsx_runtime_1.jsx)("div", { className: 'msp-drag-drop-overlay', style: { display: show ? 'flex' : 'none' }, onDragEnter: preventDrag, onDragOver: preventDrag, onDragLeave: () => showDragOverlay.next(false), onDrop: e => dropFiles(e, plugin, showDragOverlay), children: "Load File(s)" });
}
class ControlsWrapper extends base_1.PluginUIComponent {
render() {
var _a;
const StructureTools = ((_a = this.plugin.spec.components) === null || _a === void 0 ? void 0 : _a.structureTools) || controls_1.DefaultStructureTools;
return (0, jsx_runtime_1.jsx)("div", { className: 'msp-scrollable-container', children: (0, jsx_runtime_1.jsx)(StructureTools, {}) });
}
}
exports.ControlsWrapper = ControlsWrapper;
class DefaultViewport extends base_1.PluginUIComponent {
render() {
var _a, _b, _c, _d, _e, _f;
const VPControls = ((_b = (_a = this.plugin.spec.components) === null || _a === void 0 ? void 0 : _a.viewport) === null || _b === void 0 ? void 0 : _b.controls) || viewport_1.ViewportControls;
const SVPControls = ((_d = (_c = this.plugin.spec.components) === null || _c === void 0 ? void 0 : _c.selectionTools) === null || _d === void 0 ? void 0 : _d.controls) || controls_1.SelectionViewportControls;
const SnapshotDescription = ((_f = (_e = this.plugin.spec.components) === null || _e === void 0 ? void 0 : _e.viewport) === null || _f === void 0 ? void 0 : _f.snapshotDescription) || controls_1.ViewportSnapshotDescription;
return (0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(viewport_1.Viewport, {}), (0, jsx_runtime_1.jsxs)("div", { className: 'msp-viewport-top-left-controls', children: [(0, jsx_runtime_1.jsx)(controls_1.AnimationViewportControls, {}), (0, jsx_runtime_1.jsx)(controls_1.TrajectoryViewportControls, {}), (0, jsx_runtime_1.jsx)(controls_1.StateSnapshotViewportControls, {}), (0, jsx_runtime_1.jsx)(SnapshotDescription, {})] }), (0, jsx_runtime_1.jsx)(SVPControls, {}), (0, jsx_runtime_1.jsx)(VPControls, {}), (0, jsx_runtime_1.jsx)(task_1.BackgroundTaskProgress, {}), (0, jsx_runtime_1.jsxs)("div", { className: 'msp-highlight-toast-wrapper', children: [(0, jsx_runtime_1.jsx)(controls_1.LociLabels, {}), (0, jsx_runtime_1.jsx)(toast_1.Toasts, {})] })] });
}
}
exports.DefaultViewport = DefaultViewport;
class Log extends base_1.PluginUIComponent {
constructor() {
super(...arguments);
this.wrapper = React.createRef();
this.state = { entries: this.plugin.log.entries };
}
componentDidMount() {
this.subscribe(this.plugin.events.log, () => this.setState({ entries: this.plugin.log.entries }));
}
componentDidUpdate() {
this.scrollToBottom();
}
scrollToBottom() {
const log = this.wrapper.current;
if (log)
log.scrollTop = log.scrollHeight - log.clientHeight - 1;
}
render() {
// TODO: ability to show full log
// showing more entries dramatically slows animations.
const maxEntries = 10;
const xs = this.state.entries, l = xs.size;
const entries = [];
for (let i = Math.max(0, l - maxEntries), o = 0; i < l; i++) {
const e = xs.get(i);
entries.push((0, jsx_runtime_1.jsxs)("li", { children: [(0, jsx_runtime_1.jsx)("div", { className: 'msp-log-entry-badge msp-log-entry-' + e.type }), (0, jsx_runtime_1.jsx)("div", { className: 'msp-log-timestamp', children: (0, mol_util_1.formatTime)(e.timestamp) }), (0, jsx_runtime_1.jsx)("div", { className: 'msp-log-entry', children: e.message })] }, o++));
}
return (0, jsx_runtime_1.jsx)("div", { ref: this.wrapper, className: 'msp-log', style: { position: 'absolute', top: '0', right: '0', bottom: '0', left: '0', overflowY: 'auto' }, children: (0, jsx_runtime_1.jsx)("ul", { className: 'msp-list-unstyled', children: entries }) });
}
}
exports.Log = Log;