import * as React from "react"; import classes from '../styles/RcsbFvStyle.module.scss'; import {StructureViewerInterface} from '../RcsbFvStructure/StructureViewerInterface'; import '../styles/RcsbFvMolstarStyle.module.scss'; import {RcsbFvSequence, RcsbFvSequenceInterface} from "../RcsbFvSequence/RcsbFvSequence"; import {RcsbFvStructure, RcsbFvStructureConfigInterface} from "../RcsbFvStructure/RcsbFvStructure"; import { EventType, RcsbFvContextManager, RcsbFvContextManagerInterface, UpdateConfigInterface } from "../RcsbFvContextManager/RcsbFvContextManager"; import {Subscription} from "rxjs"; import {PluginContext} from "molstar/lib/mol-plugin/context"; import {CSSProperties, MouseEvent} from "react"; import {StructureViewerBehaviourObserverInterface} from "../RcsbFvStructure/StructureViewerBehaviourInterface"; import {RcsbFvStateInterface} from "../RcsbFvState/RcsbFvStateInterface"; import {RcsbFvStateManager} from "../RcsbFvState/RcsbFvStateManager"; export interface RcsbFv3DCssConfig { overwriteCss?: boolean; rootPanel?: CSSProperties; structurePanel?: CSSProperties; sequencePanel?: CSSProperties; } export interface RcsbFv3DComponentInterface { structurePanelConfig:RcsbFvStructureConfigInterface; sequencePanelConfig: RcsbFvSequenceInterface; id: string; ctxManager: RcsbFvContextManager; cssConfig?:RcsbFv3DCssConfig; unmount:(flag:boolean)=>void; fullScreen: boolean; structureViewer: StructureViewerInterface; structureViewerBehaviourObserver: StructureViewerBehaviourObserverInterface; } interface RcsbFv3DComponentState { structurePanelConfig:RcsbFvStructureConfigInterface; sequencePanelConfig:RcsbFvSequenceInterface; pfvScreenFraction: number; } export class RcsbFv3DComponent extends React.Component , RcsbFv3DComponentState> { private readonly stateManager: RcsbFvStateInterface = new RcsbFvStateManager(); private subscription: Subscription; private readonly ROOT_DIV_ID: string = "rootPanelDiv"; readonly state: RcsbFv3DComponentState = { structurePanelConfig: this.props.structurePanelConfig, sequencePanelConfig: this.props.sequencePanelConfig, pfvScreenFraction: 0.55 } render(): JSX.Element { return (
)=>{this.mouseMove(evt)}} onMouseUp={ (e)=>{this.splitPanelMouseUp()} } >
{...this.state.structurePanelConfig} componentId={this.props.id} structureViewer={this.props.structureViewer} stateManager={this.stateManager} structureViewerBehaviourObserver={this.props.structureViewerBehaviourObserver} />
type={this.state.sequencePanelConfig.type} config={this.state.sequencePanelConfig.config} componentId={this.props.id} structureViewer={this.props.structureViewer} stateManager={this.stateManager} title={this.state.sequencePanelConfig.title} subtitle={this.state.sequencePanelConfig.subtitle} unmount={this.props.unmount} />
{ this.panelDelimiter() }
); } componentDidMount() { this.subscription = this.subscribe(); } componentWillUnmount() { this.unsubscribe(); } private useDefaultCss(): boolean { return this.state.sequencePanelConfig.type === "rcsb" || !this.props.cssConfig?.overwriteCss; } private panelDelimiter(): JSX.Element { return this.useDefaultCss() ?
{ this.splitPanelMouseDown() }} className={classes.rcsbFvSplitPanel} style={{right: Math.round((1 - this.state.pfvScreenFraction) * 100) + "%"}} /> : <>; } private structureCssConfig(css: CSSProperties | undefined): CSSProperties{ const widthFr: number = Math.round((1-this.state.pfvScreenFraction)*100); const cssWidth: string = widthFr.toString()+"%"; const cssHeight: string = "100%"; return {...(this.useDefaultCss() ? {width:cssWidth, height:cssHeight, zIndex:100} : {}), ...css }; } private sequenceCssConfig(css: CSSProperties | undefined): CSSProperties{ const widthFr: number = Math.round((this.state.pfvScreenFraction)*100); const cssWidth: string = widthFr.toString()+"%"; const cssHeight: string = "100%"; return {...(this.useDefaultCss() ? {width:cssWidth, height:cssHeight, overflowY:"auto", overflowX:"hidden", paddingBottom:5} : {}), ...css }; } private static mainDivCssConfig(css: CSSProperties | undefined): CSSProperties{ return {...{ }, ...css} } private subscribe(): Subscription{ return this.props.ctxManager.subscribe((obj:RcsbFvContextManagerInterface)=>{ if(obj.eventType == EventType.UPDATE_CONFIG){ this.updateConfig(obj.eventData as UpdateConfigInterface) }else if(obj.eventType == EventType.PLUGIN_CALL){ this.props.structureViewer.pluginCall(obj.eventData as ((f:PluginContext)=>void)); } }); } /**Unsubscribe className to rxjs events. Useful if many panels are created an destroyed.*/ private unsubscribe(): void{ this.subscription.unsubscribe(); } private updateConfig(config:UpdateConfigInterface){ const structureConfig: Partial> | undefined = config.structurePanelConfig; const sequenceConfig: Partial> | undefined = config.sequencePanelConfig; if(structureConfig != null && sequenceConfig != null){ this.setState({structurePanelConfig:{...this.state.structurePanelConfig, ...structureConfig}, sequencePanelConfig:{...this.state.sequencePanelConfig, ...sequenceConfig}}); }else if(structureConfig != null){ this.setState({structurePanelConfig:{...this.state.structurePanelConfig, ...structureConfig}}); }else if(sequenceConfig != null){ this.setState({sequencePanelConfig:{...this.state.sequencePanelConfig, ...sequenceConfig}}); } } private splitPanelMouseDown(): void { const element: HTMLElement | null = document.getElementById(this.ROOT_DIV_ID); if(!element)return; element.style.cursor = "ew-resize"; document.body.classList.add(classes.disableTextSelection); this.resize = (evt: MouseEvent)=>{ const rect: DOMRect | undefined = element.getBoundingClientRect(); const x: number = evt.clientX - rect.left; this.setState({pfvScreenFraction:x/rect.width}); }; } private splitPanelMouseUp(): void { if(typeof this.resize === "function") { const element: HTMLElement | null = document.getElementById(this.ROOT_DIV_ID); if (!element) return; element.style.cursor = "auto"; document.body.classList.remove(classes.disableTextSelection); window.dispatchEvent(new Event('resize')); this.resize = null; } } private mouseMove(evt: MouseEvent): void{ if(typeof this.resize === "function") this.resize(evt); } private resize: null | ((evt: MouseEvent)=>void) = null; }