Browse Source

Extending 1D-3D view to other rcsb-saguaro-app PFV

bioinsilico 3 years ago
parent
commit
200f441597
47 changed files with 752 additions and 542 deletions
  1. 4 0
      CHANGELOG.md
  2. 0 0
      docs/assets/search.js
  3. 0 0
      docs/classes/RcsbFv3DAssembly.RcsbFv3DAssembly-1.html
  4. 0 0
      docs/classes/RcsbFv3DAssembly._internal_.AssemblyModelSate.html
  5. 0 0
      docs/classes/RcsbFv3DAssembly._internal_.BlockSelectorManager.html
  6. 0 0
      docs/classes/RcsbFv3DAssembly._internal_.DataContainer.html
  7. 0 0
      docs/classes/RcsbFv3DAssembly._internal_.PfvAbstractFactory.html
  8. 0 0
      docs/classes/RcsbFv3DAssembly._internal_.RcsbFv3DAbstract.html
  9. 0 0
      docs/classes/RcsbFv3DAssembly._internal_.RcsbFvContextManager.html
  10. 0 0
      docs/classes/RcsbFv3DCustom.RcsbFv3DCustom-1.html
  11. 0 0
      docs/interfaces/RcsbFv3DAssembly.RcsbFv3DAssemblyInterface.html
  12. 0 0
      docs/interfaces/RcsbFv3DAssembly._internal_.AssemblyModelStateInterface.html
  13. 0 0
      docs/interfaces/RcsbFv3DAssembly._internal_.AssemblyViewInterface.html
  14. 0 0
      docs/interfaces/RcsbFv3DAssembly._internal_.CustomViewInterface.html
  15. 0 0
      docs/interfaces/RcsbFv3DAssembly._internal_.FeatureBlockInterface.html
  16. 0 0
      docs/interfaces/RcsbFv3DAssembly._internal_.FeatureViewInterface.html
  17. 0 0
      docs/interfaces/RcsbFv3DAssembly._internal_.PfvFactoryInterface.html
  18. 0 0
      docs/interfaces/RcsbFv3DAssembly._internal_.RcsbFvContextManagerInterface.html
  19. 0 0
      docs/interfaces/RcsbFv3DAssembly._internal_.RcsbFvModulePublicInterface.html
  20. 0 0
      docs/interfaces/RcsbFv3DAssembly._internal_.RcsbFvSequenceInterface.html
  21. 0 0
      docs/interfaces/RcsbFv3DAssembly._internal_.RcsbViewInterface.html
  22. 0 0
      docs/interfaces/RcsbFv3DAssembly._internal_.SaguaroPluginInterface.html
  23. 0 0
      docs/interfaces/RcsbFv3DAssembly._internal_.SaguaroPluginPublicInterface.html
  24. 0 0
      docs/interfaces/RcsbFv3DAssembly._internal_.UpdateConfigInterface.html
  25. 0 0
      docs/modules/RcsbFv3DAssembly._internal_.html
  26. 1 1
      package.json
  27. 4 4
      src/RcsbFv3D/RcsbFv3DAbstract.tsx
  28. 6 5
      src/RcsbFv3D/RcsbFv3DAssembly.tsx
  29. 14 14
      src/RcsbFv3D/RcsbFv3DComponent.tsx
  30. 2 2
      src/RcsbFv3D/RcsbFv3DCustom.tsx
  31. 8 8
      src/RcsbFvContextManager/RcsbFvContextManager.ts
  32. 10 9
      src/RcsbFvSequence/RcsbFvSequence.tsx
  33. 0 489
      src/RcsbFvSequence/SequenceViews/AssemblyView/AssemblyView.tsx
  34. 3 3
      src/RcsbFvSequence/SequenceViews/CustomView/CustomView.tsx
  35. 0 0
      src/RcsbFvSequence/SequenceViews/RcsbView/AssemblyModelSate.ts
  36. 252 0
      src/RcsbFvSequence/SequenceViews/RcsbView/CallbackHelper.ts
  37. 225 0
      src/RcsbFvSequence/SequenceViews/RcsbView/PfvFactoryImplementation/AssemblyPfvFactory.tsx
  38. 1 1
      src/RcsbFvSequence/SequenceViews/RcsbView/PfvFactoryImplementation/ChainDisplay.tsx
  39. 30 0
      src/RcsbFvSequence/SequenceViews/RcsbView/PfvFactoryInterface.ts
  40. 171 0
      src/RcsbFvSequence/SequenceViews/RcsbView/RcsbView.tsx
  41. 15 0
      src/Utils/DataContainer.ts
  42. 1 1
      src/examples/css-config/index.tsx
  43. 1 1
      src/examples/external-mapping/FeatureViewerConfig.ts
  44. 1 1
      src/examples/external-mapping/index.tsx
  45. 1 1
      src/examples/multiple-chain/index.tsx
  46. 1 1
      src/examples/single-chain/index.tsx
  47. 1 1
      src/examples/structural-alignment/index.tsx

+ 4 - 0
CHANGELOG.md

@@ -2,6 +2,10 @@
 
 [Semantic Versioning](https://semver.org/)
 
+## [1.4.0] - 2022-04-20
+### Improvement
+- General 1D panel configuration to extend the 1D-3D visualization to any rcsb-saguaro-app PFV builder
+
 ## [1.3.10] - 2022-04-14
 ### Dependency update
 - rcsb-api-tools v4.0.1

File diff suppressed because it is too large
+ 0 - 0
docs/assets/search.js


File diff suppressed because it is too large
+ 0 - 0
docs/classes/RcsbFv3DAssembly.RcsbFv3DAssembly-1.html


File diff suppressed because it is too large
+ 0 - 0
docs/classes/RcsbFv3DAssembly._internal_.AssemblyModelSate.html


File diff suppressed because it is too large
+ 0 - 0
docs/classes/RcsbFv3DAssembly._internal_.BlockSelectorManager.html


File diff suppressed because it is too large
+ 0 - 0
docs/classes/RcsbFv3DAssembly._internal_.DataContainer.html


File diff suppressed because it is too large
+ 0 - 0
docs/classes/RcsbFv3DAssembly._internal_.PfvAbstractFactory.html


File diff suppressed because it is too large
+ 0 - 0
docs/classes/RcsbFv3DAssembly._internal_.RcsbFv3DAbstract.html


File diff suppressed because it is too large
+ 0 - 0
docs/classes/RcsbFv3DAssembly._internal_.RcsbFvContextManager.html


File diff suppressed because it is too large
+ 0 - 0
docs/classes/RcsbFv3DCustom.RcsbFv3DCustom-1.html


File diff suppressed because it is too large
+ 0 - 0
docs/interfaces/RcsbFv3DAssembly.RcsbFv3DAssemblyInterface.html


File diff suppressed because it is too large
+ 0 - 0
docs/interfaces/RcsbFv3DAssembly._internal_.AssemblyModelStateInterface.html


File diff suppressed because it is too large
+ 0 - 0
docs/interfaces/RcsbFv3DAssembly._internal_.AssemblyViewInterface.html


File diff suppressed because it is too large
+ 0 - 0
docs/interfaces/RcsbFv3DAssembly._internal_.CustomViewInterface.html


File diff suppressed because it is too large
+ 0 - 0
docs/interfaces/RcsbFv3DAssembly._internal_.FeatureBlockInterface.html


File diff suppressed because it is too large
+ 0 - 0
docs/interfaces/RcsbFv3DAssembly._internal_.FeatureViewInterface.html


File diff suppressed because it is too large
+ 0 - 0
docs/interfaces/RcsbFv3DAssembly._internal_.PfvFactoryInterface.html


File diff suppressed because it is too large
+ 0 - 0
docs/interfaces/RcsbFv3DAssembly._internal_.RcsbFvContextManagerInterface.html


File diff suppressed because it is too large
+ 0 - 0
docs/interfaces/RcsbFv3DAssembly._internal_.RcsbFvModulePublicInterface.html


File diff suppressed because it is too large
+ 0 - 0
docs/interfaces/RcsbFv3DAssembly._internal_.RcsbFvSequenceInterface.html


File diff suppressed because it is too large
+ 0 - 0
docs/interfaces/RcsbFv3DAssembly._internal_.RcsbViewInterface.html


File diff suppressed because it is too large
+ 0 - 0
docs/interfaces/RcsbFv3DAssembly._internal_.SaguaroPluginInterface.html


File diff suppressed because it is too large
+ 0 - 0
docs/interfaces/RcsbFv3DAssembly._internal_.SaguaroPluginPublicInterface.html


File diff suppressed because it is too large
+ 0 - 0
docs/interfaces/RcsbFv3DAssembly._internal_.UpdateConfigInterface.html


File diff suppressed because it is too large
+ 0 - 0
docs/modules/RcsbFv3DAssembly._internal_.html


+ 1 - 1
package.json

@@ -1,6 +1,6 @@
 {
   "name": "@rcsb/rcsb-saguaro-3d",
-  "version": "1.3.10",
+  "version": "1.4.0",
   "description": "RCSB Molstar/Saguaro Web App",
   "main": "build/dist/app.js",
   "files": [

+ 4 - 4
src/RcsbFv3D/RcsbFv3DAbstract.tsx

@@ -12,12 +12,12 @@ export interface RcsbFv3DAbstractInterface {
     cssConfig?: RcsbFv3DCssConfig;
 }
 
-export abstract class RcsbFv3DAbstract {
+export abstract class RcsbFv3DAbstract<T extends {}> {
 
     protected elementId: string;
     protected structureConfig: RcsbFvStructureInterface;
-    protected sequenceConfig: RcsbFvSequenceInterface;
-    protected ctxManager: RcsbFvContextManager = new RcsbFvContextManager();
+    protected sequenceConfig: RcsbFvSequenceInterface<T>;
+    protected ctxManager: RcsbFvContextManager<T> = new RcsbFvContextManager<T>();
     private fullScreenFlag: boolean = false;
     private overflowStyle: string = "";
     protected cssConfig:{
@@ -69,7 +69,7 @@ export abstract class RcsbFv3DAbstract {
         }
     }
 
-    public updateConfig(config: {structurePanelConfig?: Partial<RcsbFvStructureInterface>; sequencePanelConfig?: Partial<RcsbFvSequenceInterface>;}){
+    public updateConfig(config: {structurePanelConfig?: Partial<RcsbFvStructureInterface>; sequencePanelConfig?: Partial<RcsbFvSequenceInterface<T>>;}){
         this.ctxManager.next({eventType: EventType.UPDATE_CONFIG, eventData:config});
     }
 

+ 6 - 5
src/RcsbFv3D/RcsbFv3DAssembly.tsx

@@ -4,6 +4,7 @@ import {RcsbRepresentationPreset} from "../RcsbFvStructure/StructurePlugins/Stru
 import {RcsbFvAdditionalConfig} from "@rcsb/rcsb-saguaro-app/build/dist/RcsbFvWeb/RcsbFvModule/RcsbFvModuleInterface";
 import {InstanceSequenceConfig} from "@rcsb/rcsb-saguaro-app/build/dist/RcsbFvWeb/RcsbFvBuilder/RcsbFvInstanceBuilder";
 import {OperatorInfo} from "../RcsbFvStructure/SaguaroPluginInterface";
+import {AssemblyPfvFactory} from "../RcsbFvSequence/SequenceViews/RcsbView/PfvFactoryImplementation/AssemblyPfvFactory";
 
 type RcsbFv3DAssemblyAdditionalConfig = RcsbFvAdditionalConfig & {operatorChangeCallback?:(operatorInfo: OperatorInfo)=>void};
 export interface RcsbFv3DAssemblyInterface extends RcsbFv3DAbstractInterface {
@@ -18,14 +19,13 @@ export interface RcsbFv3DAssemblyInterface extends RcsbFv3DAbstractInterface {
    useOperatorsFlag?:boolean;
 }
 
-export class RcsbFv3DAssembly extends RcsbFv3DAbstract{
+export class RcsbFv3DAssembly extends RcsbFv3DAbstract<{instanceSequenceConfig?: InstanceSequenceConfig}>{
 
     constructor(config?: RcsbFv3DAssemblyInterface) {
             super(config);
     }
 
     init(assemblyData: RcsbFv3DAssemblyInterface) {
-        console.log(assemblyData);
         this.elementId = assemblyData.elementId ?? "RcsbFv3D_mainDiv_"+Math.random().toString(36).substring(2);
         this.structureConfig = {
             loadConfig: {
@@ -43,11 +43,12 @@ export class RcsbFv3DAssembly extends RcsbFv3DAbstract{
             }
         };
         this.sequenceConfig = {
-            type:"assembly",
+            type:"rcsb",
             config: {
-                entryId:assemblyData.config.entryId,
+                rcsbId:assemblyData.config.entryId,
                 additionalConfig: assemblyData.additionalConfig,
-                instanceSequenceConfig: assemblyData.instanceSequenceConfig,
+                pfvFactory:AssemblyPfvFactory,
+                pfvParams:{instanceSequenceConfig: assemblyData.instanceSequenceConfig},
                 useOperatorsFlag: assemblyData.useOperatorsFlag
             },
             title: assemblyData.config.title,

+ 14 - 14
src/RcsbFv3D/RcsbFv3DComponent.tsx

@@ -25,36 +25,36 @@ export interface RcsbFv3DCssConfig {
     sequencePanel?: CSSProperties;
 }
 
-export interface RcsbFv3DComponentInterface {
+export interface RcsbFv3DComponentInterface<T extends {}> {
     structurePanelConfig:RcsbFvStructureInterface;
-    sequencePanelConfig: RcsbFvSequenceInterface;
+    sequencePanelConfig: RcsbFvSequenceInterface<T>;
     id: string;
-    ctxManager: RcsbFvContextManager;
+    ctxManager: RcsbFvContextManager<T>;
     cssConfig?:RcsbFv3DCssConfig;
     unmount:(flag:boolean)=>void;
     fullScreen: boolean;
 }
 
-interface RcsbFv3DComponentState {
+interface RcsbFv3DComponentState<T extends {}> {
     structurePanelConfig:RcsbFvStructureInterface;
-    sequencePanelConfig:RcsbFvSequenceInterface;
+    sequencePanelConfig:RcsbFvSequenceInterface<T>;
     pfvScreenFraction: number;
 }
 
-export class RcsbFv3DComponent extends React.Component <RcsbFv3DComponentInterface, RcsbFv3DComponentState> {
+export class RcsbFv3DComponent<T extends {}> extends React.Component <RcsbFv3DComponentInterface<T>, RcsbFv3DComponentState<T>> {
 
     private readonly plugin: SaguaroPluginInterface;
     private readonly selectorManager: RcsbFvSelectorManager = new RcsbFvSelectorManager();
     private subscription: Subscription;
     private readonly ROOT_DIV_ID: string = "rootPanelDiv";
 
-    readonly state: RcsbFv3DComponentState = {
+    readonly state: RcsbFv3DComponentState<T> = {
         structurePanelConfig: this.props.structurePanelConfig,
         sequencePanelConfig: this.props.sequencePanelConfig,
         pfvScreenFraction: 0.55
     }
 
-    constructor(props: RcsbFv3DComponentInterface) {
+    constructor(props: RcsbFv3DComponentInterface<T>) {
         super(props);
         this.plugin = new MolstarPlugin(this.selectorManager);
     }
@@ -78,7 +78,7 @@ export class RcsbFv3DComponent extends React.Component <RcsbFv3DComponentInterfa
                         />
                     </div>
                     <div style={this.sequenceCssConfig(this.props.cssConfig?.sequencePanel)}  >
-                        <RcsbFvSequence
+                        <RcsbFvSequence<T>
                             type={this.state.sequencePanelConfig.type}
                             config={this.state.sequencePanelConfig.config}
                             componentId={this.props.id}
@@ -106,7 +106,7 @@ export class RcsbFv3DComponent extends React.Component <RcsbFv3DComponentInterfa
     }
 
     private useDefaultCss(): boolean {
-       return this.state.sequencePanelConfig.type === "assembly"  || !this.props.cssConfig?.overwriteCss;
+       return this.state.sequencePanelConfig.type === "rcsb"  || !this.props.cssConfig?.overwriteCss;
     }
 
     private panelDelimiter(): JSX.Element {
@@ -140,9 +140,9 @@ export class RcsbFv3DComponent extends React.Component <RcsbFv3DComponentInterfa
     }
 
     private subscribe(): Subscription{
-        return this.props.ctxManager.subscribe((obj:RcsbFvContextManagerInterface)=>{
+        return this.props.ctxManager.subscribe((obj:RcsbFvContextManagerInterface<T>)=>{
             if(obj.eventType == EventType.UPDATE_CONFIG){
-                this.updateConfig(obj.eventData as UpdateConfigInterface)
+                this.updateConfig(obj.eventData as UpdateConfigInterface<T>)
             }else if(obj.eventType == EventType.PLUGIN_CALL){
                 this.plugin.pluginCall(obj.eventData as ((f:PluginContext)=>void));
             }
@@ -154,9 +154,9 @@ export class RcsbFv3DComponent extends React.Component <RcsbFv3DComponentInterfa
         this.subscription.unsubscribe();
     }
 
-    private updateConfig(config:UpdateConfigInterface){
+    private updateConfig(config:UpdateConfigInterface<T>){
         const structureConfig: Partial<RcsbFvStructureInterface> | undefined = config.structurePanelConfig;
-        const sequenceConfig: Partial<RcsbFvSequenceInterface> | undefined = config.sequencePanelConfig;
+        const sequenceConfig: Partial<RcsbFvSequenceInterface<T>> | undefined = config.sequencePanelConfig;
         if(structureConfig != null && sequenceConfig != null){
             this.setState({structurePanelConfig:{...this.state.structurePanelConfig, ...structureConfig}, sequencePanelConfig:{...this.state.sequencePanelConfig, ...sequenceConfig}});
         }else if(structureConfig != null){

+ 2 - 2
src/RcsbFv3D/RcsbFv3DCustom.tsx

@@ -1,6 +1,6 @@
 
 import {RcsbFvStructureInterface} from "../RcsbFvStructure/RcsbFvStructure";
-import {CustomViewInterface} from "../RcsbFvSequence/SequenceViews/CustomView";
+import {CustomViewInterface} from "../RcsbFvSequence/SequenceViews/CustomView/CustomView";
 import {RcsbFv3DAbstract, RcsbFv3DAbstractInterface} from "./RcsbFv3DAbstract";
 
 export interface RcsbFv3DCustomInterface extends RcsbFv3DAbstractInterface {
@@ -12,7 +12,7 @@ export interface RcsbFv3DCustomInterface extends RcsbFv3DAbstractInterface {
     };
 }
 
-export class RcsbFv3DCustom extends RcsbFv3DAbstract {
+export class RcsbFv3DCustom extends RcsbFv3DAbstract<{}> {
 
     constructor(config?: RcsbFv3DCustomInterface) {
         super(config);

+ 8 - 8
src/RcsbFvContextManager/RcsbFvContextManager.ts

@@ -4,9 +4,9 @@ import {RcsbFvSequenceInterface} from "../RcsbFvSequence/RcsbFvSequence";
 import {PluginContext} from "molstar/lib/mol-plugin/context";
 
 /**Main Event Data Object Interface*/
-export interface RcsbFvContextManagerInterface {
+export interface RcsbFvContextManagerInterface<T extends {}> {
     eventType: EventType;
-    eventData: string | UpdateConfigInterface | ((plugin: PluginContext) => void);
+    eventData: string | UpdateConfigInterface<T> | ((plugin: PluginContext) => void);
 }
 
 /**Event types*/
@@ -15,24 +15,24 @@ export enum EventType {
     PLUGIN_CALL = "pluginCall"
 }
 
-export interface UpdateConfigInterface {
+export interface UpdateConfigInterface<T extends {}> {
     structurePanelConfig?:Partial<RcsbFvStructureInterface>;
-    sequencePanelConfig?:Partial<RcsbFvSequenceInterface>;
+    sequencePanelConfig?:Partial<RcsbFvSequenceInterface<T>>;
 }
 
 /**rxjs Event Handler Object. It allows objects to subscribe methods and then, get(send) events to(from) other objects*/
-export class RcsbFvContextManager {
-    private readonly subject: Subject<RcsbFvContextManagerInterface> = new Subject<RcsbFvContextManagerInterface>();
+export class RcsbFvContextManager<T extends {}> {
+    private readonly subject: Subject<RcsbFvContextManagerInterface<T>> = new Subject<RcsbFvContextManagerInterface<T>>();
     /**Call other subscribed methods
      * @param obj Event Data Structure Interface
      * */
-    public next( obj: RcsbFvContextManagerInterface ):void {
+    public next( obj: RcsbFvContextManagerInterface<T> ):void {
         this.subject.next(obj);
     }
     /**Subscribe loadMethod
      * @return Subscription
      * */
-    public subscribe(f:(x:RcsbFvContextManagerInterface)=>void):Subscription {
+    public subscribe(f:(x:RcsbFvContextManagerInterface<T>)=>void):Subscription {
         return this.subject.asObservable().subscribe(f);
     }
     /**Unsubscribe all methods*/

+ 10 - 9
src/RcsbFvSequence/RcsbFvSequence.tsx

@@ -1,14 +1,14 @@
 import * as React from "react";
-import {AssemblyView, AssemblyViewInterface} from "./SequenceViews/AssemblyView/AssemblyView";
-import {CustomView, CustomViewInterface} from "./SequenceViews/CustomView";
+import {CustomView, CustomViewInterface} from "./SequenceViews/CustomView/CustomView";
 import {SaguaroPluginInterface} from "../RcsbFvStructure/SaguaroPluginInterface";
 import {PluginContext} from "molstar/lib/mol-plugin/context";
 import {RcsbFv, RcsbFvTrackDataElementInterface} from "@rcsb/rcsb-saguaro";
 import {RcsbFvSelectorManager} from "../RcsbFvSelection/RcsbFvSelectorManager";
+import {RcsbView, RcsbViewInterface} from "./SequenceViews/RcsbView/RcsbView";
 
-export interface RcsbFvSequenceInterface{
-    type: "custom" | "assembly";
-    config: AssemblyViewInterface | CustomViewInterface;
+export interface RcsbFvSequenceInterface<T extends {}>{
+    type: "custom" | "rcsb";
+    config: RcsbViewInterface<T> | CustomViewInterface;
     title?: string;
     subtitle?: string;
 }
@@ -18,7 +18,7 @@ interface CallbackConfig {
     sequenceCallback?: (rcsbFv: RcsbFv)=>void;
 }
 
-export class RcsbFvSequence extends React.Component <RcsbFvSequenceInterface & CallbackConfig & {unmount:(flag:boolean)=>void, plugin: SaguaroPluginInterface, selectorManager:RcsbFvSelectorManager, componentId:string}, RcsbFvSequenceInterface > {
+export class RcsbFvSequence<T extends {}> extends React.Component <RcsbFvSequenceInterface<T> & CallbackConfig & {unmount:(flag:boolean)=>void, plugin: SaguaroPluginInterface, selectorManager:RcsbFvSelectorManager, componentId:string}, RcsbFvSequenceInterface<T> > {
 
     render() {
         if(this.props.type == "custom"){
@@ -32,9 +32,9 @@ export class RcsbFvSequence extends React.Component <RcsbFvSequenceInterface & C
                 subtitle={this.props.subtitle}
                 unmount={this.props.unmount}
             />)
-        }else if(this.props.type == "assembly"){
-            const config: AssemblyViewInterface = this.props.config as AssemblyViewInterface;
-            return (<AssemblyView
+        }else if(this.props.type == "rcsb"){
+            const config: RcsbViewInterface<T> = this.props.config as RcsbViewInterface<T>;
+            return (<RcsbView<T>
                 {...config}
                 componentId={this.props.componentId}
                 plugin={this.props.plugin}
@@ -44,6 +44,7 @@ export class RcsbFvSequence extends React.Component <RcsbFvSequenceInterface & C
                 unmount={this.props.unmount}
             />)
         }
+
     }
 
 }

+ 0 - 489
src/RcsbFvSequence/SequenceViews/AssemblyView/AssemblyView.tsx

@@ -1,489 +0,0 @@
-import {asyncScheduler} from "rxjs";
-import * as React from "react";
-
-import {RcsbFvDOMConstants} from "../../../RcsbFvConstants/RcsbFvConstants";
-import {buildInstanceSequenceFv, FeatureType, RcsbRequestContextManager, RcsbFvUI, unmount} from "@rcsb/rcsb-saguaro-app";
-import {AbstractView, AbstractViewInterface} from "../AbstractView";
-import {
-    InstanceSequenceConfig,
-    InstanceSequenceOnchangeInterface
-} from "@rcsb/rcsb-saguaro-app/build/dist/RcsbFvWeb/RcsbFvBuilder/RcsbFvInstanceBuilder";
-import {RcsbFvBoardConfigInterface, RcsbFvTrackDataElementInterface} from "@rcsb/rcsb-saguaro";
-import {
-    ChainInfo,
-    OperatorInfo,
-    SaguaroPluginInterface,
-    SaguaroPluginModelMapType,
-    SaguaroRange,
-    SaguaroRegionList
-} from "../../../RcsbFvStructure/SaguaroPluginInterface";
-
-import {ChainDisplay} from "./ChainDisplay";
-
-import {
-    RcsbFvAdditionalConfig,
-    RcsbFvModulePublicInterface
-} from "@rcsb/rcsb-saguaro-app/build/dist/RcsbFvWeb/RcsbFvModule/RcsbFvModuleInterface";
-import {AnnotationFeatures, Source, Type} from "@rcsb/rcsb-api-tools/build/RcsbGraphQL/Types/Borrego/GqlTypes";
-import {PolymerEntityInstanceInterface} from "@rcsb/rcsb-saguaro-app/build/dist/RcsbCollectTools/DataCollectors/PolymerEntityInstancesCollector";
-import {InterfaceInstanceTranslate} from "@rcsb/rcsb-saguaro-app/build/dist/RcsbUtils/Translators/InterfaceInstanceTranslate";
-import {AssemblyModelSate} from "./AssemblyModelSate";
-import {SelectOptionProps} from "@rcsb/rcsb-saguaro-app/build/dist/RcsbFvWeb/WebTools/SelectButton";
-
-export interface AssemblyViewInterface {
-    entryId: string;
-    additionalConfig?: RcsbFvAdditionalConfig & {operatorChangeCallback?:(operatorInfo: OperatorInfo)=>void};
-    instanceSequenceConfig?: InstanceSequenceConfig;
-    useOperatorsFlag?:boolean;
-}
-
-export class AssemblyView extends AbstractView<AssemblyViewInterface & AbstractViewInterface, {}>{
-
-    private readonly assemblyModelSate: AssemblyModelSate = new AssemblyModelSate();
-    private createComponentThreshold: number = 3;
-    private innerSelectionFlag: boolean = false;
-    private currentSelectedComponentId: string;
-    private boardConfig: Partial<RcsbFvBoardConfigInterface>;
-    private rcsbFvModule: RcsbFvModulePublicInterface | null;
-    private OPERATOR_DROPDOWN_TITLE: string = "Symmetry Partner";
-    //private readonly componentSet = new Map<string, {current: Set<string>, previous: Set<string>}>();
-
-    additionalContent(): JSX.Element {
-        return (
-            <div style={{marginTop:10}}>
-                <div>
-                    <div id={RcsbFvDOMConstants.SELECT_INSTANCE_PFV_ID} style={{display:"inline-block"}}/>
-                    <div style={{display:"inline-block", marginLeft:25}}>
-                        <a href={"/docs/sequence-viewers/protein-feature-view"} target={"_blank"}>Help</a>
-                    </div>
-                </div>
-                <div style={{position:"absolute", top:5, right:5}} >
-                    <a style={{textDecoration:"none", color:"#337ab7", cursor:"pointer", marginRight:15}} target={"_blank"} href={"/docs/sequence-viewers/3d-protein-feature-view"}>
-                        Help
-                    </a>
-                    <a style={{textDecoration:"none", color: "#337ab7", cursor:"pointer"}} onClick={()=>{this.props.unmount(true, ()=>{
-                        window.history.back();
-                    })}}>
-                        Back
-                    </a>
-                </div>
-            </div>
-        );
-    }
-
-    componentDidMount (): void {
-        super.componentDidMount();
-        const width: number | undefined = document.getElementById(this.componentDivId)?.getBoundingClientRect().width;
-        if(width == null)
-            return;
-        const trackWidth: number = width - 190 - 55;
-        this.boardConfig = {
-            ...this.props.additionalConfig?.boardConfig,
-            trackWidth: trackWidth,
-            highlightHoverPosition:true,
-            highlightHoverElement:true,
-            elementClickCallBack:(e:RcsbFvTrackDataElementInterface)=>{
-                this.elementClickCallback(e);
-                if(typeof this.props.additionalConfig?.boardConfig?.elementClickCallBack === "function")
-                    this.props.additionalConfig?.boardConfig.elementClickCallBack(e);
-            },
-            selectionChangeCallBack:(selection: Array<RcsbFvTrackDataElementInterface>)=>{
-                this.selectionChangeCallback(selection);
-                if(typeof this.props.additionalConfig?.boardConfig?.selectionChangeCallBack === "function")
-                    this.props.additionalConfig?.boardConfig.selectionChangeCallBack(selection);
-            },
-            highlightHoverCallback:(selection: RcsbFvTrackDataElementInterface[])=>{
-                this.highlightHoverCallback(selection);
-                if(typeof this.props.additionalConfig?.boardConfig?.highlightHoverCallback === "function")
-                    this.props.additionalConfig?.boardConfig.highlightHoverCallback(selection);
-            },
-        };
-    }
-
-    componentWillUnmount() {
-        super.componentWillUnmount();
-        unmount(this.rcsbFvDivId);
-    }
-
-    async structureSelectionCallback(): Promise<void> {
-        await this.pluginSelectCallback('select');
-    }
-
-    async structureHoverCallback(): Promise<void> {
-        await this.pluginSelectCallback('hover');
-    }
-
-    representationChangeCallback(): void{
-        //TODO
-    }
-
-    async updateDimensions(): Promise<void> {
-        const width: number = window.document.getElementById(this.componentDivId)?.getBoundingClientRect().width ?? 0;
-        const trackWidth: number = width - 190 - 55;
-        this.boardConfig.trackWidth = trackWidth;
-        await this.rcsbFvModule?.getFv().updateBoardConfig({boardConfigData:{trackWidth:trackWidth}})
-        await this.structureSelectionCallback();
-        return void 0;
-    }
-
-    private resetPluginView(): void {
-        this.props.plugin.clearFocus();
-        this.props.plugin.resetCamera();
-    }
-
-    private async pluginSelectCallback(mode:'select'|'hover'): Promise<void> {
-        if(this.rcsbFvModule == null)
-            return;
-        this.innerSelectionFlag = true;
-        if(mode === 'select' && this.currentSelectedComponentId != null){
-            this.props.plugin.removeComponent(this.currentSelectedComponentId);
-        }
-        const allSel: Array<SaguaroRegionList> | undefined = this.props.selectorManager.getSelection(mode);
-        if(allSel == null || allSel.length ===0) {
-            this.rcsbFvModule?.getFv().clearSelection(mode);
-            if(mode === 'select')
-                this.resetPluginView();
-        }else if(
-            mode === 'select' &&
-            this.props.selectorManager.getLastSelection('select')?.labelAsymId != null &&
-            this.props.selectorManager.getLastSelection('select')?.labelAsymId != this.assemblyModelSate.getString("labelAsymId")
-        ){
-            const authId: string | undefined = this.assemblyModelSate.getChainInfo(this.props.selectorManager.getLastSelection('select')?.labelAsymId!)?.auth;
-            await this.modelChangeCallback(this.assemblyModelSate.getMap(), authId, this.props.selectorManager.getLastSelection('select')?.operatorName);
-        }else if(
-            mode === 'select' &&
-            this.props.selectorManager.getLastSelection('select')?.labelAsymId != null &&
-            this.props.selectorManager.getLastSelection('select')?.operatorName != null &&
-            this.props.selectorManager.getLastSelection('select')?.operatorName != this.assemblyModelSate.getOperator()?.name
-        ){
-            const authId: string | undefined = this.assemblyModelSate.getChainInfo(this.props.selectorManager.getLastSelection('select')?.labelAsymId!)?.auth;
-            await this.modelChangeCallback(this.assemblyModelSate.getMap(), authId, this.props.selectorManager.getLastSelection('select')?.operatorName);
-        }else{
-            if(mode === 'select' && this.props.selectorManager.getLastSelection('select')?.operatorName && this.props.selectorManager.getLastSelection('select')?.operatorName != this.assemblyModelSate.getOperator()?.name)
-                this.addOperatorButton(this.props.selectorManager.getLastSelection('select')?.operatorName);
-            const sel: SaguaroRegionList | undefined = this.props.selectorManager.getSelectionWithCondition(
-                this.assemblyModelSate.getString("modelId"),
-                this.assemblyModelSate.getString("labelAsymId"),
-                mode,
-                this.assemblyModelSate.getOperator()?.name
-            );
-            if (sel == null) {
-                this.rcsbFvModule?.getFv().clearSelection(mode);
-                if(mode === 'select')
-                    this.resetPluginView();
-            } else {
-                this.rcsbFvModule?.getFv().setSelection({elements: sel.regions, mode: mode});
-            }
-        }
-        this.innerSelectionFlag = false;
-    }
-
-    async modelChangeCallback(modelMap:SaguaroPluginModelMapType, defaultAuthId?: string, defaultOperatorName?:string): Promise<void> {
-        this.assemblyModelSate.setMap(modelMap);
-        this.props.plugin.clearFocus();
-        const onChangeCallback: Map<string, (x: InstanceSequenceOnchangeInterface)=>void> = new Map<string, (x: InstanceSequenceOnchangeInterface) => {}>();
-        const assemblyInstances: Map<string, Set<string>> = new Map<string, Set<string>>();
-        this.assemblyModelSate.forEach((v,k)=>{
-            assemblyInstances.set(v.entryId,new Set<string>(v.chains.map(d=>d.label)));
-            onChangeCallback.set(v.entryId,(x)=>{
-                this.assemblyModelSate.set({entryId: v.entryId, labelAsymId: x.asymId, modelId: k});
-                asyncScheduler.schedule(()=>{
-                    this.props.selectorManager.setLastSelection('select', null);
-                    this.instanceChangeCallback();
-                },1000);
-            });
-        });
-        this.unmountRcsbFv();
-        const operatorNameContainer: {operatorName?:string} = {operatorName: defaultOperatorName};
-        if(this.assemblyModelSate.get("entryId") != null) {
-            this.rcsbFvModule = await buildInstanceSequenceFv(
-                this.rcsbFvDivId,
-                RcsbFvDOMConstants.SELECT_INSTANCE_PFV_ID,
-                this.assemblyModelSate.getString("entryId"),
-                {
-                    ...this.props.instanceSequenceConfig,
-                    defaultValue: defaultAuthId,
-                    onChangeCallback: onChangeCallback.get(this.assemblyModelSate.getString("entryId")),
-                    beforeChangeCallback: (x: InstanceSequenceOnchangeInterface)=>{
-                        this.assemblyModelSate.set({entryId:x.pdbId, labelAsymId: x.asymId});
-                        const entryMap:[string, {entryId: string, assemblyId: string, chains: ChainInfo[]}] | undefined = Array.from(this.assemblyModelSate.entries()).find((e)=>(e[1].entryId === x.pdbId));
-                        if(!entryMap){
-                            throw `Error: no modelId was found for ${x.pdbId}`;
-                        }
-                        const operator: OperatorInfo|undefined = getOperator(this.assemblyModelSate.getMap().get(entryMap[0])!, defaultAuthId, operatorNameContainer.operatorName);
-                        this.addOperatorButton(operator?.name);
-                        this.assemblyModelSate.setOperator(x.asymId,operator?.name);
-                        operatorNameContainer.operatorName = undefined;
-                        if(typeof this.props.additionalConfig?.operatorChangeCallback === "function" && this.assemblyModelSate.getOperator()){
-                                this.props.additionalConfig.operatorChangeCallback(this.assemblyModelSate.getOperator()!);
-                        }
-                        if((this.assemblyModelSate.getChainInfo()?.operators?.length ?? 0) > 1)
-                            return {
-                                operatorIds: operator?.ids
-                            }
-                    },
-                    filterInstances: assemblyInstances.get(this.assemblyModelSate.getString("entryId")),
-                    selectButtonOptionProps: (props: SelectOptionProps) => (
-                        <div style={{display: 'flex'}}>
-                            <ChainDisplay plugin={this.props.plugin} label={props.data.label}/>
-                            {props.children}
-                        </div>)
-                },
-                {
-                    ...this.props.additionalConfig,
-                    boardConfig: this.boardConfig,
-                    externalTrackBuilder:{
-                        filterFeatures: this.filterFeatures.bind(this)
-                    }
-                }
-            );
-        }
-        if(!defaultAuthId)
-            await createComponents(this.props.plugin, this.assemblyModelSate.getMap());
-    }
-
-    private async instanceChangeCallback(): Promise<void>{
-        this.resetPluginView();
-        await this.pluginSelectCallback('select');
-    }
-
-    private addOperatorButton(operatorName?: string): void{
-        const currentChainInfo: ChainInfo|undefined = this.assemblyModelSate.getChainInfo();
-        if(this.props.useOperatorsFlag && currentChainInfo && currentChainInfo.operators.length >1 ){
-            this.assemblyModelSate.setOperator(undefined,operatorName);
-            RcsbFvUI.addSelectButton(
-                this.rcsbFvDivId,
-                RcsbFvDOMConstants.SELECT_INSTANCE_PFV_ID,
-                currentChainInfo.operators.map(op=>({
-                    label:`${op.ids.join("-")} (${op.name})`,
-                    optId:op.name,
-                    onChange: async ()=>{
-                        this.assemblyModelSate.set({operator:op});
-                        await this.modelChangeCallback(
-                            this.assemblyModelSate.getMap(),
-                            this.assemblyModelSate.getChainInfo()?.auth,
-                            op.name
-                        )
-                    }
-                })),
-                {
-                    defaultValue: this.assemblyModelSate.getOperator()?.name,
-                    dropdownTitle:this.OPERATOR_DROPDOWN_TITLE
-                }
-            );
-        }
-    }
-
-    private unmountRcsbFv(): void {
-        this.rcsbFvModule = null;
-        unmount(this.rcsbFvDivId);
-    }
-
-    private highlightHoverCallback(selection: RcsbFvTrackDataElementInterface[]): void {
-        if(selection != null && selection.length > 0) {
-            if(selection[0].isEmpty){
-                const selectionList = [{
-                    modelId: this.assemblyModelSate.getString("modelId"),
-                    labelAsymId: this.assemblyModelSate.getString("labelAsymId"),
-                    position: selection[0].begin,
-                    operatorName: this.assemblyModelSate.getOperator()?.name
-                }];
-                if(selection[0].end != null)
-                    selectionList.push({
-                        modelId: this.assemblyModelSate.getString("modelId"),
-                        labelAsymId: this.assemblyModelSate.getString("labelAsymId"),
-                        position: selection[0].end,
-                        operatorName: this.assemblyModelSate.getOperator()?.name
-                    })
-                this.props.plugin.select(
-                    selectionList,
-                    'hover',
-                    'set'
-                );
-            }else {
-                this.props.plugin.select(processMultipleGaps(this.assemblyModelSate.getString("modelId"), this.assemblyModelSate.getString("labelAsymId"), selection, this.assemblyModelSate.getOperator()?.name), 'hover', 'set');
-            }
-        }else{
-            this.props.plugin.clearSelection('hover');
-        }
-    }
-
-    private selectionChangeCallback(selection: Array<RcsbFvTrackDataElementInterface>): void {
-        if(this.innerSelectionFlag)
-            return;
-        this.props.plugin.clearSelection('select', {modelId: this.assemblyModelSate.getString("modelId"), labelAsymId: this.assemblyModelSate.getString("labelAsymId"), operatorName: this.assemblyModelSate.getOperator()?.name});
-        this.props.selectorManager.clearSelection('select', {labelAsymId: this.assemblyModelSate.getString("labelAsymId"), operatorName: this.assemblyModelSate.getOperator()?.name});
-        if(selection == null || selection.length === 0) {
-            this.resetPluginView();
-        }else{
-            this.select(selection);
-        }
-    }
-
-    private select(selection: Array<RcsbFvTrackDataElementInterface>): void{
-        selection.forEach(e=>{
-            const x = e.begin;
-            const y = e.end ?? e.begin;
-            if(e.isEmpty){
-                this.props.plugin.select([{
-                        modelId: this.assemblyModelSate.getString("modelId"),
-                        labelAsymId: this.assemblyModelSate.getString("labelAsymId"),
-                        position: x,
-                        operatorName: this.assemblyModelSate.getOperator()?.name},
-                    {
-                        modelId: this.assemblyModelSate.getString("modelId"),
-                        labelAsymId: this.assemblyModelSate.getString("labelAsymId"),
-                        position: y,
-                        operatorName: this.assemblyModelSate.getOperator()?.name
-                    }],
-                    'select',
-                 'add'
-                );
-                this.props.selectorManager.addSelectionFromRegion(
-                    this.assemblyModelSate.getString("modelId"),
-                    this.assemblyModelSate.getString("labelAsymId"),
-                    {begin:x, end:y, isEmpty: true, source: 'sequence'},
-                    'select', this.assemblyModelSate.getOperator()?.name);
-            }else{
-                this.props.plugin.select(processGaps(this.assemblyModelSate.getString("modelId"), this.assemblyModelSate.getString("labelAsymId"), e, this.assemblyModelSate.getOperator()?.name), 'select', 'add');
-                this.props.selectorManager.addSelectionFromRegion(this.assemblyModelSate.getString("modelId"), this.assemblyModelSate.getString("labelAsymId"), {begin:x, end:y, source: 'sequence'}, 'select', this.assemblyModelSate.getOperator()?.name);
-            }
-        });
-    }
-
-    private elementClickCallback(e:RcsbFvTrackDataElementInterface): void {
-        this.props.plugin.clearFocus();
-        if(this.currentSelectedComponentId != null)
-            this.props.plugin.removeComponent(this.currentSelectedComponentId);
-        if(e == null)
-            return;
-        const x = e.begin;
-        const y = e.end ?? e.begin;
-        if(e.isEmpty){
-            this.props.plugin.cameraFocus(this.assemblyModelSate.getString("modelId"), this.assemblyModelSate.getString("labelAsymId"), [x,y], this.assemblyModelSate.getOperator()?.name);
-            this.currentSelectedComponentId = this.assemblyModelSate.getString("labelAsymId") +":"+ ((x === y) ? x.toString() : x.toString()+","+y.toString());
-            asyncScheduler.schedule(async ()=>{
-                await this.props.plugin.createComponent(
-                    this.currentSelectedComponentId,
-                    [
-                        {modelId: this.assemblyModelSate.getString("modelId"), labelAsymId: this.assemblyModelSate.getString("labelAsymId"), position: x, operatorName: this.assemblyModelSate.getOperator()?.name},
-                        {modelId: this.assemblyModelSate.getString("modelId"), labelAsymId: this.assemblyModelSate.getString("labelAsymId"), position: y, operatorName: this.assemblyModelSate.getOperator()?.name}
-                        ],
-                    'ball-and-stick'
-                )
-                if(x === y)
-                    asyncScheduler.schedule(()=>{
-                        this.props.plugin.setFocus(this.assemblyModelSate.getString("modelId"), this.assemblyModelSate.getString("labelAsymId"), x, y, this.assemblyModelSate.getOperator()?.name);
-                    },200);
-            },100);
-
-        }else{
-            this.props.plugin.cameraFocus(this.assemblyModelSate.getString("modelId"), this.assemblyModelSate.getString("labelAsymId"), x, y, this.assemblyModelSate.getOperator()?.name);
-            if((y-x)<this.createComponentThreshold){
-                this.currentSelectedComponentId = this.assemblyModelSate.getString("labelAsymId") +":"+ (x === y ? x.toString() : x.toString()+"-"+y.toString());
-                asyncScheduler.schedule(async ()=>{
-                    await this.props.plugin.createComponent(
-                        this.currentSelectedComponentId,
-                        processGaps(this.assemblyModelSate.getString("modelId"), this.assemblyModelSate.getString("labelAsymId"), e, this.assemblyModelSate.getOperator()?.name),
-                        'ball-and-stick'
-                    )
-                    if(x === y)
-                        asyncScheduler.schedule(()=>{
-                            this.props.plugin.setFocus(this.assemblyModelSate.getString("modelId"), this.assemblyModelSate.getString("labelAsymId"), x, y, this.assemblyModelSate.getOperator()?.name);
-                        },200);
-                },100);
-            }
-        }
-    }
-
-    private filterFeatures(data: {annotations: Array<AnnotationFeatures>; rcsbContext:Partial<PolymerEntityInstanceInterface>}): Promise<Array<AnnotationFeatures>> {
-        return new Promise<Array<AnnotationFeatures>>(async resolve => {
-            let annotations: Array<AnnotationFeatures> = [];
-            (await Promise.all(data.annotations.map(async ann=>{
-                if(ann.source == Source.PdbInterface && ann.target_id && data.rcsbContext?.asymId) {
-                    const interfaceToInstance: InterfaceInstanceTranslate = await RcsbRequestContextManager.getInterfaceToInstance(ann.target_id);
-                    if(typeof ann.target_identifiers?.interface_partner_index === "number" && ann.target_identifiers.assembly_id === this.assemblyModelSate.getString("assemblyId")) {
-                        const operatorIds:string[][] = interfaceToInstance.getOperatorIds(ann.target_id)[ann.target_identifiers.interface_partner_index];
-                        if(ann.features && this.assemblyModelSate.getOperator() && operatorIds.map(o=>o.join("|")).includes( this.assemblyModelSate.getOperator()!.ids.join("|") )){
-                            ann.features = ann.features.filter(f=>(f && f.type == FeatureType.BurialFraction));
-                            if(ann.features.length > 0)
-                                return ann;
-                        }
-                    }
-                }else if(ann.source == Source.PdbInstance && ann.features){
-                    ann.features = ann.features?.filter(f=>(f?.type!==Type.Asa));
-                    return ann;
-                }else if(ann.source != Source.PdbInterface){
-                    return ann;
-                }
-            }))).forEach((value,index,array)=>{
-                if(value)
-                    annotations = annotations.concat(value);
-            });
-            resolve(annotations);
-        });
-    }
-
-}
-
-function processGaps(modelId: string, labelAsymId: string, e: RcsbFvTrackDataElementInterface, operatorName?:string): Array<SaguaroRange>{
-    const regions: Array<SaguaroRange> = new Array<SaguaroRange>();
-    let lastIndex: number = e.begin;
-    e.gaps?.forEach((g)=>{
-        regions.push({
-            modelId: modelId,
-            labelAsymId: labelAsymId,
-            begin: lastIndex,
-            end: g.begin,
-            operatorName: operatorName
-        });
-        lastIndex = g.end;
-    });
-    regions.push({
-        modelId: modelId,
-        labelAsymId: labelAsymId,
-        begin: lastIndex,
-        end: e.end ?? e.begin,
-        operatorName: operatorName
-    });
-    return regions;
-}
-
-function processMultipleGaps(modelId: string, labelAsymId: string, list: Array<RcsbFvTrackDataElementInterface>, operatorName?:string): Array<SaguaroRange>{
-    let regions: Array<SaguaroRange> = new Array<SaguaroRange>();
-    list.forEach(e=>{
-        regions = regions.concat(processGaps(modelId, labelAsymId, e, operatorName));
-    });
-    return regions;
-}
-
-async function createComponents(plugin: SaguaroPluginInterface, modelMap:SaguaroPluginModelMapType): Promise<void> {
-    plugin.displayComponent("Water", false);
-    await plugin.colorComponent("Polymer", 'chain-id');
-    const chains: Array<{modelId: string; auth: string; label: string;}> = new Array<{modelId: string; auth: string; label: string;}>();
-    modelMap.forEach((entry, modelId)=>{
-        entry.chains.forEach(ch=>{
-            if(ch.type === "polymer") {
-                chains.push({modelId: modelId, auth: ch.auth, label: ch.label});
-            }
-        });
-    });
-    plugin.removeComponent();
-    plugin.clearFocus();
-    for(const ch of chains) {
-        const label: string = ch.auth === ch.label ? ch.label : `${ch.label} [auth ${ch.auth}]`;
-        await plugin.createComponent(label, ch.modelId, ch.label, 'cartoon');
-        await plugin.colorComponent(label, 'chain-id');
-    }
-    await plugin.removeComponent("Polymer");
-}
-
-function getOperator(entryInfo: {entryId: string; assemblyId: string, chains:Array<ChainInfo>;}, defaultAuthId?: string, defaultOperatorName?:string): OperatorInfo | undefined{
-    const chainInfo: ChainInfo | undefined = defaultAuthId ? entryInfo.chains.find(ch=>ch.auth === defaultAuthId) : entryInfo.chains[0];
-    if(chainInfo){
-        const operatorInfo: OperatorInfo | undefined = defaultOperatorName ? chainInfo.operators.find(op=>op.name === defaultOperatorName) : chainInfo.operators[0];
-        if(operatorInfo)
-            return operatorInfo;
-    }
-    return undefined;
-}

+ 3 - 3
src/RcsbFvSequence/SequenceViews/CustomView.tsx → src/RcsbFvSequence/SequenceViews/CustomView/CustomView.tsx

@@ -1,6 +1,6 @@
 import {asyncScheduler} from "rxjs";
 
-import {AbstractView, AbstractViewInterface} from "./AbstractView";
+import {AbstractView, AbstractViewInterface} from "../AbstractView";
 import {
     RcsbFvBoardConfigInterface,
     RcsbFvRowConfigInterface,
@@ -8,11 +8,11 @@ import {
     RcsbFvTrackDataElementInterface
 } from "@rcsb/rcsb-saguaro";
 import * as React from "react";
-import {RcsbFvSelectorManager} from "../../RcsbFvSelection/RcsbFvSelectorManager";
+import {RcsbFvSelectorManager} from "../../../RcsbFvSelection/RcsbFvSelectorManager";
 import {
     SaguaroPluginModelMapType,
     SaguaroPluginPublicInterface
-} from "../../RcsbFvStructure/SaguaroPluginInterface";
+} from "../../../RcsbFvStructure/SaguaroPluginInterface";
 
 export type CustomViewStateInterface = Omit<CustomViewInterface, "modelChangeCallback">;
 

+ 0 - 0
src/RcsbFvSequence/SequenceViews/AssemblyView/AssemblyModelSate.ts → src/RcsbFvSequence/SequenceViews/RcsbView/AssemblyModelSate.ts


+ 252 - 0
src/RcsbFvSequence/SequenceViews/RcsbView/CallbackHelper.ts

@@ -0,0 +1,252 @@
+import {
+    RcsbFvModulePublicInterface
+} from "@rcsb/rcsb-saguaro-app/build/dist/RcsbFvWeb/RcsbFvModule/RcsbFvModuleInterface";
+import {RcsbFvSelectorManager} from "../../../RcsbFvSelection/RcsbFvSelectorManager";
+import {AssemblyModelSate} from "./AssemblyModelSate";
+import {
+    SaguaroPluginInterface,
+    SaguaroPluginModelMapType, SaguaroRange,
+    SaguaroRegionList
+} from "../../../RcsbFvStructure/SaguaroPluginInterface";
+import {RcsbFvTrackDataElementInterface} from "@rcsb/rcsb-saguaro";
+import {asyncScheduler} from "rxjs";
+import {DataContainer} from "../../../Utils/DataContainer";
+
+interface CallbackHelperInterface {
+    rcsbFvContainer: DataContainer<RcsbFvModulePublicInterface>;
+    selectorManager: RcsbFvSelectorManager;
+    assemblyModelSate: AssemblyModelSate;
+    plugin: SaguaroPluginInterface;
+    modelChangeCallback(modelMap:SaguaroPluginModelMapType, defaultAuthId?: string, defaultOperatorName?:string): Promise<void>;
+}
+
+export class CallbackHelper {
+
+    private readonly rcsbFvContainer: DataContainer<RcsbFvModulePublicInterface>;
+    private readonly selectorManager: RcsbFvSelectorManager;
+    private readonly assemblyModelSate: AssemblyModelSate;
+    private selectedComponentId: string|undefined;
+    private readonly plugin: SaguaroPluginInterface;
+    private readonly modelChangeCallback: (modelMap:SaguaroPluginModelMapType, defaultAuthId?: string, defaultOperatorName?:string)=> Promise<void>;
+    private readonly CREATE_COMPONENT_THR: number = 3;
+
+    constructor(config: CallbackHelperInterface) {
+        this.rcsbFvContainer = config.rcsbFvContainer;
+        this.selectorManager = config.selectorManager;
+        this.assemblyModelSate = config.assemblyModelSate;
+        this.plugin = config.plugin;
+        this.modelChangeCallback = config.modelChangeCallback;
+    }
+
+    public async pluginSelectCallback(mode:'select'|'hover'): Promise<void> {
+        const allSel: Array<SaguaroRegionList> | undefined = this.selectorManager.getSelection(mode);
+        const lastSel: SaguaroRegionList|null = this.selectorManager.getLastSelection('select');
+        const modelId: string = this.assemblyModelSate.getString("modelId");
+        const labelAsymId: string = this.assemblyModelSate.getString("labelAsymId");
+        const operatorName: string|undefined = this.assemblyModelSate.getOperator()?.name;
+
+        if(mode === 'select') this.removeComponent();
+
+        if(allSel == null || allSel.length ===0) {
+            this.rcsbFvContainer.get()?.getFv().clearSelection(mode);
+            if(mode === 'select') this.resetPluginView();
+        }else if( mode === 'select' && lastSel?.labelAsymId && (lastSel?.labelAsymId != labelAsymId || lastSel?.operatorName != operatorName) ){
+            const authId: string | undefined = this.assemblyModelSate.getChainInfo(lastSel?.labelAsymId!)?.auth;
+            await this.modelChangeCallback(this.assemblyModelSate.getMap(), authId, lastSel?.operatorName);
+        }else{
+            const sel: SaguaroRegionList | undefined = this.selectorManager.getSelectionWithCondition(
+                modelId,
+                labelAsymId,
+                mode,
+                operatorName
+            );
+            if (sel == null) {
+                this.rcsbFvContainer.get()?.getFv().clearSelection(mode);
+                if(mode === 'select') this.resetPluginView();
+            } else {
+                this.rcsbFvContainer.get()?.getFv().setSelection({elements: sel.regions, mode: mode});
+            }
+        }
+    }
+
+    public elementClickCallback(e:RcsbFvTrackDataElementInterface): void {
+        this.plugin.clearFocus();
+        this.removeComponent();
+        if(e == null)
+            return;
+
+        const x = e.begin;
+        const y = e.end ?? e.begin;
+        const modelId: string = this.assemblyModelSate.getString("modelId");
+        const labelAsymId: string = this.assemblyModelSate.getString("labelAsymId");
+        const operatorName: string|undefined = this.assemblyModelSate.getOperator()?.name;
+
+        if(e.isEmpty){
+            this.plugin.cameraFocus(modelId, labelAsymId, [x,y], operatorName);
+            this.selectedComponentId = labelAsymId +":"+ ((x === y) ? x.toString() : x.toString()+","+y.toString());
+            asyncScheduler.schedule(async ()=>{
+                await this.plugin.createComponent(
+                    this.selectedComponentId!,
+                    [
+                        {modelId, labelAsymId, operatorName, position: x},
+                        {modelId, labelAsymId, operatorName, position: y}
+                    ],
+                    'ball-and-stick'
+                )
+                if(x === y)
+                    asyncScheduler.schedule(()=>{
+                        this.plugin.setFocus(modelId, labelAsymId, x, y, operatorName);
+                    },60);
+            },30);
+
+        }else{
+            this.plugin.cameraFocus(modelId, labelAsymId, x, y, operatorName);
+            if((y-x)<this.CREATE_COMPONENT_THR){
+                this.selectedComponentId = labelAsymId +":"+ (x === y ? x.toString() : x.toString()+"-"+y.toString());
+                asyncScheduler.schedule(async ()=>{
+                    await this.plugin.createComponent(
+                        this.selectedComponentId!,
+                        processGaps(modelId, labelAsymId, e, operatorName),
+                        'ball-and-stick'
+                    )
+                    if(x === y)
+                        asyncScheduler.schedule(()=>{
+                            this.plugin.setFocus(modelId, labelAsymId, x, y, operatorName);
+                        },60);
+                },30);
+            }
+        }
+    }
+
+    public highlightHoverCallback(selection: RcsbFvTrackDataElementInterface[]): void {
+        const modelId: string = this.assemblyModelSate.getString("modelId");
+        const labelAsymId: string = this.assemblyModelSate.getString("labelAsymId");
+        const operatorName: string|undefined = this.assemblyModelSate.getOperator()?.name;
+
+        if(selection != null && selection.length > 0) {
+            if(selection[0].isEmpty){
+                const selectionList = [{
+                    modelId,
+                    labelAsymId,
+                    position: selection[0].begin,
+                    operatorName
+                }];
+                if(selection[0].end != null)
+                    selectionList.push({
+                        modelId,
+                        labelAsymId,
+                        position: selection[0].end,
+                        operatorName
+                    })
+                this.plugin.select(
+                    selectionList,
+                    'hover',
+                    'set'
+                );
+            }else {
+                this.plugin.select(
+                    processMultipleGaps(modelId, labelAsymId, selection, operatorName),
+                    'hover',
+                    'set'
+                );
+            }
+        }else{
+            this.plugin.clearSelection('hover');
+        }
+    }
+
+    public selectionChangeCallback(selection: Array<RcsbFvTrackDataElementInterface>): void {
+        const modelId: string = this.assemblyModelSate.getString("modelId");
+        const labelAsymId: string = this.assemblyModelSate.getString("labelAsymId");
+        const operatorName: string|undefined = this.assemblyModelSate.getOperator()?.name;
+
+        this.plugin.clearSelection('select', {modelId, labelAsymId, operatorName});
+        this.selectorManager.clearSelection('select', {labelAsymId, operatorName});
+        if(selection == null || selection.length === 0) {
+            this.resetPluginView();
+        }else{
+            this.select(selection);
+        }
+    }
+
+    private select(selection: Array<RcsbFvTrackDataElementInterface>): void{
+        const modelId: string = this.assemblyModelSate.getString("modelId");
+        const labelAsymId: string = this.assemblyModelSate.getString("labelAsymId");
+        const operatorName: string|undefined = this.assemblyModelSate.getOperator()?.name;
+
+        selection.forEach(e=>{
+            const x = e.begin;
+            const y = e.end ?? e.begin;
+            if(e.isEmpty){
+                this.plugin.select([{
+                        modelId,
+                        labelAsymId,
+                        operatorName,
+                        position: x
+                    }, {
+                        modelId,
+                        labelAsymId,
+                        operatorName,
+                        position: y
+                    }],
+                    'select',
+                    'add'
+                );
+                this.selectorManager.addSelectionFromRegion(
+                    modelId,
+                    labelAsymId,
+                    {begin:x, end:y, isEmpty: true, source: 'sequence'},
+                    'select',
+                    operatorName
+                );
+            }else{
+                this.plugin.select(processGaps(modelId, labelAsymId, e, operatorName), 'select', 'add');
+                this.selectorManager.addSelectionFromRegion(modelId, labelAsymId, {begin:x, end:y, source: 'sequence'}, 'select', operatorName);
+            }
+        });
+    }
+
+    private removeComponent(): void {
+        if(this.selectedComponentId != null) {
+            this.plugin.removeComponent(this.selectedComponentId);
+            this.selectedComponentId = undefined;
+        }
+    }
+
+    private resetPluginView(): void {
+        this.plugin.clearFocus();
+        this.plugin.resetCamera();
+    }
+
+}
+
+function processMultipleGaps(modelId: string, labelAsymId: string, list: Array<RcsbFvTrackDataElementInterface>, operatorName?:string): Array<SaguaroRange>{
+    let regions: Array<SaguaroRange> = new Array<SaguaroRange>();
+    list.forEach(e=>{
+        regions = regions.concat(processGaps(modelId, labelAsymId, e, operatorName));
+    });
+    return regions;
+}
+
+function processGaps(modelId: string, labelAsymId: string, e: RcsbFvTrackDataElementInterface, operatorName?:string): Array<SaguaroRange>{
+    const regions: Array<SaguaroRange> = new Array<SaguaroRange>();
+    let lastIndex: number = e.begin;
+    e.gaps?.forEach((g)=>{
+        regions.push({
+            modelId: modelId,
+            labelAsymId: labelAsymId,
+            begin: lastIndex,
+            end: g.begin,
+            operatorName: operatorName
+        });
+        lastIndex = g.end;
+    });
+    regions.push({
+        modelId: modelId,
+        labelAsymId: labelAsymId,
+        begin: lastIndex,
+        end: e.end ?? e.begin,
+        operatorName: operatorName
+    });
+    return regions;
+}

+ 225 - 0
src/RcsbFvSequence/SequenceViews/RcsbView/PfvFactoryImplementation/AssemblyPfvFactory.tsx

@@ -0,0 +1,225 @@
+import {
+    RcsbFvAdditionalConfig,
+    RcsbFvModulePublicInterface
+} from "@rcsb/rcsb-saguaro-app/build/dist/RcsbFvWeb/RcsbFvModule/RcsbFvModuleInterface";
+import {RcsbFvSelectorManager} from "../../../../RcsbFvSelection/RcsbFvSelectorManager";
+import {AssemblyModelSate} from "../AssemblyModelSate";
+import {
+    ChainInfo, OperatorInfo,
+    SaguaroPluginInterface,
+    SaguaroPluginModelMapType
+} from "../../../../RcsbFvStructure/SaguaroPluginInterface";
+import {
+    InstanceSequenceConfig,
+    InstanceSequenceOnchangeInterface
+} from "@rcsb/rcsb-saguaro-app/build/dist/RcsbFvWeb/RcsbFvBuilder/RcsbFvInstanceBuilder";
+import {asyncScheduler} from "rxjs";
+import {buildInstanceSequenceFv, FeatureType, RcsbFvUI, RcsbRequestContextManager} from "@rcsb/rcsb-saguaro-app";
+import {RcsbFvDOMConstants} from "../../../../RcsbFvConstants/RcsbFvConstants";
+import {SelectOptionProps} from "@rcsb/rcsb-saguaro-app/build/dist/RcsbFvWeb/WebTools/SelectButton";
+import {ChainDisplay} from "./ChainDisplay";
+import * as React from "react";
+import {RcsbFvBoardConfigInterface} from "@rcsb/rcsb-saguaro";
+import {AnnotationFeatures, Source, Type} from "@rcsb/rcsb-api-tools/build/RcsbGraphQL/Types/Borrego/GqlTypes";
+import {
+    PolymerEntityInstanceInterface
+} from "@rcsb/rcsb-saguaro-app/build/dist/RcsbCollectTools/DataCollectors/PolymerEntityInstancesCollector";
+import {
+    InterfaceInstanceTranslate
+} from "@rcsb/rcsb-saguaro-app/build/dist/RcsbUtils/Translators/InterfaceInstanceTranslate";
+import {DataContainer} from "../../../../Utils/DataContainer";
+import {PfvAbstractFactory} from "../PfvFactoryInterface";
+
+interface AssemblyPfvFactoryInterface {
+    rcsbFvDivId: string;
+    rcsbFvContainer: DataContainer<RcsbFvModulePublicInterface>;
+    selectorManager: RcsbFvSelectorManager;
+    assemblyModelSate: AssemblyModelSate;
+    plugin: SaguaroPluginInterface;
+    boardConfigContainer: DataContainer<Partial<RcsbFvBoardConfigInterface>>;
+    pfvChangeCallback(): Promise<void>;
+    additionalConfig: RcsbFvAdditionalConfig & {operatorChangeCallback?:(operatorInfo: OperatorInfo)=>void} | undefined;
+    useOperatorsFlag:boolean | undefined;
+    instanceSequenceConfig: InstanceSequenceConfig | undefined;
+}
+
+export class AssemblyPfvFactory extends PfvAbstractFactory<{instanceSequenceConfig: InstanceSequenceConfig|undefined}> {
+
+    private readonly rcsbFvDivId: string;
+    private readonly rcsbFvContainer: DataContainer<RcsbFvModulePublicInterface>;
+    private readonly selectorManager: RcsbFvSelectorManager;
+    private readonly assemblyModelSate: AssemblyModelSate;
+    private readonly plugin: SaguaroPluginInterface;
+    private readonly boardConfigContainer: DataContainer<Partial<RcsbFvBoardConfigInterface>>;
+    private readonly pfvChangeCallback: ()=>Promise<void>;
+    private readonly instanceSequenceConfig: InstanceSequenceConfig|undefined;
+    private readonly additionalConfig: RcsbFvAdditionalConfig & {operatorChangeCallback?:(operatorInfo: OperatorInfo)=>void} | undefined;
+    private readonly useOperatorsFlag:boolean | undefined;
+    private readonly OPERATOR_DROPDOWN_TITLE: string = "Symmetry Partner";
+
+    constructor(config: AssemblyPfvFactoryInterface) {
+        super(config);
+        this.rcsbFvDivId = config.rcsbFvDivId;
+        this.rcsbFvContainer = config.rcsbFvContainer;
+        this.selectorManager = config.selectorManager;
+        this.assemblyModelSate = config.assemblyModelSate;
+        this.plugin = config.plugin;
+        this.instanceSequenceConfig = config.instanceSequenceConfig;
+        this.additionalConfig = config.additionalConfig;
+        this.boardConfigContainer = config.boardConfigContainer;
+        this.useOperatorsFlag = config.useOperatorsFlag;
+        this.pfvChangeCallback = config.pfvChangeCallback;
+    }
+
+    async buildPfv(modelMap:SaguaroPluginModelMapType, defaultAuthId?: string, defaultOperatorName?:string): Promise<RcsbFvModulePublicInterface | undefined> {
+        this.assemblyModelSate.setMap(modelMap);
+        this.plugin.clearFocus();
+        const onChangeCallback: Map<string, (x: InstanceSequenceOnchangeInterface)=>void> = new Map<string, (x: InstanceSequenceOnchangeInterface) => {}>();
+        const assemblyInstances: Map<string, Set<string>> = new Map<string, Set<string>>();
+        this.assemblyModelSate.forEach((v,k)=>{
+            assemblyInstances.set(v.entryId,new Set<string>(v.chains.map(d=>d.label)));
+            onChangeCallback.set(v.entryId,(x)=>{
+                this.assemblyModelSate.set({entryId: v.entryId, labelAsymId: x.asymId, modelId: k});
+                asyncScheduler.schedule(()=>{
+                    this.selectorManager.setLastSelection('select', null);
+                    this.pfvChangeCallback();
+                },1000);
+            });
+        });
+        const operatorNameContainer: DataContainer<string> = new DataContainer<string>(defaultOperatorName);
+        let module: RcsbFvModulePublicInterface | undefined = undefined;
+        if(this.assemblyModelSate.get("entryId") != null) {
+            module = await buildInstanceSequenceFv(
+                this.rcsbFvDivId,
+                RcsbFvDOMConstants.SELECT_INSTANCE_PFV_ID,
+                this.assemblyModelSate.getString("entryId"),
+                {
+                    ...this.instanceSequenceConfig,
+                    defaultValue: defaultAuthId,
+                    onChangeCallback: onChangeCallback.get(this.assemblyModelSate.getString("entryId")),
+                    beforeChangeCallback: (x: InstanceSequenceOnchangeInterface)=>{
+                        this.assemblyModelSate.set({entryId:x.pdbId, labelAsymId: x.asymId});
+                        const entryMap:[string, {entryId: string, assemblyId: string, chains: ChainInfo[]}] | undefined = Array.from(this.assemblyModelSate.entries()).find((e)=>(e[1].entryId === x.pdbId));
+                        if(!entryMap){
+                            throw `Error: no modelId was found for ${x.pdbId}`;
+                        }
+                        const operator: OperatorInfo|undefined = getOperator(this.assemblyModelSate.getMap().get(entryMap[0])!, defaultAuthId, operatorNameContainer.get());
+                        this.addOperatorButton(operator?.name);
+                        this.assemblyModelSate.setOperator(x.asymId,operator?.name);
+                        operatorNameContainer.set(undefined);
+                        if(typeof this.additionalConfig?.operatorChangeCallback === "function" && this.assemblyModelSate.getOperator()){
+                            this.additionalConfig.operatorChangeCallback(this.assemblyModelSate.getOperator()!);
+                        }
+                        if((this.assemblyModelSate.getChainInfo()?.operators?.length ?? 0) > 1)
+                            return {
+                                operatorIds: operator?.ids
+                            }
+                    },
+                    filterInstances: assemblyInstances.get(this.assemblyModelSate.getString("entryId")),
+                    selectButtonOptionProps: (props: SelectOptionProps) => (
+                        <div style={{display: 'flex'}}>
+                            <ChainDisplay plugin={this.plugin} label={props.data.label}/>
+                            {props.children}
+                        </div>)
+                },
+                {
+                    ...this.additionalConfig,
+                    boardConfig: this.boardConfigContainer.get(),
+                    externalTrackBuilder:{
+                        filterFeatures: this.filterFeatures.bind(this)
+                    }
+                }
+            );
+        }
+        if(!defaultAuthId)
+            await createComponents(this.plugin, this.assemblyModelSate.getMap());
+        return module;
+    }
+
+    private addOperatorButton(operatorName?: string): void{
+        const currentChainInfo: ChainInfo|undefined = this.assemblyModelSate.getChainInfo();
+        if(this.useOperatorsFlag && currentChainInfo && currentChainInfo.operators.length >1 ){
+            this.assemblyModelSate.setOperator(undefined,operatorName);
+            RcsbFvUI.addSelectButton(
+                this.rcsbFvDivId,
+                RcsbFvDOMConstants.SELECT_INSTANCE_PFV_ID,
+                currentChainInfo.operators.map(op=>({
+                    label:`${op.ids.join("-")} (${op.name})`,
+                    optId:op.name,
+                    onChange: async ()=>{
+                        this.assemblyModelSate.set({operator:op});
+                        await this.buildPfv(
+                            this.assemblyModelSate.getMap(),
+                            this.assemblyModelSate.getChainInfo()?.auth,
+                            op.name
+                        )
+                    }
+                })),
+                {
+                    defaultValue: this.assemblyModelSate.getOperator()?.name,
+                    dropdownTitle:this.OPERATOR_DROPDOWN_TITLE
+                }
+            );
+        }
+    }
+
+    private filterFeatures(data: {annotations: Array<AnnotationFeatures>; rcsbContext:Partial<PolymerEntityInstanceInterface>}): Promise<Array<AnnotationFeatures>> {
+        return new Promise<Array<AnnotationFeatures>>(async resolve => {
+            let annotations: Array<AnnotationFeatures> = [];
+            (await Promise.all(data.annotations.map(async ann=>{
+                if(ann.source == Source.PdbInterface && ann.target_id && data.rcsbContext?.asymId) {
+                    const interfaceToInstance: InterfaceInstanceTranslate = await RcsbRequestContextManager.getInterfaceToInstance(ann.target_id);
+                    if(typeof ann.target_identifiers?.interface_partner_index === "number" && ann.target_identifiers.assembly_id === this.assemblyModelSate.getString("assemblyId")) {
+                        const operatorIds:string[][] = interfaceToInstance.getOperatorIds(ann.target_id)[ann.target_identifiers.interface_partner_index];
+                        if(ann.features && this.assemblyModelSate.getOperator() && operatorIds.map(o=>o.join("|")).includes( this.assemblyModelSate.getOperator()!.ids.join("|") )){
+                            ann.features = ann.features.filter(f=>(f && f.type == FeatureType.BurialFraction));
+                            if(ann.features.length > 0)
+                                return ann;
+                        }
+                    }
+                }else if(ann.source == Source.PdbInstance && ann.features){
+                    ann.features = ann.features?.filter(f=>(f?.type!==Type.Asa));
+                    return ann;
+                }else if(ann.source != Source.PdbInterface){
+                    return ann;
+                }
+            }))).forEach((value,index,array)=>{
+                if(value)
+                    annotations = annotations.concat(value);
+            });
+            resolve(annotations);
+        });
+    }
+
+}
+
+async function createComponents(plugin: SaguaroPluginInterface, modelMap:SaguaroPluginModelMapType): Promise<void> {
+    plugin.displayComponent("Water", false);
+    await plugin.colorComponent("Polymer", 'chain-id');
+    const chains: Array<{modelId: string; auth: string; label: string;}> = new Array<{modelId: string; auth: string; label: string;}>();
+    modelMap.forEach((entry, modelId)=>{
+        entry.chains.forEach(ch=>{
+            if(ch.type === "polymer") {
+                chains.push({modelId: modelId, auth: ch.auth, label: ch.label});
+            }
+        });
+    });
+    plugin.removeComponent();
+    plugin.clearFocus();
+    for(const ch of chains) {
+        const label: string = ch.auth === ch.label ? ch.label : `${ch.label} [auth ${ch.auth}]`;
+        await plugin.createComponent(label, ch.modelId, ch.label, 'cartoon');
+        await plugin.colorComponent(label, 'chain-id');
+    }
+    await plugin.removeComponent("Polymer");
+}
+
+function getOperator(entryInfo: {entryId: string; assemblyId: string, chains:Array<ChainInfo>;}, defaultAuthId?: string, defaultOperatorName?:string): OperatorInfo | undefined{
+    const chainInfo: ChainInfo | undefined = defaultAuthId ? entryInfo.chains.find(ch=>ch.auth === defaultAuthId) : entryInfo.chains[0];
+    if(chainInfo){
+        const operatorInfo: OperatorInfo | undefined = defaultOperatorName ? chainInfo.operators.find(op=>op.name === defaultOperatorName) : chainInfo.operators[0];
+        if(operatorInfo)
+            return operatorInfo;
+    }
+    return undefined;
+}

+ 1 - 1
src/RcsbFvSequence/SequenceViews/AssemblyView/ChainDisplay.tsx → src/RcsbFvSequence/SequenceViews/RcsbView/PfvFactoryImplementation/ChainDisplay.tsx

@@ -1,5 +1,5 @@
 import * as React from "react";
-import {SaguaroPluginInterface} from "../../../RcsbFvStructure/SaguaroPluginInterface";
+import {SaguaroPluginInterface} from "../../../../RcsbFvStructure/SaguaroPluginInterface";
 
 interface ChainDisplayInterface {
     plugin: SaguaroPluginInterface;

+ 30 - 0
src/RcsbFvSequence/SequenceViews/RcsbView/PfvFactoryInterface.ts

@@ -0,0 +1,30 @@
+import {DataContainer} from "../../../Utils/DataContainer";
+import {
+    RcsbFvAdditionalConfig,
+    RcsbFvModulePublicInterface
+} from "@rcsb/rcsb-saguaro-app/build/dist/RcsbFvWeb/RcsbFvModule/RcsbFvModuleInterface";
+import {RcsbFvSelectorManager} from "../../../RcsbFvSelection/RcsbFvSelectorManager";
+import {AssemblyModelSate} from "./AssemblyModelSate";
+import {
+    OperatorInfo,
+    SaguaroPluginInterface,
+    SaguaroPluginModelMapType
+} from "../../../RcsbFvStructure/SaguaroPluginInterface";
+import {RcsbFvBoardConfigInterface} from "@rcsb/rcsb-saguaro";
+
+export interface PfvFactoryInterface {
+    rcsbFvDivId: string;
+    rcsbFvContainer: DataContainer<RcsbFvModulePublicInterface>;
+    selectorManager: RcsbFvSelectorManager;
+    assemblyModelSate: AssemblyModelSate;
+    plugin: SaguaroPluginInterface;
+    boardConfigContainer: DataContainer<Partial<RcsbFvBoardConfigInterface>>;
+    pfvChangeCallback(): Promise<void>;
+    additionalConfig: RcsbFvAdditionalConfig & {operatorChangeCallback?:(operatorInfo: OperatorInfo)=>void} | undefined;
+    useOperatorsFlag:boolean | undefined;
+}
+
+export abstract class PfvAbstractFactory<T={}> {
+    protected constructor(config:PfvFactoryInterface & T){}
+    abstract buildPfv(modelMap:SaguaroPluginModelMapType, defaultAuthId?: string, defaultOperatorName?:string): Promise<RcsbFvModulePublicInterface | undefined>;
+}

+ 171 - 0
src/RcsbFvSequence/SequenceViews/RcsbView/RcsbView.tsx

@@ -0,0 +1,171 @@
+import * as React from "react";
+
+import {RcsbFvDOMConstants} from "../../../RcsbFvConstants/RcsbFvConstants";
+import {unmount} from "@rcsb/rcsb-saguaro-app";
+import {AbstractView, AbstractViewInterface} from "../AbstractView";
+import {InstanceSequenceConfig} from "@rcsb/rcsb-saguaro-app/build/dist/RcsbFvWeb/RcsbFvBuilder/RcsbFvInstanceBuilder";
+import {RcsbFvBoardConfigInterface, RcsbFvTrackDataElementInterface} from "@rcsb/rcsb-saguaro";
+import {OperatorInfo, SaguaroPluginModelMapType} from "../../../RcsbFvStructure/SaguaroPluginInterface";
+import {RcsbFvAdditionalConfig, RcsbFvModulePublicInterface} from "@rcsb/rcsb-saguaro-app/build/dist/RcsbFvWeb/RcsbFvModule/RcsbFvModuleInterface";
+import {AssemblyModelSate} from "./AssemblyModelSate";
+import {CallbackHelper} from "./CallbackHelper";
+import {AssemblyPfvFactory} from "./PfvFactoryImplementation/AssemblyPfvFactory";
+import {DataContainer} from "../../../Utils/DataContainer";
+import {PfvAbstractFactory, PfvFactoryInterface} from "./PfvFactoryInterface";
+
+export interface RcsbViewInterface<T extends {}> {
+    rcsbId: string;
+    additionalConfig?: RcsbFvAdditionalConfig & {operatorChangeCallback?:(operatorInfo: OperatorInfo)=>void};
+    useOperatorsFlag?:boolean;
+    pfvParams:T;
+    pfvFactory: new(config:PfvFactoryInterface & T) => PfvAbstractFactory<T>;
+}
+
+export class RcsbView<T extends {}> extends AbstractView<RcsbViewInterface<T> & AbstractViewInterface, {}>{
+
+    private readonly assemblyModelSate: AssemblyModelSate = new AssemblyModelSate();
+    private innerSelectionFlag: boolean = false;
+    private boardConfigContainer: DataContainer<Partial<RcsbFvBoardConfigInterface>> = new DataContainer();
+    private rcsbFvContainer: DataContainer<RcsbFvModulePublicInterface> = new DataContainer<RcsbFvModulePublicInterface>();
+    private readonly callbackHelper: CallbackHelper;
+    private readonly pfvFactory: PfvAbstractFactory<T>;
+
+    constructor(props:RcsbViewInterface<T> & AbstractViewInterface) {
+        super(props);
+        this.callbackHelper = new CallbackHelper({
+            rcsbFvContainer: this.rcsbFvContainer,
+            selectorManager: this.props.selectorManager,
+            plugin: this.props.plugin,
+            modelChangeCallback: this.modelChangeCallback.bind(this),
+            assemblyModelSate: this.assemblyModelSate
+        });
+        const pfvFactory = this.props.pfvFactory;
+        this.pfvFactory = new pfvFactory({
+            ...this.props.pfvParams,
+            rcsbFvContainer: this.rcsbFvContainer,
+            selectorManager: this.props.selectorManager,
+            plugin: this.props.plugin,
+            assemblyModelSate: this.assemblyModelSate,
+            boardConfigContainer: this.boardConfigContainer,
+            rcsbFvDivId: this.rcsbFvDivId,
+            pfvChangeCallback: this.instanceChangeCallback.bind(this),
+            additionalConfig: this.props.additionalConfig,
+            useOperatorsFlag: this.props.useOperatorsFlag
+        });
+    }
+
+    additionalContent(): JSX.Element {
+        return (
+            <div style={{marginTop:10}}>
+                <div>
+                    <div id={RcsbFvDOMConstants.SELECT_INSTANCE_PFV_ID} style={{display:"inline-block"}}/>
+                    <div style={{display:"inline-block", marginLeft:25}}>
+                        <a href={"/docs/sequence-viewers/protein-feature-view"} target={"_blank"}>Help</a>
+                    </div>
+                </div>
+                <div style={{position:"absolute", top:5, right:5}} >
+                    <a style={{textDecoration:"none", color:"#337ab7", cursor:"pointer", marginRight:15}} target={"_blank"} href={"/docs/sequence-viewers/3d-protein-feature-view"}>
+                        Help
+                    </a>
+                    <a style={{textDecoration:"none", color: "#337ab7", cursor:"pointer"}} onClick={()=>{this.props.unmount(true, ()=>{
+                        window.history.back();
+                    })}}>
+                        Back
+                    </a>
+                </div>
+            </div>
+        );
+    }
+
+    componentDidMount (): void {
+        super.componentDidMount();
+        const width: number | undefined = document.getElementById(this.componentDivId)?.getBoundingClientRect().width;
+        if(width == null)
+            return;
+        const trackWidth: number = width - 190 - 55;
+        this.boardConfigContainer.set({
+            ...this.props.additionalConfig?.boardConfig,
+            trackWidth: trackWidth,
+            highlightHoverPosition:true,
+            highlightHoverElement:true,
+            elementClickCallBack:(e:RcsbFvTrackDataElementInterface)=>{
+                this.elementClickCallback(e);
+                if(typeof this.props.additionalConfig?.boardConfig?.elementClickCallBack === "function")
+                    this.props.additionalConfig?.boardConfig.elementClickCallBack(e);
+            },
+            selectionChangeCallBack:(selection: Array<RcsbFvTrackDataElementInterface>)=>{
+                this.selectionChangeCallback(selection);
+                if(typeof this.props.additionalConfig?.boardConfig?.selectionChangeCallBack === "function")
+                    this.props.additionalConfig?.boardConfig.selectionChangeCallBack(selection);
+            },
+            highlightHoverCallback:(selection: RcsbFvTrackDataElementInterface[])=>{
+                this.highlightHoverCallback(selection);
+                if(typeof this.props.additionalConfig?.boardConfig?.highlightHoverCallback === "function")
+                    this.props.additionalConfig?.boardConfig.highlightHoverCallback(selection);
+            },
+        });
+    }
+
+    componentWillUnmount() {
+        super.componentWillUnmount();
+        unmount(this.rcsbFvDivId);
+    }
+
+    async structureSelectionCallback(): Promise<void> {
+        await this.pluginSelectCallback('select');
+    }
+
+    async structureHoverCallback(): Promise<void> {
+        await this.pluginSelectCallback('hover');
+    }
+
+    representationChangeCallback(): void{
+        //TODO
+    }
+
+    async updateDimensions(): Promise<void> {
+        const width: number = window.document.getElementById(this.componentDivId)?.getBoundingClientRect().width ?? 0;
+        const trackWidth: number = width - 190 - 55;
+        this.boardConfigContainer.set({...this.boardConfigContainer.get(), trackWidth});
+        await this.rcsbFvContainer.get()?.getFv().updateBoardConfig({boardConfigData:{trackWidth:trackWidth}})
+        await this.structureSelectionCallback();
+        return void 0;
+    }
+
+    private resetPluginView(): void {
+        this.props.plugin.clearFocus();
+        this.props.plugin.resetCamera();
+    }
+
+    private async pluginSelectCallback(mode:'select'|'hover'): Promise<void> {
+        if(this.rcsbFvContainer.get() == null)
+            return;
+        this.innerSelectionFlag = true;
+        await this.callbackHelper.pluginSelectCallback(mode);
+        this.innerSelectionFlag = false;
+    }
+
+    async modelChangeCallback(modelMap:SaguaroPluginModelMapType, defaultAuthId?: string, defaultOperatorName?:string): Promise<void> {
+        this.rcsbFvContainer.set(await this.pfvFactory.buildPfv(modelMap, defaultAuthId, defaultOperatorName));
+    }
+
+    private async instanceChangeCallback(): Promise<void>{
+        this.resetPluginView();
+        await this.pluginSelectCallback('select');
+    }
+
+    private highlightHoverCallback(selection: RcsbFvTrackDataElementInterface[]): void {
+        this.callbackHelper.highlightHoverCallback(selection);
+    }
+
+    private selectionChangeCallback(selection: Array<RcsbFvTrackDataElementInterface>): void {
+        if(this.innerSelectionFlag)
+            return;
+        this.callbackHelper.selectionChangeCallback(selection);
+    }
+
+    private elementClickCallback(e:RcsbFvTrackDataElementInterface): void {
+        this.callbackHelper.elementClickCallback(e);
+    }
+
+}

+ 15 - 0
src/Utils/DataContainer.ts

@@ -0,0 +1,15 @@
+
+export class DataContainer<T> {
+    private data: T | undefined = undefined;
+
+    constructor(data?:T) {
+        this.data = data;
+    }
+
+    public get(): T | undefined{
+        return this.data
+    }
+    public set(data: T | undefined): void{
+        this.data = data;
+    }
+}

+ 1 - 1
src/examples/css-config/index.tsx

@@ -6,7 +6,7 @@ import {
     CustomViewInterface,
     FeatureBlockInterface,
     FeatureViewInterface
-} from "../../RcsbFvSequence/SequenceViews/CustomView";
+} from "../../RcsbFvSequence/SequenceViews/CustomView/CustomView";
 import * as React from "react";
 import {
     RcsbFv,

+ 1 - 1
src/examples/external-mapping/FeatureViewerConfig.ts

@@ -1,4 +1,4 @@
-import {FeatureViewInterface} from "../../RcsbFvSequence/SequenceViews/CustomView";
+import {FeatureViewInterface} from "../../RcsbFvSequence/SequenceViews/CustomView/CustomView";
 import {SaguaroPluginPublicInterface, SaguaroRegionList} from "../../RcsbFvStructure/SaguaroPluginInterface";
 import {RcsbFvSelectorManager, RegionSelectionInterface} from "../../RcsbFvSelection/RcsbFvSelectorManager";
 import {

+ 1 - 1
src/examples/external-mapping/index.tsx

@@ -5,7 +5,7 @@ import {LoadMethod} from "../../RcsbFvStructure/StructurePlugins/MolstarPlugin";
 import {
     CustomViewInterface,
     FeatureBlockInterface
-} from "../../RcsbFvSequence/SequenceViews/CustomView";
+} from "../../RcsbFvSequence/SequenceViews/CustomView/CustomView";
 import * as React from "react";
 import {RcsbRepresentationPreset} from "./TrajectoryPreset";
 import {PluginStateObject} from "molstar/lib/mol-plugin-state/objects";

+ 1 - 1
src/examples/multiple-chain/index.tsx

@@ -6,7 +6,7 @@ import {
     BlockSelectorManager,
     CustomViewInterface,
     FeatureBlockInterface, FeatureViewInterface
-} from "../../RcsbFvSequence/SequenceViews/CustomView";
+} from "../../RcsbFvSequence/SequenceViews/CustomView/CustomView";
 import * as React from "react";
 import {
     RcsbFv,

+ 1 - 1
src/examples/single-chain/index.tsx

@@ -5,7 +5,7 @@ import {LoadMethod} from "../../RcsbFvStructure/StructurePlugins/MolstarPlugin";
 import {
     CustomViewInterface,
     FeatureBlockInterface, FeatureViewInterface
-} from "../../RcsbFvSequence/SequenceViews/CustomView";
+} from "../../RcsbFvSequence/SequenceViews/CustomView/CustomView";
 import * as React from "react";
 import {
     RcsbFv,

+ 1 - 1
src/examples/structural-alignment/index.tsx

@@ -6,7 +6,7 @@ import {
     CustomViewInterface,
     FeatureBlockInterface,
     FeatureViewInterface
-} from "../../RcsbFvSequence/SequenceViews/CustomView";
+} from "../../RcsbFvSequence/SequenceViews/CustomView/CustomView";
 import * as React from "react";
 import {
     RcsbFv,

Some files were not shown because too many files changed in this diff