fix: parallax replacer might fail to find shadow root on its first try

This commit is contained in:
2023-07-25 16:17:54 +02:00
committed by Rainer Killinger
parent 23481d0d73
commit 2f59ab9707
5 changed files with 59 additions and 36 deletions

View File

@@ -13,6 +13,7 @@
* this program. If not, see <https://www.gnu.org/licenses/>.
*/
import {Directive, ElementRef, HostBinding, Input, OnDestroy, OnInit} from '@angular/core';
import {waitForElement} from './ion-icon/shadow-attacher';
type IonicColor =
| 'danger'
@@ -86,31 +87,39 @@ export class IonContentParallaxDirective implements OnInit, OnDestroy {
constructor(private element: ElementRef) {}
ngOnInit() {
this.mutationObserver = new MutationObserver(() => {
const inner = this.element.nativeElement.shadowRoot.querySelector('.inner-scroll') as
| HTMLDivElement
| undefined;
if (!inner) return;
// eslint-disable-next-line unicorn/no-array-for-each
inner.childNodes.forEach(node => node.remove());
inner.part.add('parallax-scroll');
const parallaxParentElement = document.createElement('div');
parallaxParentElement.part.add('parallax-parent');
const parallaxElement = document.createElement('div');
parallaxElement.part.add('parallax');
inner.append(parallaxParentElement);
parallaxParentElement.append(parallaxElement, document.createElement('slot'));
});
this.mutationObserver.observe(this.element.nativeElement.shadowRoot, {
childList: true,
});
async ngOnInit() {
this.mutationObserver = new MutationObserver(this.replace.bind(this));
const element = this.element.nativeElement;
if (element.shadowRoot) {
this.mutationObserver.observe(element.shadowRoot, {childList: true});
} else {
console.warn("Shadow root didn't exist for parallax, retrying...");
await waitForElement(() => this.element.nativeElement.shadowRoot);
this.mutationObserver.observe(element.shadowRoot, {childList: true});
this.replace();
}
}
ngOnDestroy() {
this.mutationObserver.disconnect();
}
replace() {
const inner = this.element.nativeElement.shadowRoot.querySelector('.inner-scroll') as
| HTMLDivElement
| undefined;
if (!inner) return;
// eslint-disable-next-line unicorn/no-array-for-each
inner.childNodes.forEach(node => node.remove());
inner.part.add('parallax-scroll');
const parallaxParentElement = document.createElement('div');
parallaxParentElement.part.add('parallax-parent');
const parallaxElement = document.createElement('div');
parallaxElement.part.add('parallax');
inner.append(parallaxParentElement);
parallaxParentElement.append(parallaxElement, document.createElement('slot'));
}
}

View File

@@ -16,6 +16,7 @@
import {ComponentRef, Directive, ElementRef, OnDestroy, OnInit, ViewContainerRef} from '@angular/core';
import {IonIcon} from '@ionic/angular';
import {IonIconDirective} from './ion-icon.directive';
import {waitForElement} from './shadow-attacher';
export type IconData = Omit<
Partial<IonIconDirective>,
@@ -79,25 +80,15 @@ export abstract class IconReplacer implements OnInit, OnDestroy {
// noop
}
ngOnInit() {
async ngOnInit() {
this.init();
if (this.host) {
this.attachObserver();
} else {
let tries = 0;
console.warn('IconReplacer: host not found, trying again');
const interval = setInterval(() => {
if (tries > this.maxAttempts) {
clearInterval(interval);
throw new Error('IconReplacer: host not found');
}
if (this.host) {
clearInterval(interval);
this.replace();
}
tries++;
}, this.retryAfterMs);
console.warn("Shadow root didn't exist for ion icon replacer, retrying...");
await waitForElement(() => this.host);
this.replace();
}
}

View File

@@ -0,0 +1,23 @@
/**
* Waits for an element to appear
*
* In normal circumstances *please* use the mutation observer for these kinds of tasks.
* However, shadowDom attachment is **not** covered by the mutation observer,
* so a solution like this is required.
*/
export async function waitForElement(query: () => HTMLElement, maxAttempts = 10, retryAfterMs = 10) {
let tries = 0;
return new Promise<void>(resolve => {
const interval = setInterval(() => {
if (tries > maxAttempts) {
clearInterval(interval);
throw new Error('Element not found');
}
if (query()) {
clearInterval(interval);
resolve();
}
tries++;
}, retryAfterMs);
});
}