Quellcode durchsuchen

Sorting for sequence identity MSA && saguaro update

Joan Segura vor 2 Jahren
Ursprung
Commit
e975b22bf9

+ 8 - 0
CHANGELOG.md

@@ -2,6 +2,14 @@
 
 [Semantic Versioning](https://semver.org/)
 
+## [2.3.1] - 2022-11-23
+### New Features
+- Sorting component `MsaUiSortComponent` for sequence identity MSA
+
+### Dependency update
+- rcsb-saguaro-app v4.5.1
+- rcsb-saguaro v2.5.4
+
 ## [2.3.0] - 2022-11-08
 ### Breaking Change 
 - Param `LoadMethod.loadPdbIds` has been removed. Multiple entries can be loaded passing a list of `LoadMolstarInterface` to `RcsbFvStructureConfigInterface.loadConfig`

+ 24 - 24
package-lock.json

@@ -1,18 +1,18 @@
 {
   "name": "@rcsb/rcsb-saguaro-3d",
-  "version": "2.2.1",
+  "version": "2.3.0",
   "lockfileVersion": 2,
   "requires": true,
   "packages": {
     "": {
       "name": "@rcsb/rcsb-saguaro-3d",
-      "version": "2.2.1",
+      "version": "2.3.0",
       "license": "MIT",
       "dependencies": {
         "@rcsb/rcsb-api-tools": "^4.1.1",
         "@rcsb/rcsb-molstar": "^2.5.5",
-        "@rcsb/rcsb-saguaro": "^2.2.16",
-        "@rcsb/rcsb-saguaro-app": "^4.4.15",
+        "@rcsb/rcsb-saguaro": "^2.5.4",
+        "@rcsb/rcsb-saguaro-app": "^4.5.1",
         "http-server": "^14.1.1",
         "molstar": "^3.13.0"
       },
@@ -2515,9 +2515,9 @@
       }
     },
     "node_modules/@rcsb/rcsb-saguaro": {
-      "version": "2.2.16",
-      "resolved": "https://registry.npmjs.org/@rcsb/rcsb-saguaro/-/rcsb-saguaro-2.2.16.tgz",
-      "integrity": "sha512-/I3SVWy1eVG+zqdqpN57OevM54THGi1rs4k31X4X4JaZRqMd4Uvfxk2P/kj+8ZVIQau27UizZ9w6Ejw87cmDBQ==",
+      "version": "2.5.4",
+      "resolved": "https://registry.npmjs.org/@rcsb/rcsb-saguaro/-/rcsb-saguaro-2.5.4.tgz",
+      "integrity": "sha512-x0/o4fubMdnsO9ltc1ll6kYapBaQMsDbDcWmpIyqm/yNvnWvkj0+0ZuOmZtoeJ8zwtJgHsyUS/DClq/tI/tHNg==",
       "dependencies": {
         "@d3fc/d3fc-sample": "^5.0.1",
         "d3": "^7.6.1",
@@ -2529,12 +2529,12 @@
       }
     },
     "node_modules/@rcsb/rcsb-saguaro-app": {
-      "version": "4.4.15",
-      "resolved": "https://registry.npmjs.org/@rcsb/rcsb-saguaro-app/-/rcsb-saguaro-app-4.4.15.tgz",
-      "integrity": "sha512-HlzhIHCAJR2tYEnDc42T8mom9pBkcXRdrW64G66v5tdFFpHBp4sehwi+3t2/5x9hEsFBs6NhGZwrXT9eT4I9fA==",
+      "version": "4.5.1",
+      "resolved": "https://registry.npmjs.org/@rcsb/rcsb-saguaro-app/-/rcsb-saguaro-app-4.5.1.tgz",
+      "integrity": "sha512-Iq6wy9TSafAK42LRcL43KN3YcneO+rCyAW4ej62I6br3YBmEdjRt7WOywsZFteDRPbu39TOtXDQ2G4bKw6O0Pg==",
       "dependencies": {
         "@rcsb/rcsb-api-tools": "^4.1.1",
-        "@rcsb/rcsb-saguaro": "^2.2.16",
+        "@rcsb/rcsb-saguaro": "^2.5.4",
         "react-select": "^5.4.0",
         "rxjs": "^7.5.5",
         "victory": "^36.5.0"
@@ -7880,9 +7880,9 @@
       }
     },
     "node_modules/loader-utils": {
-      "version": "2.0.3",
-      "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.3.tgz",
-      "integrity": "sha512-THWqIsn8QRnvLl0shHYVBN9syumU8pYWEHPTmkiVGd+7K5eFNVSY6AJhRvgGF70gg1Dz+l/k8WicvFCxdEs60A==",
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz",
+      "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==",
       "dev": true,
       "dependencies": {
         "big.js": "^5.2.2",
@@ -14330,9 +14330,9 @@
       }
     },
     "@rcsb/rcsb-saguaro": {
-      "version": "2.2.16",
-      "resolved": "https://registry.npmjs.org/@rcsb/rcsb-saguaro/-/rcsb-saguaro-2.2.16.tgz",
-      "integrity": "sha512-/I3SVWy1eVG+zqdqpN57OevM54THGi1rs4k31X4X4JaZRqMd4Uvfxk2P/kj+8ZVIQau27UizZ9w6Ejw87cmDBQ==",
+      "version": "2.5.4",
+      "resolved": "https://registry.npmjs.org/@rcsb/rcsb-saguaro/-/rcsb-saguaro-2.5.4.tgz",
+      "integrity": "sha512-x0/o4fubMdnsO9ltc1ll6kYapBaQMsDbDcWmpIyqm/yNvnWvkj0+0ZuOmZtoeJ8zwtJgHsyUS/DClq/tI/tHNg==",
       "requires": {
         "@d3fc/d3fc-sample": "^5.0.1",
         "d3": "^7.6.1",
@@ -14344,12 +14344,12 @@
       }
     },
     "@rcsb/rcsb-saguaro-app": {
-      "version": "4.4.15",
-      "resolved": "https://registry.npmjs.org/@rcsb/rcsb-saguaro-app/-/rcsb-saguaro-app-4.4.15.tgz",
-      "integrity": "sha512-HlzhIHCAJR2tYEnDc42T8mom9pBkcXRdrW64G66v5tdFFpHBp4sehwi+3t2/5x9hEsFBs6NhGZwrXT9eT4I9fA==",
+      "version": "4.5.1",
+      "resolved": "https://registry.npmjs.org/@rcsb/rcsb-saguaro-app/-/rcsb-saguaro-app-4.5.1.tgz",
+      "integrity": "sha512-Iq6wy9TSafAK42LRcL43KN3YcneO+rCyAW4ej62I6br3YBmEdjRt7WOywsZFteDRPbu39TOtXDQ2G4bKw6O0Pg==",
       "requires": {
         "@rcsb/rcsb-api-tools": "^4.1.1",
-        "@rcsb/rcsb-saguaro": "^2.2.16",
+        "@rcsb/rcsb-saguaro": "^2.5.4",
         "react-select": "^5.4.0",
         "rxjs": "^7.5.5",
         "victory": "^36.5.0"
@@ -18427,9 +18427,9 @@
       "dev": true
     },
     "loader-utils": {
-      "version": "2.0.3",
-      "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.3.tgz",
-      "integrity": "sha512-THWqIsn8QRnvLl0shHYVBN9syumU8pYWEHPTmkiVGd+7K5eFNVSY6AJhRvgGF70gg1Dz+l/k8WicvFCxdEs60A==",
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz",
+      "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==",
       "dev": true,
       "requires": {
         "big.js": "^5.2.2",

+ 4 - 3
package.json

@@ -1,6 +1,6 @@
 {
   "name": "@rcsb/rcsb-saguaro-3d",
-  "version": "2.3.0",
+  "version": "2.3.1",
   "description": "RCSB Molstar/Saguaro Web App",
   "main": "build/dist/app.js",
   "files": [
@@ -17,6 +17,7 @@
     "buildDoc": "typedoc --excludeExternals --externalPattern \"**/node_modules/**\" src/RcsbFv3D/RcsbFv3DAssembly.tsx src/RcsbFv3D/RcsbFv3DUniprot.tsx src/RcsbFv3D/RcsbFv3DSequenceIdentity.tsx src/RcsbFv3D/RcsbFv3DCustom.tsx && sed -i '' '/---/d' docs/index.html",
     "clean": "del-cli build/src",
     "cleanAll": "npm run clean && del-cli build/dist",
+    "publishApp": "npm publish",
     "test": "echo \"Error: no test specified\" && exit 1"
   },
   "repository": {
@@ -82,8 +83,8 @@
   "dependencies": {
     "@rcsb/rcsb-api-tools": "^4.1.1",
     "@rcsb/rcsb-molstar": "^2.5.5",
-    "@rcsb/rcsb-saguaro": "^2.2.16",
-    "@rcsb/rcsb-saguaro-app": "^4.4.15",
+    "@rcsb/rcsb-saguaro": "^2.5.4",
+    "@rcsb/rcsb-saguaro-app": "^4.5.1",
     "http-server": "^14.1.1",
     "molstar": "^3.13.0"
   },

+ 10 - 3
src/RcsbFv3D/RcsbFv3DSequenceIdentity.tsx

@@ -26,6 +26,8 @@ import {
 } from "@rcsb/rcsb-saguaro-app/build/dist/RcsbCollectTools/DataCollectors/PolymerEntityInstancesCollector";
 import {SearchQuery} from "@rcsb/rcsb-api-tools/build/RcsbSearch/Types/SearchQueryInterface";
 import {HelpLinkComponent} from "../RcsbFvSequence/SequenceViews/RcsbView/Components/HelpLinkComponent";
+import {AlignmentResponse} from "@rcsb/rcsb-api-tools/build/RcsbGraphQL/Types/Borrego/GqlTypes";
+import {DataContainer} from "../Utils/DataContainer";
 
 export interface RcsbFv3DSequenceIdentityInterface  {
     elementId?: string;
@@ -40,9 +42,10 @@ export interface RcsbFv3DSequenceIdentityInterface  {
     cssConfig?: RcsbFv3DCssConfig;
 }
 
-export class RcsbFv3DSequenceIdentity extends RcsbFv3DAbstract<{groupId:string; query?: SearchQuery;},LoadMolstarInterface|undefined,{viewerElement:string|HTMLElement,viewerProps:Partial<ViewerProps>},{context:any,module:RcsbFvModulePublicInterface}> {
+export class RcsbFv3DSequenceIdentity extends RcsbFv3DAbstract<{groupId:string; query?: SearchQuery;alignmentResponseContainer: DataContainer<AlignmentResponse>;},LoadMolstarInterface|undefined,{viewerElement:string|HTMLElement,viewerProps:Partial<ViewerProps>},{context:any,module:RcsbFvModulePublicInterface}> {
     constructor(params:RcsbFv3DSequenceIdentityInterface){
         const elementId: string = params.elementId ?? uniqid("RcsbFv3D_");
+        const alignmentResponseContainer:DataContainer<AlignmentResponse> = new DataContainer<AlignmentResponse>();
         super({
             elementId,
             sequenceConfig:{
@@ -54,11 +57,15 @@ export class RcsbFv3DSequenceIdentity extends RcsbFv3DAbstract<{groupId:string;
                     additionalConfig: params.additionalConfig,
                     pfvParams:{
                         groupId:params.config.groupId,
-                        query:params.config.query
+                        query:params.config.query,
+                        alignmentResponseContainer
                     },
                     buildPfvOnMount: true,
                     pfvManagerFactory: new SequenceIdentityPfvManagerFactory<LoadMolstarInterface>(),
-                    callbackManagerFactory: new MsaCallbackManagerFactory<LoadMolstarInterface, {context:{groupId:string} & Partial<PolymerEntityInstanceInterface>}>({pluginLoadParamsDefinition}),
+                    callbackManagerFactory: new MsaCallbackManagerFactory<LoadMolstarInterface, {context:{groupId:string} & Partial<PolymerEntityInstanceInterface>}>({
+                        pluginLoadParamsDefinition,
+                        alignmentResponseContainer
+                    }),
                     additionalContent:(props)=>(<HelpLinkComponent {...props} helpHref={"/docs/grouping-structures/groups-1d-3d-alignment"}/>)
                 }
             },

+ 6 - 1
src/RcsbFv3D/RcsbFv3DUniprot.tsx

@@ -26,6 +26,8 @@ import {MolstarAlignmentLoader} from "../RcsbFvStructure/StructureUtils/MolstarA
 import {MsaBehaviourObserver} from "../RcsbFvStructure/StructureViewerBehaviour/MsaBehaviour";
 import {SearchQuery} from "@rcsb/rcsb-api-tools/build/RcsbSearch/Types/SearchQueryInterface";
 import {HelpLinkComponent} from "../RcsbFvSequence/SequenceViews/RcsbView/Components/HelpLinkComponent";
+import {DataContainer} from "../Utils/DataContainer";
+import {AlignmentResponse} from "@rcsb/rcsb-api-tools/build/RcsbGraphQL/Types/Borrego/GqlTypes";
 
 export interface RcsbFv3DUniprotInterface  {
     elementId?: string;
@@ -58,7 +60,10 @@ export class RcsbFv3DUniprot extends RcsbFv3DAbstract<{upAcc:string; query?: Sea
                     },
                     buildPfvOnMount: true,
                     pfvManagerFactory: new UniprotPfvManagerFactory<LoadMolstarInterface>(),
-                    callbackManagerFactory: new MsaCallbackManagerFactory<LoadMolstarInterface, {context: UniprotSequenceOnchangeInterface;}>({pluginLoadParamsDefinition}),
+                    callbackManagerFactory: new MsaCallbackManagerFactory<LoadMolstarInterface, {context: UniprotSequenceOnchangeInterface;}>({
+                        pluginLoadParamsDefinition,
+                        alignmentResponseContainer: new DataContainer<AlignmentResponse>()
+                    }),
                     additionalContent:(props)=>(<HelpLinkComponent {...props} helpHref={"/docs/grouping-structures/groups-1d-3d-alignment"}/>)
                 }
             },

+ 25 - 11
src/RcsbFvSequence/SequenceViews/RcsbView/CallbackManagerFactoryImplementation/MsaCallbackManager.ts

@@ -6,23 +6,33 @@ import {
 import {RcsbFvTrackDataElementInterface} from "@rcsb/rcsb-saguaro";
 import {
     AlignedRegion,
-    AlignmentResponse,
-    TargetAlignment
+    AlignmentResponse
 } from "@rcsb/rcsb-api-tools/build/RcsbGraphQL/Types/Borrego/GqlTypes";
 import {RegionSelectionInterface} from "../../../../RcsbFvState/RcsbFvSelectorManager";
 import {ChainInfo, SaguaroRegionList} from "../../../../RcsbFvStructure/StructureViewerInterface";
 import {TagDelimiter} from "@rcsb/rcsb-saguaro-app";
 import {AlignmentMapper as AM} from "../../../../Utils/AlignmentMapper";
+import {DataContainer} from "../../../../Utils/DataContainer";
 
 export class MsaCallbackManagerFactory<R,U> implements CallbackManagerFactoryInterface<R,U> {
 
     private readonly pluginLoadParamsDefinition:(id: string)=>R;
-    constructor(config: {pluginLoadParamsDefinition:(id: string)=>R}) {
+    private readonly alignmentResponseContainer: DataContainer<AlignmentResponse>;
+
+    constructor(config: {
+        pluginLoadParamsDefinition:(id: string)=>R;
+        alignmentResponseContainer: DataContainer<AlignmentResponse>;
+    }) {
         this.pluginLoadParamsDefinition = config.pluginLoadParamsDefinition;
+        this.alignmentResponseContainer = config.alignmentResponseContainer;
     }
 
     getCallbackManager(config: CallbackConfigInterface<R>): CallbackManagerInterface<U> {
-        return new MsaCallbackManager( {...config, loadParamRequest:this.pluginLoadParamsDefinition});
+        return new MsaCallbackManager( {
+            ...config,
+            loadParamRequest:this.pluginLoadParamsDefinition,
+            alignmentResponseContainer:this.alignmentResponseContainer
+        });
     }
 
 }
@@ -32,11 +42,12 @@ class MsaCallbackManager<R,U>  extends AbstractCallbackManager<R,U>{
 
     private readonly loadParamRequest:(id: string)=>R;
     private readonly targetIds: {[key:string]:boolean} = {};
-    private alignmentResponse: AlignmentResponse;
+    private readonly alignmentResponseContainer: DataContainer<AlignmentResponse>;
 
-    constructor(config: CallbackConfigInterface<R> & {loadParamRequest:(id: string)=>R}) {
+    constructor(config: CallbackConfigInterface<R> & {loadParamRequest:(id: string)=>R;alignmentResponseContainer: DataContainer<AlignmentResponse>;}) {
         super(config);
         this.loadParamRequest = config.loadParamRequest;
+        this.alignmentResponseContainer = config.alignmentResponseContainer;
     }
 
     async featureClickCallback(e: RcsbFvTrackDataElementInterface): Promise<void> {
@@ -59,8 +70,8 @@ class MsaCallbackManager<R,U>  extends AbstractCallbackManager<R,U>{
         if(typeof this.rcsbFvContainer.get() === "undefined")
             return;
         const alignmentResponse: AlignmentResponse|undefined = await this.rcsbFvContainer.get()?.getAlignmentResponse();
-        if(!this.alignmentResponse && alignmentResponse) {
-            this.alignmentResponse = alignmentResponse;
+        if(!this.alignmentResponseContainer.get() && alignmentResponse) {
+            this.alignmentResponseContainer.set(alignmentResponse);
             alignmentResponse.target_alignment?.forEach(ta=> {if(ta?.target_id) this.targetIds[ta.target_id]=true})
         }else if(alignmentResponse) {
             const newTargetAlignments = alignmentResponse.target_alignment?.filter(ta=>{
@@ -69,10 +80,13 @@ class MsaCallbackManager<R,U>  extends AbstractCallbackManager<R,U>{
                     return true;
                 }
             });
-            if(newTargetAlignments)
-                this.alignmentResponse.target_alignment = this.alignmentResponse.target_alignment?.concat(
+            if(newTargetAlignments && this.alignmentResponseContainer.get()){
+                const ar = this.alignmentResponseContainer.get()!;
+                ar.target_alignment = ar.target_alignment?.concat(
                     newTargetAlignments
                 );
+                this.alignmentResponseContainer.set(ar)
+            }
         }
     }
 
@@ -122,7 +136,7 @@ class MsaCallbackManager<R,U>  extends AbstractCallbackManager<R,U>{
                 return;
             selection.forEach(s=>{
                 const alignedRegions = (alignment.target_alignment?.find(ta=>ta?.target_id === modelId)?.aligned_regions!.filter((o): o is AlignedRegion => o!=null) ?? []).concat(
-                    this.alignmentResponse?.target_alignment?.find(ta=>ta?.target_id === modelId)?.aligned_regions!.filter((o): o is AlignedRegion => o!=null) ?? []
+                    this.alignmentResponseContainer.get()?.target_alignment?.find(ta=>ta?.target_id === modelId)?.aligned_regions!.filter((o): o is AlignedRegion => o!=null) ?? []
                 );
 
                 if(!alignedRegions)

+ 43 - 0
src/RcsbFvSequence/SequenceViews/RcsbView/PfvManagerFactoryImplementation/MsaPfvComponents/MsaUiSortComponent.tsx

@@ -0,0 +1,43 @@
+
+import * as React from "react";
+import {DataContainer} from "../../../../../Utils/DataContainer";
+import {
+    RcsbFvModulePublicInterface
+} from "@rcsb/rcsb-saguaro-app/build/dist/RcsbFvWeb/RcsbFvModule/RcsbFvModuleInterface";
+import {RcsbFvStateManager} from "../../../../../RcsbFvState/RcsbFvStateManager";
+import {RcsbFvRowConfigInterface} from "@rcsb/rcsb-saguaro";
+
+export interface MsaUiSortInterface {
+    rcsbFvContainer: DataContainer<RcsbFvModulePublicInterface>;
+    stateManager: RcsbFvStateManager;
+}
+export class MsaUiSortComponent extends React.Component<MsaUiSortInterface, {}>{
+
+    private readonly TRACK_HEADER_SHIFT: number = 2;
+
+    render() {
+        return <div title={"Move selected entities to top"} onClick={()=>this.click()} style={{cursor: "pointer"}}>SORT</div>;
+    }
+
+    private async click(): Promise<void> {
+        const targets : string[]|undefined = this.props.rcsbFvContainer.get()?.getFv().getBoardData()
+            .map((d:RcsbFvRowConfigInterface<{},{},{},{targetId:string}>)=>d.metadata?.targetId)
+            .filter((d): d is string => Boolean(d))
+        if(!targets)
+            return;
+
+        const threshold: number = targets.findIndex(
+            target => !this.props.stateManager.assemblyModelSate.getMap().has(target)
+        );
+        if(threshold < 0)
+            return ;
+
+        const toMove: number[] = targets.reduce<number[]>((prev,curr, currIndex)=>{ if(this.props.stateManager.assemblyModelSate.getMap().has(curr) && currIndex > threshold) prev.push(currIndex); return prev;},[])
+        for(const [n,i] of toMove.map((n,i)=>[n,threshold+i])){
+           await this.props.rcsbFvContainer.get()?.getFv()?.moveTrack(
+                n+this.TRACK_HEADER_SHIFT,
+                i+this.TRACK_HEADER_SHIFT
+            )
+        }
+    }
+}

+ 26 - 1
src/RcsbFvSequence/SequenceViews/RcsbView/PfvManagerFactoryImplementation/SequenceIdentityPfvManagerFactory.ts

@@ -19,9 +19,12 @@ import {
     PolymerEntityInstanceInterface
 } from "@rcsb/rcsb-saguaro-app/build/dist/RcsbCollectTools/DataCollectors/PolymerEntityInstancesCollector";
 import {SearchQuery} from "@rcsb/rcsb-api-tools/build/RcsbSearch/Types/SearchQueryInterface";
+import {DataContainer} from "../../../../Utils/DataContainer";
+import {MsaUiSortComponent} from "./MsaPfvComponents/MsaUiSortComponent";
 
 interface SequenceIdentityPfvManagerInterface<R> extends PfvManagerFactoryConfigInterface<R,{context: {groupId:string};}> {
     groupId:string;
+    alignmentResponseContainer: DataContainer<AlignmentResponse>;
     query?: SearchQuery;
 }
 
@@ -58,6 +61,18 @@ class SequenceIdentityPfvManager<R> extends AbstractPfvManager<{groupId:string},
             {
                 ... this.additionalConfig,
                 boardConfig: this.boardConfigContainer.get(),
+                externalTrackBuilder:{
+                    filterAlignments: (data: { alignments: AlignmentResponse; rcsbContext?: Partial<PolymerEntityInstanceInterface> }) => {
+                        const visAlignment = this.config.alignmentResponseContainer?.get()?.target_alignment
+                                ?.filter(ta=>ta?.target_id && this.config.stateManager.assemblyModelSate.getMap()?.has(ta.target_id));
+                        const otherAlignment = data.alignments.target_alignment
+                            ?.filter(ta=>ta?.target_id && !this.config.stateManager.assemblyModelSate.getMap()?.has(ta.target_id));
+                        return new Promise(resolve => resolve({
+                            ...data.alignments,
+                            target_alignment: (visAlignment ?? []).concat(otherAlignment ?? [])
+                        }));
+                    }
+                },
                 trackConfigModifier: {
                     alignment: (alignmentContext: AlignmentRequestContextType, targetAlignment: TargetAlignment) => new Promise((resolve)=>{
                         resolve({
@@ -79,13 +94,23 @@ class SequenceIdentityPfvManager<R> extends AbstractPfvManager<{groupId:string},
                                     stateManager: this.stateManager,
                                     titleClick: ()=> this.loadAlignment(alignmentContext,targetAlignment)
                                 }
+                            },
+                            metadata:{
+                                targetId:targetAlignment.target_id
                             }
                         });
                     })
                 },
                 beforeChangeCallback: (module) => {
                     this.config.pfvChangeCallback({context:{groupId:this.config.groupId}});
-                }
+                },
+                externalUiComponents:[{
+                    component:MsaUiSortComponent,
+                    props: {
+                        rcsbFvContainer: this.rcsbFvContainer,
+                        stateManager: this.stateManager
+                    }
+                }]
             }
         );
         this.rcsbFvContainer.set(module);

+ 1 - 3
src/RcsbFvSequence/SequenceViews/RcsbView/PfvManagerFactoryInterface.ts

@@ -3,11 +3,9 @@ import {
     RcsbFvAdditionalConfig,
     RcsbFvModulePublicInterface
 } from "@rcsb/rcsb-saguaro-app/build/dist/RcsbFvWeb/RcsbFvModule/RcsbFvModuleInterface";
-import {RcsbFvSelectorManager} from "../../../RcsbFvState/RcsbFvSelectorManager";
-import {AssemblyModelSate} from "../../../RcsbFvState/AssemblyModelSate";
 import {
     OperatorInfo,
-    SaguaroPluginModelMapType, ViewerCallbackManagerInterface, ViewerActionManagerInterface
+    ViewerCallbackManagerInterface, ViewerActionManagerInterface
 } from "../../../RcsbFvStructure/StructureViewerInterface";
 import {RcsbFvBoardConfigInterface} from "@rcsb/rcsb-saguaro";
 import {RcsbFvStateManager} from "../../../RcsbFvState/RcsbFvStateManager";

+ 3 - 11
src/RcsbFvStructure/StructureViewerBehaviour/MsaBehaviour.ts

@@ -21,6 +21,8 @@ import {TagDelimiter} from "@rcsb/rcsb-saguaro-app";
 import {createSelectionExpressions} from "@rcsb/rcsb-molstar/build/src/viewer/helpers/selection";
 import {RegionSelectionInterface} from "../../RcsbFvState/RcsbFvSelectorManager";
 import {TargetAlignment} from "@rcsb/rcsb-api-tools/build/RcsbGraphQL/Types/Borrego/GqlTypes";
+import {FunctionCall} from "../../Utils/FunctionCall";
+import onetimeCall = FunctionCall.onetimeCall;
 
 export class MsaBehaviourObserver<R> implements StructureViewerBehaviourObserverInterface<R> {
 
@@ -111,7 +113,7 @@ class MsaBehaviour<R> implements StructureViewerBehaviourInterface {
                     return;
                 if(numRes == data?.length)
                     this.structureViewer.setFocus(modelId,labelAsymId,residues[0],residues[0],operatorName);
-                cameraFocus.call(d);
+                cameraFocus(d);
                 const ranges: SaguaroRange[] = regions.map(r=>({
                     modelId,
                     labelAsymId,
@@ -215,14 +217,4 @@ class MsaBehaviour<R> implements StructureViewerBehaviourInterface {
         }));
     }
 
-}
-
-function onetimeCall<P>(f:(x:P)=>void): {call: (x:P)=>void} {
-    const g = {
-        call:(x:P)=>{
-            f(x)
-            g.call = (x)=>{}
-        }
-    };
-    return g;
 }

+ 16 - 0
src/Utils/FunctionCall.ts

@@ -0,0 +1,16 @@
+
+export namespace FunctionCall {
+
+    export function onetimeCall<P>(f:(x:P)=>void): (x:P)=>void {
+        const g = {
+            onetime:(x:P)=>{
+                f(x)
+                g.onetime = (x)=>{}
+            }
+        };
+        return (x:P)=>{
+            g.onetime(x);
+        }
+    }
+
+}