Update tb-icon to accept custom image.
This commit is contained in:
		
							parent
							
								
									a3c28e9db4
								
							
						
					
					
						commit
						20e44ab004
					
				@ -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);
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -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<Element, {name: string; value: string}[]>;
 | 
			
		||||
@ -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;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -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);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -16,15 +16,16 @@
 | 
			
		||||
 | 
			
		||||
-->
 | 
			
		||||
<div class="tb-material-icons-panel">
 | 
			
		||||
  <div *ngIf="showTitle" class="tb-material-icons-title" translate>icon.icons</div>
 | 
			
		||||
  <div class="flex w-full">
 | 
			
		||||
    <span *ngIf="showTitle" class="tb-material-icons-title flex-1" translate>icon.icons</span>
 | 
			
		||||
    @if (allowedCustomIcon) {
 | 
			
		||||
    <div class="flex w-full flex-row items-center justify-end">
 | 
			
		||||
      <tb-toggle-select [(ngModel)]="isCustomIcon" [ngModelOptions]="{standalone: true}" (ngModelChange)="selectedIcon = null">
 | 
			
		||||
        <tb-toggle-option [value]="false">{{ 'resource.system' | translate }}</tb-toggle-option>
 | 
			
		||||
        <tb-toggle-option [value]="true">{{ 'icon.custom' | translate }}</tb-toggle-option>
 | 
			
		||||
      </tb-toggle-select>
 | 
			
		||||
    </div>
 | 
			
		||||
      <span class="flex-1"></span>
 | 
			
		||||
    }
 | 
			
		||||
  </div>
 | 
			
		||||
  @if (!isCustomIcon) {
 | 
			
		||||
    <mat-form-field class="tb-material-icons-search tb-inline-field" appearance="outline" subscriptSizing="dynamic">
 | 
			
		||||
      <mat-icon matPrefix>search</mat-icon>
 | 
			
		||||
 | 
			
		||||
@ -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() {
 | 
			
		||||
 | 
			
		||||
@ -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);
 | 
			
		||||
 | 
			
		||||
@ -896,7 +896,7 @@ pre.tb-highlight {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .mat-icon {
 | 
			
		||||
    svg {
 | 
			
		||||
    svg, img {
 | 
			
		||||
      vertical-align: inherit;
 | 
			
		||||
    }
 | 
			
		||||
    &.tb-mat-12 {
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user