MOBILE-3814 chore: Add a disconnect method on waitToBeInDOM
parent
30d24f99e3
commit
23cfb29de0
|
@ -19,7 +19,7 @@ import { CoreUtils } from '@services/utils/utils';
|
||||||
import { CoreMath } from '@singletons/math';
|
import { CoreMath } from '@singletons/math';
|
||||||
import { CoreComponentsRegistry } from '@singletons/components-registry';
|
import { CoreComponentsRegistry } from '@singletons/components-registry';
|
||||||
import { CoreFormatTextDirective } from './format-text';
|
import { CoreFormatTextDirective } from './format-text';
|
||||||
import { CoreEventObserver } from '@singletons/events';
|
import { CoreEventObserver, CoreSingleTimeEventObserver } from '@singletons/events';
|
||||||
import { CoreLoadingComponent } from '@components/loading/loading';
|
import { CoreLoadingComponent } from '@components/loading/loading';
|
||||||
import { CoreDomUtils } from '@services/utils/dom';
|
import { CoreDomUtils } from '@services/utils/dom';
|
||||||
|
|
||||||
|
@ -48,6 +48,7 @@ export class CoreCollapsibleFooterDirective implements OnInit, OnDestroy {
|
||||||
protected contentScrollListener?: EventListener;
|
protected contentScrollListener?: EventListener;
|
||||||
protected endContentScrollListener?: EventListener;
|
protected endContentScrollListener?: EventListener;
|
||||||
protected resizeListener?: CoreEventObserver;
|
protected resizeListener?: CoreEventObserver;
|
||||||
|
protected domListener?: CoreSingleTimeEventObserver;
|
||||||
|
|
||||||
constructor(el: ElementRef, protected ionContent: IonContent) {
|
constructor(el: ElementRef, protected ionContent: IonContent) {
|
||||||
this.element = el.nativeElement;
|
this.element = el.nativeElement;
|
||||||
|
@ -61,7 +62,9 @@ export class CoreCollapsibleFooterDirective implements OnInit, OnDestroy {
|
||||||
// Only if not present or explicitly falsy it will be false.
|
// Only if not present or explicitly falsy it will be false.
|
||||||
this.appearOnBottom = !CoreUtils.isFalseOrZero(this.appearOnBottom);
|
this.appearOnBottom = !CoreUtils.isFalseOrZero(this.appearOnBottom);
|
||||||
|
|
||||||
await CoreDomUtils.waitToBeInDOM(this.element);
|
this.domListener = CoreDomUtils.waitToBeInDOM(this.element);
|
||||||
|
await this.domListener.promise;
|
||||||
|
|
||||||
await this.waitLoadingsDone();
|
await this.waitLoadingsDone();
|
||||||
await this.waitFormatTextsRendered(this.element);
|
await this.waitFormatTextsRendered(this.element);
|
||||||
|
|
||||||
|
@ -231,6 +234,7 @@ export class CoreCollapsibleFooterDirective implements OnInit, OnDestroy {
|
||||||
}
|
}
|
||||||
|
|
||||||
this.resizeListener?.off();
|
this.resizeListener?.off();
|
||||||
|
this.domListener?.off();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,7 @@ import { CoreDomUtils } from '@services/utils/dom';
|
||||||
import { CoreUtils } from '@services/utils/utils';
|
import { CoreUtils } from '@services/utils/utils';
|
||||||
import { Translate } from '@singletons';
|
import { Translate } from '@singletons';
|
||||||
import { CoreComponentsRegistry } from '@singletons/components-registry';
|
import { CoreComponentsRegistry } from '@singletons/components-registry';
|
||||||
import { CoreEventObserver } from '@singletons/events';
|
import { CoreEventObserver, CoreSingleTimeEventObserver } from '@singletons/events';
|
||||||
import { CoreFormatTextDirective } from './format-text';
|
import { CoreFormatTextDirective } from './format-text';
|
||||||
|
|
||||||
const defaultMaxHeight = 80;
|
const defaultMaxHeight = 80;
|
||||||
|
@ -49,6 +49,7 @@ export class CoreCollapsibleItemDirective implements OnInit, OnDestroy {
|
||||||
protected maxHeight = defaultMaxHeight;
|
protected maxHeight = defaultMaxHeight;
|
||||||
protected expandedHeight = 0;
|
protected expandedHeight = 0;
|
||||||
protected resizeListener?: CoreEventObserver;
|
protected resizeListener?: CoreEventObserver;
|
||||||
|
protected domListener?: CoreSingleTimeEventObserver;
|
||||||
|
|
||||||
constructor(el: ElementRef<HTMLElement>) {
|
constructor(el: ElementRef<HTMLElement>) {
|
||||||
this.element = el.nativeElement;
|
this.element = el.nativeElement;
|
||||||
|
@ -95,7 +96,8 @@ export class CoreCollapsibleItemDirective implements OnInit, OnDestroy {
|
||||||
* @return Promise resolved when loadings are done.
|
* @return Promise resolved when loadings are done.
|
||||||
*/
|
*/
|
||||||
protected async waitLoadingsDone(): Promise<void> {
|
protected async waitLoadingsDone(): Promise<void> {
|
||||||
await CoreDomUtils.waitToBeInDOM(this.element);
|
this.domListener = CoreDomUtils.waitToBeInDOM(this.element);
|
||||||
|
await this.domListener.promise;
|
||||||
|
|
||||||
const page = this.element.closest('.ion-page');
|
const page = this.element.closest('.ion-page');
|
||||||
|
|
||||||
|
@ -240,6 +242,7 @@ export class CoreCollapsibleItemDirective implements OnInit, OnDestroy {
|
||||||
*/
|
*/
|
||||||
ngOnDestroy(): void {
|
ngOnDestroy(): void {
|
||||||
this.resizeListener?.off();
|
this.resizeListener?.off();
|
||||||
|
this.domListener?.off();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,7 @@ import {
|
||||||
Optional,
|
Optional,
|
||||||
ViewContainerRef,
|
ViewContainerRef,
|
||||||
ViewChild,
|
ViewChild,
|
||||||
|
OnDestroy,
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
import { IonContent } from '@ionic/angular';
|
import { IonContent } from '@ionic/angular';
|
||||||
|
|
||||||
|
@ -41,6 +42,7 @@ import { CoreFilterHelper } from '@features/filter/services/filter-helper';
|
||||||
import { CoreSubscriptions } from '@singletons/subscriptions';
|
import { CoreSubscriptions } from '@singletons/subscriptions';
|
||||||
import { CoreComponentsRegistry } from '@singletons/components-registry';
|
import { CoreComponentsRegistry } from '@singletons/components-registry';
|
||||||
import { CoreCollapsibleItemDirective } from './collapsible-item';
|
import { CoreCollapsibleItemDirective } from './collapsible-item';
|
||||||
|
import { CoreSingleTimeEventObserver } from '@singletons/events';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Directive to format text rendered. It renders the HTML and treats all links and media, using CoreLinkDirective
|
* Directive to format text rendered. It renders the HTML and treats all links and media, using CoreLinkDirective
|
||||||
|
@ -54,7 +56,7 @@ import { CoreCollapsibleItemDirective } from './collapsible-item';
|
||||||
@Directive({
|
@Directive({
|
||||||
selector: 'core-format-text',
|
selector: 'core-format-text',
|
||||||
})
|
})
|
||||||
export class CoreFormatTextDirective implements OnChanges {
|
export class CoreFormatTextDirective implements OnChanges, OnDestroy {
|
||||||
|
|
||||||
@ViewChild(CoreCollapsibleItemDirective) collapsible?: CoreCollapsibleItemDirective;
|
@ViewChild(CoreCollapsibleItemDirective) collapsible?: CoreCollapsibleItemDirective;
|
||||||
|
|
||||||
|
@ -88,6 +90,7 @@ export class CoreFormatTextDirective implements OnChanges {
|
||||||
protected element: HTMLElement;
|
protected element: HTMLElement;
|
||||||
protected emptyText = '';
|
protected emptyText = '';
|
||||||
protected contentSpan: HTMLElement;
|
protected contentSpan: HTMLElement;
|
||||||
|
protected domListener?: CoreSingleTimeEventObserver;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
element: ElementRef,
|
element: ElementRef,
|
||||||
|
@ -126,6 +129,13 @@ export class CoreFormatTextDirective implements OnChanges {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
this.domListener?.off();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wait until the text is fully rendered.
|
* Wait until the text is fully rendered.
|
||||||
*/
|
*/
|
||||||
|
@ -546,7 +556,8 @@ export class CoreFormatTextDirective implements OnChanges {
|
||||||
* @return The width of the element in pixels.
|
* @return The width of the element in pixels.
|
||||||
*/
|
*/
|
||||||
protected async getElementWidth(): Promise<number> {
|
protected async getElementWidth(): Promise<number> {
|
||||||
await CoreUtils.ignoreErrors(CoreDomUtils.waitToBeInDOM(this.element));
|
this.domListener = CoreDomUtils.waitToBeInDOM(this.element);
|
||||||
|
await this.domListener.promise;
|
||||||
|
|
||||||
let width = this.element.getBoundingClientRect().width;
|
let width = this.element.getBoundingClientRect().width;
|
||||||
if (!width) {
|
if (!width) {
|
||||||
|
@ -700,7 +711,7 @@ export class CoreFormatTextDirective implements OnChanges {
|
||||||
let width: string | number;
|
let width: string | number;
|
||||||
let height: string | number;
|
let height: string | number;
|
||||||
|
|
||||||
await CoreDomUtils.waitToBeInDOM(iframe);
|
await CoreDomUtils.waitToBeInDOM(iframe, 5000).promise;
|
||||||
|
|
||||||
if (iframe.width) {
|
if (iframe.width) {
|
||||||
width = iframe.width;
|
width = iframe.width;
|
||||||
|
|
|
@ -36,7 +36,7 @@ import { CoreDomUtils } from '@services/utils/dom';
|
||||||
import { CoreUrlUtils } from '@services/utils/url';
|
import { CoreUrlUtils } from '@services/utils/url';
|
||||||
import { CoreUtils } from '@services/utils/utils';
|
import { CoreUtils } from '@services/utils/utils';
|
||||||
import { Platform, Translate } from '@singletons';
|
import { Platform, Translate } from '@singletons';
|
||||||
import { CoreEventFormActionData, CoreEventObserver, CoreEvents } from '@singletons/events';
|
import { CoreEventFormActionData, CoreEventObserver, CoreEvents, CoreSingleTimeEventObserver } from '@singletons/events';
|
||||||
import { CoreEditorOffline } from '../../services/editor-offline';
|
import { CoreEditorOffline } from '../../services/editor-offline';
|
||||||
import { CoreComponentsRegistry } from '@singletons/components-registry';
|
import { CoreComponentsRegistry } from '@singletons/components-registry';
|
||||||
import { CoreLoadingComponent } from '@components/loading/loading';
|
import { CoreLoadingComponent } from '@components/loading/loading';
|
||||||
|
@ -104,6 +104,7 @@ export class CoreEditorRichTextEditorComponent implements OnInit, AfterContentIn
|
||||||
protected selectionChangeFunction?: () => void;
|
protected selectionChangeFunction?: () => void;
|
||||||
protected languageChangedSubscription?: Subscription;
|
protected languageChangedSubscription?: Subscription;
|
||||||
protected resizeListener?: CoreEventObserver;
|
protected resizeListener?: CoreEventObserver;
|
||||||
|
protected domListener?: CoreSingleTimeEventObserver;
|
||||||
|
|
||||||
rteEnabled = false;
|
rteEnabled = false;
|
||||||
isPhone = false;
|
isPhone = false;
|
||||||
|
@ -249,9 +250,6 @@ export class CoreEditorRichTextEditorComponent implements OnInit, AfterContentIn
|
||||||
this.windowResized();
|
this.windowResized();
|
||||||
}, 50);
|
}, 50);
|
||||||
|
|
||||||
// Start observing the target node for configured mutations
|
|
||||||
this.resizeObserver?.observe(this.element);
|
|
||||||
|
|
||||||
document.addEventListener('selectionchange', this.selectionChangeFunction = this.updateToolbarStyles.bind(this));
|
document.addEventListener('selectionchange', this.selectionChangeFunction = this.updateToolbarStyles.bind(this));
|
||||||
|
|
||||||
this.keyboardObserver = CoreEvents.on(CoreEvents.KEYBOARD_CHANGE, () => {
|
this.keyboardObserver = CoreEvents.on(CoreEvents.KEYBOARD_CHANGE, () => {
|
||||||
|
@ -289,7 +287,8 @@ export class CoreEditorRichTextEditorComponent implements OnInit, AfterContentIn
|
||||||
* @return Promise resolved when loadings are done.
|
* @return Promise resolved when loadings are done.
|
||||||
*/
|
*/
|
||||||
protected async waitLoadingsDone(): Promise<void> {
|
protected async waitLoadingsDone(): Promise<void> {
|
||||||
await CoreDomUtils.waitToBeInDOM(this.element);
|
this.domListener = CoreDomUtils.waitToBeInDOM(this.element);
|
||||||
|
await this.domListener.promise;
|
||||||
|
|
||||||
const page = this.element.closest('.ion-page');
|
const page = this.element.closest('.ion-page');
|
||||||
|
|
||||||
|
@ -829,7 +828,7 @@ export class CoreEditorRichTextEditorComponent implements OnInit, AfterContentIn
|
||||||
|
|
||||||
const length = await this.toolbarSlides.length();
|
const length = await this.toolbarSlides.length();
|
||||||
|
|
||||||
await CoreDomUtils.waitToBeInDOM(this.toolbar.nativeElement);
|
await CoreDomUtils.waitToBeInDOM(this.toolbar.nativeElement, 5000).promise;
|
||||||
|
|
||||||
const width = this.toolbar.nativeElement.getBoundingClientRect().width;
|
const width = this.toolbar.nativeElement.getBoundingClientRect().width;
|
||||||
|
|
||||||
|
@ -1075,7 +1074,7 @@ export class CoreEditorRichTextEditorComponent implements OnInit, AfterContentIn
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component being destroyed.
|
* @inheritdoc
|
||||||
*/
|
*/
|
||||||
ngOnDestroy(): void {
|
ngOnDestroy(): void {
|
||||||
this.valueChangeSubscription?.unsubscribe();
|
this.valueChangeSubscription?.unsubscribe();
|
||||||
|
@ -1088,6 +1087,7 @@ export class CoreEditorRichTextEditorComponent implements OnInit, AfterContentIn
|
||||||
this.keyboardObserver?.off();
|
this.keyboardObserver?.off();
|
||||||
this.labelObserver?.disconnect();
|
this.labelObserver?.disconnect();
|
||||||
this.resizeListener?.off();
|
this.resizeListener?.off();
|
||||||
|
this.domListener?.off();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,7 +53,7 @@ import { NavigationStart } from '@angular/router';
|
||||||
import { filter } from 'rxjs/operators';
|
import { filter } from 'rxjs/operators';
|
||||||
import { Subscription } from 'rxjs';
|
import { Subscription } from 'rxjs';
|
||||||
import { CoreComponentsRegistry } from '@singletons/components-registry';
|
import { CoreComponentsRegistry } from '@singletons/components-registry';
|
||||||
import { CoreEventObserver } from '@singletons/events';
|
import { CoreEventObserver, CoreSingleTimeEventObserver } from '@singletons/events';
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* "Utils" service with helper functions for UI, DOM elements and HTML code.
|
* "Utils" service with helper functions for UI, DOM elements and HTML code.
|
||||||
|
@ -96,36 +96,52 @@ export class CoreDomUtilsProvider {
|
||||||
* Wait an element to be in dom of another element.
|
* Wait an element to be in dom of another element.
|
||||||
*
|
*
|
||||||
* @param element Element to wait.
|
* @param element Element to wait.
|
||||||
* @return Promise resolved when added. It will be rejected after a timeout of 5s.
|
* @param timeout If defined, timeout to wait before rejecting the promise.
|
||||||
|
* @return Promise CoreSingleTimeEventObserver with a promise.
|
||||||
*/
|
*/
|
||||||
async waitToBeInDOM(
|
waitToBeInDOM(
|
||||||
element: Element,
|
element: Element,
|
||||||
): Promise<void> {
|
timeout?: number,
|
||||||
|
): CoreSingleTimeEventObserver {
|
||||||
let root = element.getRootNode({ composed: true });
|
let root = element.getRootNode({ composed: true });
|
||||||
|
|
||||||
if (root === document) {
|
if (root === document) {
|
||||||
// Already in DOM.
|
// Already in DOM.
|
||||||
return;
|
return {
|
||||||
|
off: (): void => {
|
||||||
|
// Nothing to do here.
|
||||||
|
},
|
||||||
|
promise: Promise.resolve(),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
let observer: MutationObserver | undefined;
|
||||||
// Disconnect observer for performance reasons.
|
let observerTimeout: number | undefined;
|
||||||
const timeout = window.setTimeout(() => {
|
if (timeout) {
|
||||||
observer.disconnect();
|
observerTimeout = window.setTimeout(() => {
|
||||||
reject(new Error('Waiting for DOM timeout reached'));
|
observer?.disconnect();
|
||||||
}, 5000);
|
throw new Error('Waiting for DOM timeout reached');
|
||||||
|
}, timeout);
|
||||||
|
}
|
||||||
|
|
||||||
const observer = new MutationObserver(() => {
|
return {
|
||||||
root = element.getRootNode({ composed: true });
|
off: (): void => {
|
||||||
if (root === document) {
|
observer?.disconnect();
|
||||||
observer.disconnect();
|
clearTimeout(observerTimeout);
|
||||||
clearTimeout(timeout);
|
},
|
||||||
resolve();
|
promise: new Promise((resolve) => {
|
||||||
}
|
observer = new MutationObserver(() => {
|
||||||
});
|
root = element.getRootNode({ composed: true });
|
||||||
|
if (root === document) {
|
||||||
|
observer?.disconnect();
|
||||||
|
clearTimeout(observerTimeout);
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
observer.observe(document.body, { subtree: true, childList: true });
|
observer.observe(document.body, { subtree: true, childList: true });
|
||||||
});
|
}),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -31,6 +31,21 @@ export interface CoreEventObserver {
|
||||||
off: () => void;
|
off: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Observer instance to stop listening to an observer.
|
||||||
|
*/
|
||||||
|
export interface CoreSingleTimeEventObserver {
|
||||||
|
/**
|
||||||
|
* Stop the observer.
|
||||||
|
*/
|
||||||
|
off: () => void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Promise Resolved when event is done (first time).
|
||||||
|
*/
|
||||||
|
promise: Promise<void>;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Event payloads.
|
* Event payloads.
|
||||||
*/
|
*/
|
||||||
|
|
Loading…
Reference in New Issue