Merge pull request #13972 from deaflynx/feature/custom-icons
Feature custom icon image
This commit is contained in:
		
						commit
						5d024e1277
					
				@ -71,6 +71,7 @@
 | 
				
			|||||||
            </mat-form-field>
 | 
					            </mat-form-field>
 | 
				
			||||||
            <tb-material-icon-select asBoxInput
 | 
					            <tb-material-icon-select asBoxInput
 | 
				
			||||||
                                     iconClearButton
 | 
					                                     iconClearButton
 | 
				
			||||||
 | 
					                                     allowedCustomIcon
 | 
				
			||||||
                                     [color]="widgetSettings.get('iconColor').value"
 | 
					                                     [color]="widgetSettings.get('iconColor').value"
 | 
				
			||||||
              formControlName="titleIcon">
 | 
					              formControlName="titleIcon">
 | 
				
			||||||
            </tb-material-icon-select>
 | 
					            </tb-material-icon-select>
 | 
				
			||||||
 | 
				
			|||||||
@ -33,6 +33,9 @@ import { Subscription } from 'rxjs';
 | 
				
			|||||||
import { take } from 'rxjs/operators';
 | 
					import { take } from 'rxjs/operators';
 | 
				
			||||||
import { isSvgIcon, splitIconName } from '@shared/models/icon.models';
 | 
					import { isSvgIcon, splitIconName } from '@shared/models/icon.models';
 | 
				
			||||||
import { ContentObserver } from '@angular/cdk/observers';
 | 
					import { ContentObserver } from '@angular/cdk/observers';
 | 
				
			||||||
 | 
					import { isTbImage } from '@shared/models/resource.models';
 | 
				
			||||||
 | 
					import { ImagePipe } from '@shared/pipe/image.pipe';
 | 
				
			||||||
 | 
					import { DomSanitizer } from '@angular/platform-browser';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const _TbIconBase = mixinColor(
 | 
					const _TbIconBase = mixinColor(
 | 
				
			||||||
  class {
 | 
					  class {
 | 
				
			||||||
@ -70,7 +73,7 @@ const funcIriPattern = /^url\(['"]?#(.*?)['"]?\)$/;
 | 
				
			|||||||
  host: {
 | 
					  host: {
 | 
				
			||||||
    role: 'img',
 | 
					    role: 'img',
 | 
				
			||||||
    class: 'mat-icon notranslate',
 | 
					    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-name]': '_svgName',
 | 
				
			||||||
    '[attr.data-mat-icon-namespace]': '_svgNamespace',
 | 
					    '[attr.data-mat-icon-namespace]': '_svgNamespace',
 | 
				
			||||||
    '[class.mat-icon-no-color]': 'color !== "primary" && color !== "accent" && color !== "warn"',
 | 
					    '[class.mat-icon-no-color]': 'color !== "primary" && color !== "accent" && color !== "warn"',
 | 
				
			||||||
@ -99,6 +102,9 @@ export class TbIconComponent extends _TbIconBase
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  private _textElement = null;
 | 
					  private _textElement = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  _useImageIcon = false;
 | 
				
			||||||
 | 
					  private _imageElement = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private _previousPath?: string;
 | 
					  private _previousPath?: string;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private _elementsWithExternalReferences?: Map<Element, {name: string; value: string}[]>;
 | 
					  private _elementsWithExternalReferences?: Map<Element, {name: string; value: string}[]>;
 | 
				
			||||||
@ -109,6 +115,8 @@ export class TbIconComponent extends _TbIconBase
 | 
				
			|||||||
              private contentObserver: ContentObserver,
 | 
					              private contentObserver: ContentObserver,
 | 
				
			||||||
              private renderer: Renderer2,
 | 
					              private renderer: Renderer2,
 | 
				
			||||||
              private _iconRegistry: MatIconRegistry,
 | 
					              private _iconRegistry: MatIconRegistry,
 | 
				
			||||||
 | 
					              private imagePipe: ImagePipe,
 | 
				
			||||||
 | 
					              private sanitizer: DomSanitizer,
 | 
				
			||||||
              @Inject(MAT_ICON_LOCATION) private _location: MatIconLocation,
 | 
					              @Inject(MAT_ICON_LOCATION) private _location: MatIconLocation,
 | 
				
			||||||
              private readonly _errorHandler: ErrorHandler) {
 | 
					              private readonly _errorHandler: ErrorHandler) {
 | 
				
			||||||
    super(elementRef);
 | 
					    super(elementRef);
 | 
				
			||||||
@ -148,16 +156,29 @@ export class TbIconComponent extends _TbIconBase
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  private _updateIcon() {
 | 
					  private _updateIcon() {
 | 
				
			||||||
    const useSvgIcon = isSvgIcon(this.icon);
 | 
					    const useSvgIcon = isSvgIcon(this.icon);
 | 
				
			||||||
 | 
					    const useImageIcon = isTbImage(this.icon);
 | 
				
			||||||
    if (this._useSvgIcon !== useSvgIcon) {
 | 
					    if (this._useSvgIcon !== useSvgIcon) {
 | 
				
			||||||
      this._useSvgIcon = useSvgIcon;
 | 
					      this._useSvgIcon = useSvgIcon;
 | 
				
			||||||
      if (!this._useSvgIcon) {
 | 
					      if (!this._useSvgIcon) {
 | 
				
			||||||
        this._updateSvgIcon(undefined);
 | 
					        this._updateSvgIcon(undefined);
 | 
				
			||||||
      } else {
 | 
					      } else {
 | 
				
			||||||
        this._updateFontIcon(undefined);
 | 
					        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) {
 | 
					    if (this._useSvgIcon) {
 | 
				
			||||||
      this._updateSvgIcon(this.icon);
 | 
					      this._updateSvgIcon(this.icon);
 | 
				
			||||||
 | 
					    } else if (this._useImageIcon) {
 | 
				
			||||||
 | 
					      this._updateImageIcon(this.icon);
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
      this._updateFontIcon(this.icon);
 | 
					      this._updateFontIcon(this.icon);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -278,4 +299,49 @@ 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 urlStr = imageUrl as string;
 | 
				
			||||||
 | 
					          const isSvg = urlStr?.startsWith('data:image/svg+xml') || urlStr?.endsWith('.svg');
 | 
				
			||||||
 | 
					          if (isSvg) {
 | 
				
			||||||
 | 
					            const safeUrl = this.sanitizer.bypassSecurityTrustResourceUrl(urlStr);
 | 
				
			||||||
 | 
					            this._iconRegistry
 | 
				
			||||||
 | 
					              .getSvgIconFromUrl(safeUrl)
 | 
				
			||||||
 | 
					              .pipe(take(1))
 | 
				
			||||||
 | 
					              .subscribe({
 | 
				
			||||||
 | 
					                next: (svg) => {
 | 
				
			||||||
 | 
					                  this.renderer.insertBefore(this._elementRef.nativeElement, svg, this._iconNameContent.nativeElement);
 | 
				
			||||||
 | 
					                  this._imageElement = svg;
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                error: () => this._setImageElement(urlStr)
 | 
				
			||||||
 | 
					              });
 | 
				
			||||||
 | 
					          } else {
 | 
				
			||||||
 | 
					            this._setImageElement(urlStr);
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      this._clearImageIcon();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private _setImageElement(urlStr: string) {
 | 
				
			||||||
 | 
					    const imgElement = this.renderer.createElement('img');
 | 
				
			||||||
 | 
					    this.renderer.addClass(imgElement, 'mat-icon');
 | 
				
			||||||
 | 
					    this.renderer.setAttribute(imgElement, 'alt', 'Image icon');
 | 
				
			||||||
 | 
					    this.renderer.setAttribute(imgElement, 'src', urlStr);
 | 
				
			||||||
 | 
					    this.renderer.insertBefore(this._elementRef.nativeElement, imgElement, this._iconNameContent.nativeElement);
 | 
				
			||||||
 | 
					    this._imageElement = imgElement;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private _clearImageIcon() {
 | 
				
			||||||
 | 
					    const elem: HTMLElement = this._elementRef.nativeElement;
 | 
				
			||||||
 | 
					    if (this._imageElement !== null) {
 | 
				
			||||||
 | 
					      this.renderer.removeChild(elem, this._imageElement);
 | 
				
			||||||
 | 
					      this._imageElement = null;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -71,6 +71,10 @@ export class MaterialIconSelectComponent extends PageComponent implements OnInit
 | 
				
			|||||||
  @coerceBoolean()
 | 
					  @coerceBoolean()
 | 
				
			||||||
  iconClearButton = false;
 | 
					  iconClearButton = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Input()
 | 
				
			||||||
 | 
					  @coerceBoolean()
 | 
				
			||||||
 | 
					  allowedCustomIcon = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private requiredValue: boolean;
 | 
					  private requiredValue: boolean;
 | 
				
			||||||
  get required(): boolean {
 | 
					  get required(): boolean {
 | 
				
			||||||
    return this.requiredValue;
 | 
					    return this.requiredValue;
 | 
				
			||||||
@ -169,7 +173,8 @@ export class MaterialIconSelectComponent extends PageComponent implements OnInit
 | 
				
			|||||||
        this.viewContainerRef, MaterialIconsComponent, 'left', true, null,
 | 
					        this.viewContainerRef, MaterialIconsComponent, 'left', true, null,
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
          selectedIcon: this.materialIconFormGroup.get('icon').value,
 | 
					          selectedIcon: this.materialIconFormGroup.get('icon').value,
 | 
				
			||||||
          iconClearButton: this.iconClearButton
 | 
					          iconClearButton: this.iconClearButton,
 | 
				
			||||||
 | 
					          allowedCustomIcon: this.allowedCustomIcon,
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        {},
 | 
					        {},
 | 
				
			||||||
        {}, {}, true);
 | 
					        {}, {}, true);
 | 
				
			||||||
 | 
				
			|||||||
@ -16,63 +16,86 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
-->
 | 
					-->
 | 
				
			||||||
<div class="tb-material-icons-panel">
 | 
					<div class="tb-material-icons-panel">
 | 
				
			||||||
  <div *ngIf="showTitle" class="tb-material-icons-title" translate>icon.icons</div>
 | 
					  <div class="flex w-full">
 | 
				
			||||||
  <mat-form-field class="tb-material-icons-search tb-inline-field" appearance="outline" subscriptSizing="dynamic">
 | 
					    <span *ngIf="showTitle" class="tb-material-icons-title flex-1" translate>icon.icons</span>
 | 
				
			||||||
    <mat-icon matPrefix>search</mat-icon>
 | 
					    @if (allowedCustomIcon) {
 | 
				
			||||||
    <input matInput [formControl]="searchIconControl" placeholder="{{ 'icon.search-icon' | translate }}"/>
 | 
					      <tb-toggle-select [(ngModel)]="isCustomIcon" [ngModelOptions]="{standalone: true}" (ngModelChange)="selectedIcon = null">
 | 
				
			||||||
    <button *ngIf="searchIconControl.value"
 | 
					        <tb-toggle-option [value]="false">{{ 'resource.system' | translate }}</tb-toggle-option>
 | 
				
			||||||
            type="button"
 | 
					        <tb-toggle-option [value]="true">{{ 'icon.custom' | translate }}</tb-toggle-option>
 | 
				
			||||||
            matSuffix mat-icon-button aria-label="Clear"
 | 
					      </tb-toggle-select>
 | 
				
			||||||
            (click)="clearSearch()">
 | 
					      <span class="flex-1"></span>
 | 
				
			||||||
      <mat-icon class="material-icons">close</mat-icon>
 | 
					    }
 | 
				
			||||||
    </button>
 | 
					 | 
				
			||||||
  </mat-form-field>
 | 
					 | 
				
			||||||
  <cdk-virtual-scroll-viewport [class.!hidden]="notFound" #iconsPanel
 | 
					 | 
				
			||||||
                               [itemSize]="iconsRowHeight" class="tb-material-icons-viewport"
 | 
					 | 
				
			||||||
                               [style.width]="iconsPanelWidth"
 | 
					 | 
				
			||||||
                               [style.height]="iconsPanelHeight">
 | 
					 | 
				
			||||||
    <div *cdkVirtualFor="let iconRow of iconRows$ | async" class="tb-material-icons-row">
 | 
					 | 
				
			||||||
      <ng-container *ngFor="let icon of iconRow">
 | 
					 | 
				
			||||||
        <button *ngIf="icon.name === selectedIcon"
 | 
					 | 
				
			||||||
                class="tb-select-icon-button"
 | 
					 | 
				
			||||||
                mat-raised-button
 | 
					 | 
				
			||||||
                color="primary"
 | 
					 | 
				
			||||||
                (click)="selectIcon(icon)"
 | 
					 | 
				
			||||||
                matTooltip="{{ icon.displayName }}"
 | 
					 | 
				
			||||||
                matTooltipPosition="above"
 | 
					 | 
				
			||||||
                type="button">
 | 
					 | 
				
			||||||
          <tb-icon matButtonIcon>{{icon.name}}</tb-icon>
 | 
					 | 
				
			||||||
        </button>
 | 
					 | 
				
			||||||
        <button *ngIf="icon.name !== selectedIcon"
 | 
					 | 
				
			||||||
                class="tb-select-icon-button"
 | 
					 | 
				
			||||||
                mat-button
 | 
					 | 
				
			||||||
                (click)="selectIcon(icon)"
 | 
					 | 
				
			||||||
                matTooltip="{{ icon.displayName }}"
 | 
					 | 
				
			||||||
                matTooltipPosition="above"
 | 
					 | 
				
			||||||
                type="button">
 | 
					 | 
				
			||||||
          <tb-icon matButtonIcon>{{icon.name}}</tb-icon>
 | 
					 | 
				
			||||||
        </button>
 | 
					 | 
				
			||||||
      </ng-container>
 | 
					 | 
				
			||||||
    </div>
 | 
					 | 
				
			||||||
  </cdk-virtual-scroll-viewport>
 | 
					 | 
				
			||||||
  <ng-container *ngIf="notFound">
 | 
					 | 
				
			||||||
    <div class="tb-no-data-available" [style.width]="iconsPanelWidth">
 | 
					 | 
				
			||||||
      <div class="tb-no-data-bg"></div>
 | 
					 | 
				
			||||||
      <div class="tb-no-data-text">{{ 'icon.no-icons-found' | translate:{iconSearch: searchIconControl.value} }}</div>
 | 
					 | 
				
			||||||
    </div>
 | 
					 | 
				
			||||||
  </ng-container>
 | 
					 | 
				
			||||||
  <div class="tb-material-icons-panel-buttons" *ngIf="iconClearButton || !showAllSubject.value">
 | 
					 | 
				
			||||||
    <button *ngIf="iconClearButton"
 | 
					 | 
				
			||||||
            mat-button
 | 
					 | 
				
			||||||
            color="primary"
 | 
					 | 
				
			||||||
            type="button"
 | 
					 | 
				
			||||||
            (click)="clearIcon()"
 | 
					 | 
				
			||||||
            [disabled]="!selectedIcon">
 | 
					 | 
				
			||||||
      {{ 'action.clear' | translate }}
 | 
					 | 
				
			||||||
    </button>
 | 
					 | 
				
			||||||
    <span class="flex-1"></span>
 | 
					 | 
				
			||||||
    <button *ngIf="!showAllSubject.value" class="tb-material-icons-show-more" mat-button color="primary" (click)="showAllSubject.next(true)">
 | 
					 | 
				
			||||||
      {{ 'action.show-more' | translate }}
 | 
					 | 
				
			||||||
    </button>
 | 
					 | 
				
			||||||
  </div>
 | 
					  </div>
 | 
				
			||||||
 | 
					  @if (!isCustomIcon) {
 | 
				
			||||||
 | 
					    <mat-form-field class="tb-material-icons-search tb-inline-field" appearance="outline" subscriptSizing="dynamic">
 | 
				
			||||||
 | 
					      <mat-icon matPrefix>search</mat-icon>
 | 
				
			||||||
 | 
					      <input matInput [formControl]="searchIconControl" placeholder="{{ 'icon.search-icon' | translate }}"/>
 | 
				
			||||||
 | 
					      <button *ngIf="searchIconControl.value"
 | 
				
			||||||
 | 
					              type="button"
 | 
				
			||||||
 | 
					              matSuffix mat-icon-button aria-label="Clear"
 | 
				
			||||||
 | 
					              (click)="clearSearch()">
 | 
				
			||||||
 | 
					        <mat-icon class="material-icons">close</mat-icon>
 | 
				
			||||||
 | 
					      </button>
 | 
				
			||||||
 | 
					    </mat-form-field>
 | 
				
			||||||
 | 
					    <cdk-virtual-scroll-viewport [class.!hidden]="notFound" #iconsPanel
 | 
				
			||||||
 | 
					                                 [itemSize]="iconsRowHeight" class="tb-material-icons-viewport"
 | 
				
			||||||
 | 
					                                 [style.width]="iconsPanelWidth"
 | 
				
			||||||
 | 
					                                 [style.height]="iconsPanelHeight">
 | 
				
			||||||
 | 
					      <div *cdkVirtualFor="let iconRow of iconRows$ | async" class="tb-material-icons-row">
 | 
				
			||||||
 | 
					        <ng-container *ngFor="let icon of iconRow">
 | 
				
			||||||
 | 
					          <button *ngIf="icon.name === selectedIcon"
 | 
				
			||||||
 | 
					                  class="tb-select-icon-button"
 | 
				
			||||||
 | 
					                  mat-raised-button
 | 
				
			||||||
 | 
					                  color="primary"
 | 
				
			||||||
 | 
					                  (click)="selectIcon(icon.name)"
 | 
				
			||||||
 | 
					                  matTooltip="{{ icon.displayName }}"
 | 
				
			||||||
 | 
					                  matTooltipPosition="above"
 | 
				
			||||||
 | 
					                  type="button">
 | 
				
			||||||
 | 
					            <tb-icon matButtonIcon>{{icon.name}}</tb-icon>
 | 
				
			||||||
 | 
					          </button>
 | 
				
			||||||
 | 
					          <button *ngIf="icon.name !== selectedIcon"
 | 
				
			||||||
 | 
					                  class="tb-select-icon-button"
 | 
				
			||||||
 | 
					                  mat-button
 | 
				
			||||||
 | 
					                  (click)="selectIcon(icon.name)"
 | 
				
			||||||
 | 
					                  matTooltip="{{ icon.displayName }}"
 | 
				
			||||||
 | 
					                  matTooltipPosition="above"
 | 
				
			||||||
 | 
					                  type="button">
 | 
				
			||||||
 | 
					            <tb-icon matButtonIcon>{{icon.name}}</tb-icon>
 | 
				
			||||||
 | 
					          </button>
 | 
				
			||||||
 | 
					        </ng-container>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    </cdk-virtual-scroll-viewport>
 | 
				
			||||||
 | 
					    <ng-container *ngIf="notFound">
 | 
				
			||||||
 | 
					      <div class="tb-no-data-available" [style.width]="iconsPanelWidth">
 | 
				
			||||||
 | 
					        <div class="tb-no-data-bg"></div>
 | 
				
			||||||
 | 
					        <div class="tb-no-data-text">{{ 'icon.no-icons-found' | translate:{iconSearch: searchIconControl.value} }}</div>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    </ng-container>
 | 
				
			||||||
 | 
					    <div class="tb-material-icons-panel-buttons" *ngIf="iconClearButton || !showAllSubject.value">
 | 
				
			||||||
 | 
					      <button *ngIf="iconClearButton"
 | 
				
			||||||
 | 
					              mat-button
 | 
				
			||||||
 | 
					              color="primary"
 | 
				
			||||||
 | 
					              type="button"
 | 
				
			||||||
 | 
					              (click)="clearIcon()"
 | 
				
			||||||
 | 
					              [disabled]="!selectedIcon">
 | 
				
			||||||
 | 
					        {{ 'action.clear' | translate }}
 | 
				
			||||||
 | 
					      </button>
 | 
				
			||||||
 | 
					      <span class="flex-1"></span>
 | 
				
			||||||
 | 
					      <button *ngIf="!showAllSubject.value" class="tb-material-icons-show-more" mat-button color="primary" (click)="showAllSubject.next(true)">
 | 
				
			||||||
 | 
					        {{ 'action.show-more' | translate }}
 | 
				
			||||||
 | 
					      </button>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  } @else {
 | 
				
			||||||
 | 
					    <tb-gallery-image-input [style.width]="iconsPanelWidth"
 | 
				
			||||||
 | 
					                            [(ngModel)]="selectedIcon">
 | 
				
			||||||
 | 
					    </tb-gallery-image-input>
 | 
				
			||||||
 | 
					    <div class="tb-material-icons-panel-buttons">
 | 
				
			||||||
 | 
					      <span class="flex-1"></span>
 | 
				
			||||||
 | 
					      <button class="tb-material-icons-show-more" mat-button color="primary"
 | 
				
			||||||
 | 
					              [disabled]="!selectedIcon"
 | 
				
			||||||
 | 
					              (click)="selectIcon(selectedIcon)">
 | 
				
			||||||
 | 
					        {{ 'action.set' | translate }}
 | 
				
			||||||
 | 
					      </button>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
 | 
				
			|||||||
@ -37,6 +37,7 @@ import { TbPopoverComponent } from '@shared/components/popover.component';
 | 
				
			|||||||
import { BreakpointObserver } from '@angular/cdk/layout';
 | 
					import { BreakpointObserver } from '@angular/cdk/layout';
 | 
				
			||||||
import { MediaBreakpoints } from '@shared/models/constants';
 | 
					import { MediaBreakpoints } from '@shared/models/constants';
 | 
				
			||||||
import { coerceBoolean } from '@shared/decorators/coercion';
 | 
					import { coerceBoolean } from '@shared/decorators/coercion';
 | 
				
			||||||
 | 
					import { isTbImage } from '@shared/models/resource.models';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Component({
 | 
					@Component({
 | 
				
			||||||
  selector: 'tb-material-icons',
 | 
					  selector: 'tb-material-icons',
 | 
				
			||||||
@ -61,6 +62,10 @@ export class MaterialIconsComponent extends PageComponent implements OnInit {
 | 
				
			|||||||
  @coerceBoolean()
 | 
					  @coerceBoolean()
 | 
				
			||||||
  showTitle = true;
 | 
					  showTitle = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Input()
 | 
				
			||||||
 | 
					  @coerceBoolean()
 | 
				
			||||||
 | 
					  allowedCustomIcon = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Input()
 | 
					  @Input()
 | 
				
			||||||
  popover: TbPopoverComponent;
 | 
					  popover: TbPopoverComponent;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -71,6 +76,8 @@ export class MaterialIconsComponent extends PageComponent implements OnInit {
 | 
				
			|||||||
  showAllSubject = new BehaviorSubject<boolean>(false);
 | 
					  showAllSubject = new BehaviorSubject<boolean>(false);
 | 
				
			||||||
  searchIconControl: UntypedFormControl;
 | 
					  searchIconControl: UntypedFormControl;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  isCustomIcon = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  iconsRowHeight = 48;
 | 
					  iconsRowHeight = 48;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  iconsPanelHeight: string;
 | 
					  iconsPanelHeight: string;
 | 
				
			||||||
@ -122,14 +129,15 @@ export class MaterialIconsComponent extends PageComponent implements OnInit {
 | 
				
			|||||||
      map((data) => data.iconRows),
 | 
					      map((data) => data.iconRows),
 | 
				
			||||||
      share()
 | 
					      share()
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
 | 
					    this.isCustomIcon = isTbImage(this.selectedIcon)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  clearSearch() {
 | 
					  clearSearch() {
 | 
				
			||||||
    this.searchIconControl.patchValue('', {emitEvent: true});
 | 
					    this.searchIconControl.patchValue('', {emitEvent: true});
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  selectIcon(icon: MaterialIcon) {
 | 
					  selectIcon(icon: string) {
 | 
				
			||||||
    this.iconSelected.emit(icon.name);
 | 
					    this.iconSelected.emit(icon);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  clearIcon() {
 | 
					  clearIcon() {
 | 
				
			||||||
 | 
				
			|||||||
@ -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 isJSResourceUrl = (url: string): boolean => url && RESOURCES_URL_REGEXP.test(url);
 | 
				
			||||||
export const isJSResource = (url: string): boolean => url?.startsWith(TB_RESOURCE_PREFIX);
 | 
					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} => {
 | 
					export const extractParamsFromImageResourceUrl = (url: string): {type: ImageResourceType; key: string} => {
 | 
				
			||||||
  const res = url.match(IMAGES_URL_REGEXP);
 | 
					  const res = url.match(IMAGES_URL_REGEXP);
 | 
				
			||||||
 | 
				
			|||||||
@ -9553,6 +9553,7 @@
 | 
				
			|||||||
    "icon": {
 | 
					    "icon": {
 | 
				
			||||||
        "icon": "Icon",
 | 
					        "icon": "Icon",
 | 
				
			||||||
        "icons": "Icons",
 | 
					        "icons": "Icons",
 | 
				
			||||||
 | 
					        "custom": "Custom",
 | 
				
			||||||
        "select-icon": "Select icon",
 | 
					        "select-icon": "Select icon",
 | 
				
			||||||
        "material-icons": "Material icons",
 | 
					        "material-icons": "Material icons",
 | 
				
			||||||
        "show-all": "Show all icons",
 | 
					        "show-all": "Show all icons",
 | 
				
			||||||
 | 
				
			|||||||
@ -26,6 +26,10 @@
 | 
				
			|||||||
    width: #{$size}px;
 | 
					    width: #{$size}px;
 | 
				
			||||||
    height: #{$size}px;
 | 
					    height: #{$size}px;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					  img {
 | 
				
			||||||
 | 
					    width: #{$size}px;
 | 
				
			||||||
 | 
					    height: #{$size}px;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@mixin tb-mat-icon-button-size($size) {
 | 
					@mixin tb-mat-icon-button-size($size) {
 | 
				
			||||||
 | 
				
			|||||||
@ -896,7 +896,7 @@ pre.tb-highlight {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  .mat-icon {
 | 
					  .mat-icon {
 | 
				
			||||||
    svg {
 | 
					    svg, img {
 | 
				
			||||||
      vertical-align: inherit;
 | 
					      vertical-align: inherit;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    &.tb-mat-12 {
 | 
					    &.tb-mat-12 {
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user