Files
openstapps/src/app/util/ion-icon/ion-icon.directive.ts
2022-08-19 11:48:34 +00:00

134 lines
3.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,
Host,
Input,
OnChanges,
OnDestroy,
OnInit,
Optional,
Self,
ViewContainerRef,
} from '@angular/core';
import {IconComponent} from './icon.component';
import {IonIcon} from '@ionic/angular';
// eslint-disable-next-line @typescript-eslint/no-empty-function
const noop = () => {};
const noopProperty = {
set: noop,
get: noop,
};
@Directive({
selector: 'ion-icon',
})
export class IonIconDirective implements OnInit, OnDestroy, OnChanges {
@Input() name: string;
@Input() md: string;
@Input() ios: string;
@Input() fill = false;
@Input() weight: number;
@Input() size: number;
@Input() grade: number;
private mutationObserver: MutationObserver;
iconComponent?: ComponentRef<IconComponent>;
private static get mode(): 'md' | 'ios' {
return document.querySelector(':root')?.getAttribute('mode') as
| 'md'
| 'ios';
}
constructor(
private element: ElementRef,
private viewContainerRef: ViewContainerRef,
@Host() @Self() @Optional() private ionIcon: IonIcon,
) {}
ngOnInit() {
this.iconComponent = this.viewContainerRef.createComponent(
IconComponent,
{},
);
this.element.nativeElement.insertBefore(
this.iconComponent.location.nativeElement,
this.element.nativeElement.firstChild,
);
this.mutationObserver = new MutationObserver(() => {
const inner =
this.element.nativeElement.shadowRoot.querySelector('.icon-inner');
if (!inner) return;
inner.insertBefore(document.createElement('slot'), inner.firstChild);
});
this.mutationObserver.observe(this.element.nativeElement.shadowRoot, {
childList: true,
});
this.ngOnChanges();
// this will effectively completely disable the ion-icon component
for (const name of ['src', 'name', 'icon', 'md', 'ios']) {
this.disableProperty(name);
}
}
ngOnDestroy() {
this.mutationObserver.disconnect();
}
ngOnChanges() {
if (!this.iconComponent) return;
for (const key of ['name', 'weight', 'fill', 'size', 'grade'] as Array<
keyof IconComponent & keyof IonIconDirective
>) {
// @ts-expect-error type mismatch
this.iconComponent.instance[key] = this[key];
}
for (const mode of ['md', 'ios'] as Array<'md' | 'ios'>) {
if (this[mode] && IonIconDirective.mode === mode) {
this.iconComponent.instance.name = this[mode];
}
}
if (this.size) {
this.element.nativeElement.style.cssText = `font-size: ${this.size}px;`;
}
}
disableProperty(name: string) {
Object.defineProperty(
Object.getPrototypeOf((this.ionIcon as unknown as {el: HTMLElement}).el),
name,
noopProperty,
);
}
}