123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232 |
- import {asyncScheduler} from "rxjs";
- import {AbstractView, AbstractViewInterface} from "./AbstractView";
- import {
- RcsbFvBoardConfigInterface,
- RcsbFvRowConfigInterface,
- RcsbFv,
- RcsbFvTrackDataElementInterface
- } from "@rcsb/rcsb-saguaro";
- import * as React from "react";
- import {RcsbFvSelectorManager} from "../../RcsbFvSelection/RcsbFvSelectorManager";
- import {
- SaguaroPluginModelMapType,
- SaguaroPluginPublicInterface
- } from "../../RcsbFvStructure/StructurePlugins/SaguaroPluginInterface";
- export type CustomViewStateInterface = Omit<CustomViewInterface, "modelChangeCallback">;
- export interface CustomViewInterface {
- blockConfig: FeatureBlockInterface | Array<FeatureBlockInterface>;
- blockSelectorElement?: (blockSelector: BlockSelectorManager) => JSX.Element;
- modelChangeCallback?: (modelMap: SaguaroPluginModelMapType) => CustomViewStateInterface;
- blockChangeCallback?: (plugin: SaguaroPluginPublicInterface, pfvList: Array<RcsbFv>, selection: RcsbFvSelectorManager) => void;
- }
- export interface FeatureBlockInterface {
- blockId:string;
- blockTitle?: string;
- blockShortName?: string;
- featureViewConfig: Array<FeatureViewInterface> | FeatureViewInterface;
- }
- export interface FeatureViewInterface {
- boardId?:string;
- boardConfig: RcsbFvBoardConfigInterface;
- rowConfig: Array<RcsbFvRowConfigInterface>;
- sequenceSelectionChangeCallback: (plugin: SaguaroPluginPublicInterface, selectorManager: RcsbFvSelectorManager, sequenceRegion: Array<RcsbFvTrackDataElementInterface>) => void;
- sequenceElementClickCallback: (plugin: SaguaroPluginPublicInterface, selectorManager: RcsbFvSelectorManager, d: RcsbFvTrackDataElementInterface) => void;
- sequenceHoverCallback: (plugin: SaguaroPluginPublicInterface, selectorManager: RcsbFvSelectorManager, hoverRegion: Array<RcsbFvTrackDataElementInterface>) => void;
- structureSelectionCallback: (plugin: SaguaroPluginPublicInterface, pfv: RcsbFv, selectorManager: RcsbFvSelectorManager) => void;
- structureHoverCallback: (plugin: SaguaroPluginPublicInterface, pfv: RcsbFv, selectorManager: RcsbFvSelectorManager) => 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 extends AbstractView<CustomViewInterface & AbstractViewInterface, CustomViewStateInterface> {
- private blockViewSelector: BlockSelectorManager = new BlockSelectorManager( this.blockChange.bind(this) );
- private boardMap: Map<string, FeatureViewInterface> = new Map<string, FeatureViewInterface>();
- 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;
- readonly state: CustomViewStateInterface = {
- blockConfig: this.props.blockConfig,
- blockSelectorElement: this.props.blockSelectorElement,
- blockChangeCallback: this.props.blockChangeCallback
- };
- constructor(props: CustomViewInterface & AbstractViewInterface) {
- super(props);
- this.mapBlocks(props.blockConfig);
- }
- 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();
- });
- }
- private mapBlocks(config: FeatureBlockInterface | Array<FeatureBlockInterface>){
- this.blockMap.clear();
- this.boardMap.clear();
- ( config instanceof Array ? config : [config]).forEach(block=>{
- if(block.blockId == null)block.blockId = "block_"+Math.random().toString(36).substr(2);
- 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 = "board_"+Math.random().toString(36).substr(2);
- 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.plugin, Array.from(this.blockMap.get(this.blockViewSelector.getActiveBlock())!.values()).map(boardId=>(this.rcsbFvMap.get(boardId)!)), this.props.selectorManager);
- 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();
- this.props.plugin.unsetCallbacks();
- }
- 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.plugin, this.props.selectorManager, selection);
- },
- highlightHoverCallback:(elements:Array<RcsbFvTrackDataElementInterface>)=>{
- this.boardMap.get(boardId)!.sequenceHoverCallback(this.props.plugin, this.props.selectorManager, elements);
- },
- elementClickCallBack: (element: RcsbFvTrackDataElementInterface)=>{
- this.boardMap.get(boardId)!.sequenceElementClickCallback(this.props.plugin, this.props.selectorManager, element);
- }
- },
- rowConfigData: this.boardMap.get(boardId)!.rowConfig
- });
- this.rcsbFvMap.set(boardId, rcsbFv);
- });
- this.props.plugin.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.plugin, pfv, this.props.selectorManager);
- });
- 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.plugin, pfv, this.props.selectorManager);
- });
- }
- representationChangeCallback(): void{
- //TODO
- }
- additionalContent(): JSX.Element {
- if(this.state.blockSelectorElement == null)
- return <></>;
- return this.state.blockSelectorElement(this.blockViewSelector);
- }
- modelChangeCallback(modelMap:SaguaroPluginModelMapType): void {
- if(this.firstModelLoad){
- this.firstModelLoad = false;
- return;
- }
- if(typeof this.props.modelChangeCallback === "function") {
- let newConfig: CustomViewStateInterface = this.props.modelChangeCallback(modelMap);
- if(newConfig != null ){
- newConfig = newConfig as CustomViewStateInterface;
- 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})
- }
- }
- }
- }
- 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}});
- });
- }
- }
|