Files
openstapps/src/app/util/ion-icon/replace-util.ts
Thea Schöbl 0d755bcbd3 fix: correct data path color
fix: missing ionic component icons
feat: parallax detail background
2022-09-08 07:23:58 +00:00

169 lines
4.3 KiB
TypeScript

/*
* Copyright (C) 2022 StApps
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <https://www.gnu.org/licenses/>.
*/
import {
ComponentRef,
Directive,
ElementRef,
OnDestroy,
OnInit,
ViewContainerRef,
} from '@angular/core';
import {IonIcon} from '@ionic/angular';
import {IonIconDirective} from './ion-icon.directive';
export type IconData = Omit<
Partial<IonIconDirective>,
| 'ngOnChanges'
| 'ngOnInit'
| 'viewContainerRef'
| 'ngOnDestroy'
| 'element'
| 'ionIcon'
| 'disableProperty'
>;
/**
* A utility class to replace ion-icons in other ionic components.
*/
@Directive()
export abstract class IconReplacer implements OnInit, OnDestroy {
private mutationObserver: MutationObserver;
protected slotName = 'sc-icon';
protected maxAttempts = 10;
protected retryAfterMs = 10;
/**
* The host element
*
* This will be either element.nativeElement.shadowRoot or element.nativeElement
* depending on the iconDomLocation
*/
protected get host() {
return this.iconDomLocation === 'shadow'
? this.element.nativeElement.shadowRoot
: this.element.nativeElement;
}
/**
* @param element The host element
* @param viewContainerRef The view container ref
* @param iconDomLocation If the icon is placed inside the shadow dom or not
* @protected
*/
protected constructor(
private readonly element: ElementRef,
private readonly viewContainerRef: ViewContainerRef,
private readonly iconDomLocation: 'shadow' | 'light',
) {}
/**
* Replace the icons here
*/
abstract replace(): void;
/**
* If any additional work needs to be done, this
* is called during ngOnInit
*/
init() {
// noop
}
/**
* If you need to do cleanup, this method is called during ngOnDestroy
*/
destroy() {
// noop
}
ngOnInit() {
this.init();
if (!this.host) {
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);
} else {
this.attachObserver();
}
}
private attachObserver() {
this.mutationObserver = new MutationObserver(() => this.replace());
this.mutationObserver.observe(this.host, {
childList: true,
});
}
replaceIcon(parent: HTMLElement | null, iconData: IconData, slotName = '') {
if (!parent) return;
const icon = parent.querySelector('ion-icon');
if (!icon) return;
const scIcon = this.createIcon(iconData);
// @ts-expect-error can be spread
scIcon.location.nativeElement.classList.add(...icon.classList);
if (this.iconDomLocation === 'shadow') {
// shadow dom needs to utilize slotting, to put it outside
// the shadow dom, otherwise it won't receive any css data
const slot = document.createElement('slot');
slot.name = this.slotName + slotName;
icon.replaceWith(slot);
scIcon.location.nativeElement.slot = this.slotName + slotName;
this.element.nativeElement.append(scIcon.location.nativeElement);
} else {
icon.replaceWith(scIcon.location.nativeElement);
}
}
private createIcon(iconData: IconData): ComponentRef<IonIcon> {
const ionIcon = this.viewContainerRef.createComponent(IonIcon, {});
const iconDirective = new IonIconDirective(
ionIcon.location,
this.viewContainerRef,
ionIcon.instance,
);
for (const key in iconData) {
// @ts-expect-error type mismatch
iconDirective[key] = iconData[key];
}
iconDirective.ngOnInit();
return ionIcon;
}
ngOnDestroy() {
this.mutationObserver?.disconnect();
this.destroy();
}
}