mirror of
https://gitlab.com/openstapps/openstapps.git
synced 2026-01-03 12:02:53 +00:00
fix: parallax replacer might fail to find shadow root on its first try
This commit is contained in:
@@ -13,6 +13,7 @@
|
|||||||
* this program. If not, see <https://www.gnu.org/licenses/>.
|
* this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
import {Directive, ElementRef, HostBinding, Input, OnDestroy, OnInit} from '@angular/core';
|
import {Directive, ElementRef, HostBinding, Input, OnDestroy, OnInit} from '@angular/core';
|
||||||
|
import {waitForElement} from './ion-icon/shadow-attacher';
|
||||||
|
|
||||||
type IonicColor =
|
type IonicColor =
|
||||||
| 'danger'
|
| 'danger'
|
||||||
@@ -86,31 +87,39 @@ export class IonContentParallaxDirective implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
constructor(private element: ElementRef) {}
|
constructor(private element: ElementRef) {}
|
||||||
|
|
||||||
ngOnInit() {
|
async ngOnInit() {
|
||||||
this.mutationObserver = new MutationObserver(() => {
|
this.mutationObserver = new MutationObserver(this.replace.bind(this));
|
||||||
const inner = this.element.nativeElement.shadowRoot.querySelector('.inner-scroll') as
|
const element = this.element.nativeElement;
|
||||||
| HTMLDivElement
|
if (element.shadowRoot) {
|
||||||
| undefined;
|
this.mutationObserver.observe(element.shadowRoot, {childList: true});
|
||||||
if (!inner) return;
|
} else {
|
||||||
|
console.warn("Shadow root didn't exist for parallax, retrying...");
|
||||||
// eslint-disable-next-line unicorn/no-array-for-each
|
await waitForElement(() => this.element.nativeElement.shadowRoot);
|
||||||
inner.childNodes.forEach(node => node.remove());
|
this.mutationObserver.observe(element.shadowRoot, {childList: true});
|
||||||
|
this.replace();
|
||||||
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,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy() {
|
ngOnDestroy() {
|
||||||
this.mutationObserver.disconnect();
|
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'));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
import {ComponentRef, Directive, ElementRef, OnDestroy, OnInit, ViewContainerRef} from '@angular/core';
|
import {ComponentRef, Directive, ElementRef, OnDestroy, OnInit, ViewContainerRef} from '@angular/core';
|
||||||
import {IonIcon} from '@ionic/angular';
|
import {IonIcon} from '@ionic/angular';
|
||||||
import {IonIconDirective} from './ion-icon.directive';
|
import {IonIconDirective} from './ion-icon.directive';
|
||||||
|
import {waitForElement} from './shadow-attacher';
|
||||||
|
|
||||||
export type IconData = Omit<
|
export type IconData = Omit<
|
||||||
Partial<IonIconDirective>,
|
Partial<IonIconDirective>,
|
||||||
@@ -79,25 +80,15 @@ export abstract class IconReplacer implements OnInit, OnDestroy {
|
|||||||
// noop
|
// noop
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit() {
|
async ngOnInit() {
|
||||||
this.init();
|
this.init();
|
||||||
|
|
||||||
if (this.host) {
|
if (this.host) {
|
||||||
this.attachObserver();
|
this.attachObserver();
|
||||||
} else {
|
} else {
|
||||||
let tries = 0;
|
console.warn("Shadow root didn't exist for ion icon replacer, retrying...");
|
||||||
console.warn('IconReplacer: host not found, trying again');
|
await waitForElement(() => this.host);
|
||||||
const interval = setInterval(() => {
|
this.replace();
|
||||||
if (tries > this.maxAttempts) {
|
|
||||||
clearInterval(interval);
|
|
||||||
throw new Error('IconReplacer: host not found');
|
|
||||||
}
|
|
||||||
if (this.host) {
|
|
||||||
clearInterval(interval);
|
|
||||||
this.replace();
|
|
||||||
}
|
|
||||||
tries++;
|
|
||||||
}, this.retryAfterMs);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
23
frontend/app/src/app/util/ion-icon/shadow-attacher.ts
Normal file
23
frontend/app/src/app/util/ion-icon/shadow-attacher.ts
Normal 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);
|
||||||
|
});
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user