/* * 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 . */ 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; 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, ); } }