|
@@ -15,7 +15,8 @@ type TmFv1DParams = {
|
|
|
side1: string,
|
|
|
trackWidth?: number,
|
|
|
rowTitleWidth?: number,
|
|
|
- autoResize?: boolean
|
|
|
+ autoResize?: boolean,
|
|
|
+ parent?: TmFv1DElement
|
|
|
};
|
|
|
|
|
|
type TmFv1DState = {
|
|
@@ -24,7 +25,7 @@ type TmFv1DState = {
|
|
|
};
|
|
|
|
|
|
|
|
|
-export class TmFv1D extends React.Component<TmFv1DParams, TmFv1DState> {
|
|
|
+export class TmFv1DComponent extends React.Component<TmFv1DParams, TmFv1DState> {
|
|
|
|
|
|
protected containerRef = React.createRef<HTMLDivElement>();
|
|
|
|
|
@@ -38,7 +39,8 @@ export class TmFv1D extends React.Component<TmFv1DParams, TmFv1DState> {
|
|
|
url: this.props.url,
|
|
|
side1: this.props.side1,
|
|
|
rowTitleWidth: this.props.rowTitleWidth,
|
|
|
- trackWidth: this.props.trackWidth
|
|
|
+ trackWidth: this.props.trackWidth,
|
|
|
+ parent: this.props.parent
|
|
|
});
|
|
|
|
|
|
if (this.props.autoResize) {
|
|
@@ -71,9 +73,24 @@ export class TmFv1D extends React.Component<TmFv1DParams, TmFv1DState> {
|
|
|
|
|
|
export class TmFv1DElement extends HTMLElement {
|
|
|
|
|
|
+ public static readonly TagName: string = "tm-saguaro-1d";
|
|
|
+
|
|
|
+ public interSections: number = 0;
|
|
|
+ public static interVal: number = 500;
|
|
|
+ public static isMassResizeScheduled: boolean = false;
|
|
|
+ public static isMassResizeTriggered: boolean = false;
|
|
|
+ public isDataReceived: boolean = false;
|
|
|
+
|
|
|
connectedCallback() {
|
|
|
const root = createRoot(this);
|
|
|
- setTimeout(() => this.render(root), 1000);
|
|
|
+ this.render(root);
|
|
|
+
|
|
|
+ const intersectionOptions = {
|
|
|
+ root: null,
|
|
|
+ rootMargin: "0px",
|
|
|
+ threshold: 0.01
|
|
|
+ };
|
|
|
+ new IntersectionObserver(this.intersection, intersectionOptions).observe(this);
|
|
|
}
|
|
|
|
|
|
render(root: Root) {
|
|
@@ -94,26 +111,40 @@ export class TmFv1DElement extends HTMLElement {
|
|
|
? parseInt(this.getAttribute("trackWidth")!) || (defaultViewerWidth - ULTIMATE_GAP_CONSTANT)
|
|
|
: calculatedWidth;
|
|
|
trackWidth -= rowTitleWidth;
|
|
|
- console.log(`${elementId} Widths:`, [ rowTitleWidth, trackWidth ]);
|
|
|
|
|
|
- root.render(
|
|
|
- <TmFv1D
|
|
|
+ root.render(<>
|
|
|
+ <div className="loader">Loading...</div>
|
|
|
+ <TmFv1DComponent
|
|
|
elementId={ elementId }
|
|
|
url={ url }
|
|
|
side1={ side1 }
|
|
|
trackWidth={ trackWidth }
|
|
|
rowTitleWidth={ rowTitleWidth }
|
|
|
autoResize={ autoResize }
|
|
|
+ parent={ this }
|
|
|
/>
|
|
|
- );
|
|
|
+ </>);
|
|
|
+
|
|
|
+ setTimeout(() => this.triggerResize(elementId), TmFv1DElement.interVal);
|
|
|
+ TmFv1DElement.interVal += 250;
|
|
|
+ }
|
|
|
|
|
|
- setTimeout(() => this.triggerResize(elementId), 250);
|
|
|
+ public dataReceivedCallback() {
|
|
|
+ this.isDataReceived = true;
|
|
|
+ this.firstChild?.remove();
|
|
|
+ const elements: Element[] = Array.from(document.getElementsByTagName(TmFv1DElement.TagName));
|
|
|
+ const isAllReceived = elements.every(element => (element as TmFv1DElement).isDataReceived);
|
|
|
+ if (isAllReceived && !TmFv1DElement.isMassResizeScheduled) {
|
|
|
+ TmFv1DElement.isMassResizeScheduled = true;
|
|
|
+ TmFv1DElement.massResize();
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- public triggerResize(id: string): void {
|
|
|
- const element = document.getElementById(id);
|
|
|
+ public triggerResize(id?: string|null): void {
|
|
|
+ const element = id ? document.getElementById(id)
|
|
|
+ : document.getElementById((this.firstChild! as HTMLElement).id);
|
|
|
if (!element) {
|
|
|
- console.warn(`'${id}' element not found`);
|
|
|
+ console.warn(`triggerResize: counter: ${this.interSections} '${id}' element not found`);
|
|
|
return;
|
|
|
}
|
|
|
const paddingRight = element.style.paddingRight;
|
|
@@ -121,6 +152,31 @@ export class TmFv1DElement extends HTMLElement {
|
|
|
setTimeout(() => element.style.setProperty('padding-right', paddingRight), 250);
|
|
|
}
|
|
|
|
|
|
+ public intersection(entries: IntersectionObserverEntry[], observer: IntersectionObserver) {
|
|
|
+ const target = entries[0].target as TmFv1DElement;
|
|
|
+ target.interSections++;
|
|
|
+
|
|
|
+ if (target.interSections < 4) {
|
|
|
+ target.triggerResize(target.getAttribute("elementId"));
|
|
|
+ } else {
|
|
|
+ observer.unobserve(target);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public static massResize() {
|
|
|
+ const element = document.getElementsByTagName('body')[0];
|
|
|
+ if (!element) {
|
|
|
+ console.error(`body element not found`);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ TmFv1DElement.isMassResizeTriggered = true;
|
|
|
+
|
|
|
+ const paddingRight = element.style.paddingRight;
|
|
|
+ element.style.setProperty('padding-right', '1px');
|
|
|
+ setTimeout(() => element.style.setProperty('padding-right', paddingRight), 500);
|
|
|
+ console.log(`Mass resize of ${TmFv1DElement.TagName} elements has been scheduled`);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
function calculateViewerWidth(parent: HTMLElement|null) {
|
|
@@ -147,11 +203,15 @@ const featureViewerRegistry = new Map();
|
|
|
* @param side1 side1 paramter to update site colors, if it is needed
|
|
|
*/
|
|
|
export async function createRcsbFeatureViewer(params: {
|
|
|
- elementId: string, url: string, side1: string, trackWidth?: number, rowTitleWidth?: number }): Promise<RcsbFv> {
|
|
|
+ elementId: string, url: string, side1: string,
|
|
|
+ trackWidth?: number, rowTitleWidth?: number, parent?: TmFv1DElement }): Promise<RcsbFv> {
|
|
|
|
|
|
updateSiteColors(params.side1 as any);
|
|
|
|
|
|
const featureTracks = await fetchDescriptor(params.url);
|
|
|
+ if (params.parent) {
|
|
|
+ params.parent.dataReceivedCallback();
|
|
|
+ }
|
|
|
const blockConfig = featureTracks.blockConfig;
|
|
|
const boardConfig = blockConfig[0].featureViewConfig[0].boardConfig;
|
|
|
boardConfig.trackWidth = params.trackWidth ?? 600;
|