123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268 |
- import {asyncScheduler} from "rxjs";
- import {AbstractView, AbstractViewInterface} from "../AbstractView";
- import {
- RcsbFvBoardConfigInterface,
- RcsbFvRowConfigInterface,
- RcsbFv,
- RcsbFvTrackDataElementInterface
- } from "@rcsb/rcsb-saguaro";
- import * as React from "react";
- import {
- StructureViewerPublicInterface, ViewerActionManagerInterface, ViewerCallbackManagerInterface
- } from "../../../RcsbFvStructure/StructureViewerInterface";
- import uniqid from "uniqid";
- import {RcsbFvStateInterface} from "../../../RcsbFvState/RcsbFvStateInterface";
- export type CustomViewStateInterface<R,L> = Omit<Omit<CustomViewInterface<R,L>, "modelChangeCallback">, "structureViewer">;
- export interface CustomViewInterface<R,L> {
- customViewContainerId?: string;
- blockConfig: FeatureBlockInterface<R,L> | Array<FeatureBlockInterface<R,L>>;
- blockSelectorElement?: (blockSelector: BlockSelectorManager) => JSX.Element;
- modelChangeCallback?: () => CustomViewStateInterface<R,L>;
- blockChangeCallback?: (plugin: StructureViewerPublicInterface<R,L>, pfvList: Array<RcsbFv>, stateManager: RcsbFvStateInterface) => void;
- }
- export interface FeatureBlockInterface<R,L> {
- blockId:string;
- blockTitle?: string;
- blockShortName?: string;
- featureViewConfig: Array<FeatureViewInterface<R,L>> | FeatureViewInterface<R,L>;
- }
- export interface FeatureViewInterface<R,L> {
- boardId?:string;
- boardConfig: RcsbFvBoardConfigInterface;
- rowConfig: Array<RcsbFvRowConfigInterface>;
- sequenceSelectionChangeCallback: (plugin: StructureViewerPublicInterface<R,L>, stateManager: RcsbFvStateInterface, sequenceRegion: Array<RcsbFvTrackDataElementInterface>) => void;
- sequenceElementClickCallback: (plugin: StructureViewerPublicInterface<R,L>, stateManager: RcsbFvStateInterface, d: RcsbFvTrackDataElementInterface) => void;
- sequenceHoverCallback: (plugin: StructureViewerPublicInterface<R,L>, stateManager: RcsbFvStateInterface, hoverRegion: Array<RcsbFvTrackDataElementInterface>) => void;
- structureSelectionCallback: (plugin: StructureViewerPublicInterface<R,L>, pfv: RcsbFv, stateManager: RcsbFvStateInterface) => void;
- structureHoverCallback: (plugin: StructureViewerPublicInterface<R,L>, pfv: RcsbFv, stateManager: RcsbFvStateInterface) => void;
- }
- export class BlockSelectorManager {
- private blockId: string;
- private previousBlockId: string;
- private readonly blockChangeCallback: ()=>void = ()=>{};
- constructor(f:()=>void){
- this.blockChangeCallback = f;
- }
- setActiveBlock(blockId:string): void{
- this.previousBlockId = this.blockId;
- this.blockId = blockId;
- this.blockChangeCallback();
- }
- getActiveBlock(): string{
- return this.blockId;
- }
- getPreviousBlock(): string{
- return this.previousBlockId;
- }
- }
- export class CustomView<R,L> extends AbstractView<CustomViewInterface<R,L> & {structureViewer: ViewerActionManagerInterface<R,L>;}, CustomViewStateInterface<R,L>> {
- private blockViewSelector: BlockSelectorManager = new BlockSelectorManager( this.blockChange.bind(this) );
- private boardMap: Map<string, FeatureViewInterface<R,L>> = new Map<string, FeatureViewInterface<R,L>>();
- private blockMap: Map<string, Array<string>> = new Map<string, Array<string>>();
- private rcsbFvMap: Map<string, RcsbFv> = new Map<string, RcsbFv>();
- private firstModelLoad: boolean = true;
- private innerSelectionFlag: boolean = false;
- private updateContext:"state-change"|null = null;
- readonly state: CustomViewStateInterface<R,L> = {
- customViewContainerId: this.props.customViewContainerId,
- blockConfig: this.props.blockConfig,
- blockSelectorElement: this.props.blockSelectorElement,
- blockChangeCallback: this.props.blockChangeCallback
- };
- constructor(props: CustomViewInterface<R,L> & {structureViewer: ViewerActionManagerInterface<R,L>;} & AbstractViewInterface) {
- super(props);
- this.mapBlocks(props.blockConfig);
- }
- render():JSX.Element {
- return (
- this.props.customViewContainerId ? <div id={this.rcsbFvDivId}></div> : super.render()
- );
- }
- componentDidMount(): void {
- super.componentDidMount();
- this.blockViewSelector.setActiveBlock( (this.state.blockConfig instanceof Array ? this.state.blockConfig : [this.state.blockConfig])[0].blockId! );
- }
- componentWillUnmount() {
- super.componentWillUnmount();
- this.rcsbFvMap.forEach((pfv,id)=>{
- pfv.unmount();
- });
- }
- componentDidUpdate(prevProps: Readonly<CustomViewInterface<R,L> & AbstractViewInterface>, prevState: Readonly<CustomViewStateInterface<R,L>>, snapshot?: any) {
- if(this.updateContext != "state-change") {
- this.updateContext = "state-change";
- this.mapBlocks(this.props.blockConfig);
- this.setState( {
- blockConfig: this.props.blockConfig,
- blockSelectorElement: this.props.blockSelectorElement,
- blockChangeCallback: this.props.blockChangeCallback
- });
- }
- }
- private mapBlocks(config: FeatureBlockInterface<R,L> | Array<FeatureBlockInterface<R,L>>){
- this.rcsbFvMap.forEach((pfv, id) => {
- pfv.unmount();
- });
- this.blockMap.clear();
- this.boardMap.clear();
- ( config instanceof Array ? config : [config]).forEach(block=>{
- if(block.blockId == null)block.blockId = uniqid("block_");
- if(!this.blockMap.has(block.blockId))this.blockMap.set(block.blockId, new Array<string>());
- (block.featureViewConfig instanceof Array ? block.featureViewConfig : [block.featureViewConfig]).forEach(board=>{
- if(board.boardId == null)board.boardId = uniqid("board_");
- this.blockMap.get(block.blockId!)?.push(board.boardId);
- this.boardMap.set(board.boardId, board);
- });
- });
- }
- private blockChange(): void{
- this.unmountBlockFv();
- this.buildBlockFv();
- asyncScheduler.schedule(()=>{
- if(typeof this.state.blockChangeCallback === "function")
- this.state.blockChangeCallback(this.props.structureViewer, Array.from(this.blockMap.get(this.blockViewSelector.getActiveBlock())!.values()).map(boardId=>(this.rcsbFvMap.get(boardId)!)), this.props.stateManager);
- else
- this.structureSelectionCallback();
- },1000);
- }
- private unmountBlockFv(){
- this.blockMap.get(this.blockViewSelector.getPreviousBlock())?.forEach(boardId=>{
- if(this.rcsbFvMap.get(boardId) == null)
- return;
- this.rcsbFvMap.get(boardId)!.unmount();
- document.getElementById("boardDiv_"+boardId)?.remove()
- });
- this.rcsbFvMap.clear();
- }
- private buildBlockFv(){
- this.blockMap.get(this.blockViewSelector.getActiveBlock())?.forEach(boardId=>{
- if(this.boardMap.get(boardId) == null)
- return;
- const div: HTMLDivElement = document.createElement<"div">("div");
- div.setAttribute("id", "boardDiv_"+boardId);
- document.getElementById(this.componentDivId)?.append(div);
- const width: number = window.document.getElementById(this.componentDivId)?.getBoundingClientRect().width ?? 0;
- const trackWidth: number = width - (this.boardMap.get(boardId)!.boardConfig?.rowTitleWidth ?? 190) - 55;
- const rcsbFv: RcsbFv = new RcsbFv({
- elementId: "boardDiv_"+boardId,
- boardConfigData:{
- highlightHoverPosition:true,
- highlightHoverElement:true,
- ...this.boardMap.get(boardId)!.boardConfig,
- trackWidth:this.boardMap.get(boardId)!.boardConfig?.trackWidth ? this.boardMap.get(boardId)!.boardConfig?.trackWidth!-4 : trackWidth,
- selectionChangeCallBack:(selection: RcsbFvTrackDataElementInterface[])=>{
- if(this.innerSelectionFlag)
- return;
- this.boardMap.get(boardId)!.sequenceSelectionChangeCallback(this.props.structureViewer, this.props.stateManager, selection);
- },
- highlightHoverCallback:(elements:Array<RcsbFvTrackDataElementInterface>)=>{
- this.boardMap.get(boardId)!.sequenceHoverCallback(this.props.structureViewer, this.props.stateManager, elements);
- },
- elementClickCallBack: (element: RcsbFvTrackDataElementInterface)=>{
- this.boardMap.get(boardId)!.sequenceElementClickCallback(this.props.structureViewer, this.props.stateManager, element);
- }
- },
- rowConfigData: this.boardMap.get(boardId)!.rowConfig
- });
- this.rcsbFvMap.set(boardId, rcsbFv);
- });
- /*this.props.structureViewer.setSelectCallback(()=>{
- this.structureSelectionCallback();
- });*/
- }
- structureSelectionCallback(): void {
- this.innerSelectionFlag = true;
- this.blockMap.get(this.blockViewSelector.getActiveBlock())?.forEach(boardId=>{
- const pfv: RcsbFv | undefined = this.rcsbFvMap.get(boardId);
- if(pfv == null)
- return;
- this.boardMap.get(boardId)?.structureSelectionCallback(this.props.structureViewer, pfv, this.props.stateManager);
- });
- this.innerSelectionFlag = false;
- }
- structureHoverCallback(): void{
- this.blockMap.get(this.blockViewSelector.getActiveBlock())?.forEach(boardId=>{
- const pfv: RcsbFv | undefined = this.rcsbFvMap.get(boardId);
- if(pfv == null)
- return;
- this.boardMap.get(boardId)?.structureHoverCallback(this.props.structureViewer, pfv, this.props.stateManager);
- });
- }
- representationChangeCallback(): void{
- //TODO
- }
- additionalContent(): JSX.Element {
- if(this.state.blockSelectorElement == null)
- return <></>;
- return this.state.blockSelectorElement(this.blockViewSelector);
- }
- modelChangeCallback(): void {
- if(this.firstModelLoad){
- this.firstModelLoad = false;
- return;
- }
- if(typeof this.props.modelChangeCallback === "function") {
- let newConfig: CustomViewStateInterface<R,L> = this.props.modelChangeCallback();
- if(newConfig != null ){
- this.updateContext = "state-change";
- if(newConfig.blockConfig != null && newConfig.blockSelectorElement != null){
- this.mapBlocks(newConfig.blockConfig);
- this.setState({blockConfig: newConfig.blockConfig, blockSelectorElement: newConfig.blockSelectorElement})
- }else if(newConfig.blockConfig == null && newConfig.blockSelectorElement != null){
- this.setState({blockSelectorElement: newConfig.blockSelectorElement})
- }else if(newConfig.blockConfig != null && newConfig.blockSelectorElement == null){
- this.mapBlocks(newConfig.blockConfig);
- this.setState({blockConfig: newConfig.blockConfig})
- }
- }
- }
- }
- setState<K extends keyof CustomViewStateInterface<R,L>>(
- state: ((
- prevState: Readonly<CustomViewStateInterface<R,L>>,
- props: Readonly<CustomViewInterface<R,L> & AbstractViewInterface>
- ) => (Pick<CustomViewStateInterface<R,L>, K> | CustomViewStateInterface<R,L> | null)) | Pick<CustomViewStateInterface<R,L>, K> | CustomViewStateInterface<R,L> | null, callback?: () => void
- ) {
- super.setState(state, ()=>{
- this.blockViewSelector.setActiveBlock( (this.state.blockConfig instanceof Array ? this.state.blockConfig : [this.state.blockConfig])[0].blockId! )
- if(typeof callback === "function") callback();
- this.updateContext = null
- });
- }
- updateDimensions(): void {
- const div: HTMLElement | undefined | null = document.getElementById(this.componentDivId)?.parentElement;
- const width: number = window.document.getElementById(this.componentDivId)?.getBoundingClientRect().width ?? 0;
- if(div == null || (div.style.width && !div.style.width.includes("%")) )
- return;
- this.rcsbFvMap.forEach((rcsbFv, boardId)=>{
- const trackWidth: number = width - (this.boardMap.get(boardId)!.boardConfig?.rowTitleWidth ?? 190) - 55;
- rcsbFv.updateBoardConfig({boardConfigData:{trackWidth:this.boardMap.get(boardId)!.boardConfig?.trackWidth ? this.boardMap.get(boardId)!.boardConfig?.trackWidth!-4 : trackWidth}});
- });
- }
- }
|