diff --git a/ui-ngx/src/app/modules/home/menu/menu-link.component.ts b/ui-ngx/src/app/modules/home/menu/menu-link.component.ts index da7a21632b..da8a583183 100644 --- a/ui-ngx/src/app/modules/home/menu/menu-link.component.ts +++ b/ui-ngx/src/app/modules/home/menu/menu-link.component.ts @@ -14,9 +14,8 @@ /// limitations under the License. /// -import { ChangeDetectionStrategy, Component, Input, OnInit, OnChanges, SimpleChanges } from '@angular/core'; +import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core'; import { MenuSection } from '@core/services/menu.models'; -import { tbImageIcon } from '@shared/models/custom-menu.models'; @Component({ selector: 'tb-menu-link', @@ -24,27 +23,14 @@ import { tbImageIcon } from '@shared/models/custom-menu.models'; styleUrls: ['./menu-link.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush }) -export class MenuLinkComponent implements OnInit, OnChanges { +export class MenuLinkComponent implements OnInit { @Input() section: MenuSection; - isCustomIcon: boolean; - constructor() { } ngOnInit() { } - ngOnChanges(changes: SimpleChanges): void { - for (const propName of Object.keys(changes)) { - const change = changes[propName]; - if (change.currentValue !== change.previousValue) { - if (propName === 'section' && change.currentValue) { - this.isCustomIcon = tbImageIcon(change.currentValue.icon); - } - } - } - } - } diff --git a/ui-ngx/src/app/shared/components/icon.component.ts b/ui-ngx/src/app/shared/components/icon.component.ts index 2b6170b71c..36c4e2e0e8 100644 --- a/ui-ngx/src/app/shared/components/icon.component.ts +++ b/ui-ngx/src/app/shared/components/icon.component.ts @@ -33,6 +33,8 @@ import { Subscription } from 'rxjs'; import { take } from 'rxjs/operators'; import { isSvgIcon, splitIconName } from '@shared/models/icon.models'; import { ContentObserver } from '@angular/cdk/observers'; +import { isTbImage } from '@shared/models/resource.models'; +import { ImagePipe } from '@shared/pipe/image.pipe'; const _TbIconBase = mixinColor( class { @@ -70,7 +72,7 @@ const funcIriPattern = /^url\(['"]?#(.*?)['"]?\)$/; host: { role: 'img', class: 'mat-icon notranslate', - '[attr.data-mat-icon-type]': '!_useSvgIcon ? "font" : "svg"', + '[attr.data-mat-icon-type]': '_useSvgIcon ? "svg" : (_useImageIcon ? null : "font")', '[attr.data-mat-icon-name]': '_svgName', '[attr.data-mat-icon-namespace]': '_svgNamespace', '[class.mat-icon-no-color]': 'color !== "primary" && color !== "accent" && color !== "warn"', @@ -99,6 +101,9 @@ export class TbIconComponent extends _TbIconBase private _textElement = null; + _useImageIcon = false; + private _imageElement = null; + private _previousPath?: string; private _elementsWithExternalReferences?: Map; @@ -109,6 +114,7 @@ export class TbIconComponent extends _TbIconBase private contentObserver: ContentObserver, private renderer: Renderer2, private _iconRegistry: MatIconRegistry, + private imagePipe: ImagePipe, @Inject(MAT_ICON_LOCATION) private _location: MatIconLocation, private readonly _errorHandler: ErrorHandler) { super(elementRef); @@ -148,16 +154,29 @@ export class TbIconComponent extends _TbIconBase private _updateIcon() { const useSvgIcon = isSvgIcon(this.icon); + const useImageIcon = isTbImage(this.icon); if (this._useSvgIcon !== useSvgIcon) { this._useSvgIcon = useSvgIcon; if (!this._useSvgIcon) { this._updateSvgIcon(undefined); } else { this._updateFontIcon(undefined); + this._updateImageIcon(undefined); + } + } + if (this._useImageIcon !== useImageIcon) { + this._useImageIcon = useImageIcon; + if (!this._useImageIcon) { + this._updateImageIcon(undefined); + } else { + this._updateFontIcon(undefined); + this._updateSvgIcon(undefined); } } if (this._useSvgIcon) { this._updateSvgIcon(this.icon); + } else if (this._useImageIcon) { + this._updateImageIcon(this.icon); } else { this._updateFontIcon(this.icon); } @@ -278,4 +297,28 @@ export class TbIconComponent extends _TbIconBase } } + private _updateImageIcon(rawName: string | undefined) { + if (rawName) { + this._clearImageIcon(); + this.imagePipe.transform(rawName, { asString: true, ignoreLoadingImage: true }).subscribe( + imageUrl => { + const imgElement = this.renderer.createElement('img'); + this.renderer.setAttribute(imgElement, 'src', imageUrl as string); + const elem: HTMLElement = this._elementRef.nativeElement; + this.renderer.insertBefore(elem, imgElement, this._iconNameContent.nativeElement); + this._imageElement = imgElement; + } + ); + } else { + this._clearImageIcon(); + } + } + + private _clearImageIcon() { + const elem: HTMLElement = this._elementRef.nativeElement; + if (this._imageElement !== null) { + this.renderer.removeChild(elem, this._imageElement); + this._imageElement = null; + } + } } diff --git a/ui-ngx/src/app/shared/components/material-icon-select.component.ts b/ui-ngx/src/app/shared/components/material-icon-select.component.ts index 97c5519ed3..c2e904d8d0 100644 --- a/ui-ngx/src/app/shared/components/material-icon-select.component.ts +++ b/ui-ngx/src/app/shared/components/material-icon-select.component.ts @@ -36,7 +36,7 @@ import { TbPopoverService } from '@shared/components/popover.service'; import { MaterialIconsComponent } from '@shared/components/material-icons.component'; import { MatButton } from '@angular/material/button'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; -import { tbImageIcon } from '@shared/models/custom-menu.models'; +import { isTbImage } from '@shared/models/resource.models'; @Component({ selector: 'tb-material-icon-select', @@ -201,7 +201,7 @@ export class MaterialIconSelectComponent extends PageComponent implements OnInit private defineIconType(icon: string) { if (this.allowedCustomIcon) { - this.isCustomIcon = tbImageIcon(icon); + this.isCustomIcon = isTbImage(icon); } } } diff --git a/ui-ngx/src/app/shared/components/material-icons.component.html b/ui-ngx/src/app/shared/components/material-icons.component.html index dcd285a99d..558ffd5e0a 100644 --- a/ui-ngx/src/app/shared/components/material-icons.component.html +++ b/ui-ngx/src/app/shared/components/material-icons.component.html @@ -16,15 +16,16 @@ -->
-
icon.icons
- @if (allowedCustomIcon) { -
+
+ icon.icons + @if (allowedCustomIcon) { {{ 'resource.system' | translate }} {{ 'icon.custom' | translate }} -
- } + + } +
@if (!isCustomIcon) { search diff --git a/ui-ngx/src/app/shared/components/material-icons.component.ts b/ui-ngx/src/app/shared/components/material-icons.component.ts index 6fe4c995e5..d144fd45e6 100644 --- a/ui-ngx/src/app/shared/components/material-icons.component.ts +++ b/ui-ngx/src/app/shared/components/material-icons.component.ts @@ -37,7 +37,7 @@ import { TbPopoverComponent } from '@shared/components/popover.component'; import { BreakpointObserver } from '@angular/cdk/layout'; import { MediaBreakpoints } from '@shared/models/constants'; import { coerceBoolean } from '@shared/decorators/coercion'; -import { tbImageIcon } from '@shared/models/custom-menu.models'; +import { isTbImage } from '@shared/models/resource.models'; @Component({ selector: 'tb-material-icons', @@ -129,7 +129,7 @@ export class MaterialIconsComponent extends PageComponent implements OnInit { map((data) => data.iconRows), share() ); - this.isCustomIcon = tbImageIcon(this.selectedIcon) + this.isCustomIcon = isTbImage(this.selectedIcon) } clearSearch() { diff --git a/ui-ngx/src/app/shared/models/resource.models.ts b/ui-ngx/src/app/shared/models/resource.models.ts index 0419e40a7c..c1e692e04f 100644 --- a/ui-ngx/src/app/shared/models/resource.models.ts +++ b/ui-ngx/src/app/shared/models/resource.models.ts @@ -188,6 +188,7 @@ export const isImageResourceUrl = (url: string): boolean => url && IMAGES_URL_RE export const isJSResourceUrl = (url: string): boolean => url && RESOURCES_URL_REGEXP.test(url); export const isJSResource = (url: string): boolean => url?.startsWith(TB_RESOURCE_PREFIX); +export const isTbImage = (url: string): boolean => url?.startsWith(TB_IMAGE_PREFIX); export const extractParamsFromImageResourceUrl = (url: string): {type: ImageResourceType; key: string} => { const res = url.match(IMAGES_URL_REGEXP); diff --git a/ui-ngx/src/styles.scss b/ui-ngx/src/styles.scss index a8cf53534e..6d49e9f698 100644 --- a/ui-ngx/src/styles.scss +++ b/ui-ngx/src/styles.scss @@ -896,7 +896,7 @@ pre.tb-highlight { } .mat-icon { - svg { + svg, img { vertical-align: inherit; } &.tb-mat-12 {