206 lines
6.0 KiB
TypeScript
206 lines
6.0 KiB
TypeScript
import {
|
|
AfterViewInit,
|
|
ChangeDetectorRef,
|
|
Component,
|
|
ElementRef,
|
|
Input,
|
|
OnChanges,
|
|
SimpleChanges,
|
|
ViewChild
|
|
} from "@angular/core";
|
|
import {IconsService} from "./icons.service";
|
|
import {Icon} from "./icons";
|
|
|
|
export interface StopRule {
|
|
class: string,
|
|
offset: number
|
|
}
|
|
|
|
/**
|
|
* By default, this component uses Material Icons Library to render an icon with
|
|
* a specific @name. For custom icons you should:
|
|
*
|
|
* - Add your icon in icons.ts and register it to Icon registry, by adding this to your component Module.
|
|
*
|
|
* e.g export class ExampleModule {
|
|
* constructor(private iconsService: IconsService) {
|
|
* this.iconsService.registerIcons([arrow_right])
|
|
* }
|
|
* }
|
|
*
|
|
* If the name of your icon is the same with a Material Icon name, yours will be used instead of the default.
|
|
*
|
|
* - Custom SVG Icon. Define a variable in your component with an SVG and give it as Input @svg.
|
|
*
|
|
* In case of SVGs, there is an opportunity to add gradient color by define at least a start and end stop-color rules
|
|
* and an id to support them. This option is enabled by giving id as input in @gradient (Optional @degrees for gradient direction)
|
|
*
|
|
* e.g #gradient .start {
|
|
* stop-color: red;
|
|
* }
|
|
*
|
|
* e.g #gradient .end {
|
|
* stop-color: orange;
|
|
* }
|
|
*
|
|
* */
|
|
@Component({
|
|
selector: 'icon',
|
|
template: `
|
|
<ng-container *ngIf="icon">
|
|
<span #svgIcon *ngIf="icon.data" class="uk-icon" [class.uk-preserve]="gradient || icon.preserveColor" [class.uk-flex]="flex" [ngClass]="customClass" [ngStyle]="style" [innerHTML]="icon.data | safeHtml"></span>
|
|
<span *ngIf="!icon.data && icon.name" [class.uk-flex]="flex" [ngClass]="customClass" [class.uk-display-inline-block]="!flex">
|
|
<span class="material-icons" [ngClass]="type?type:icon.type" [ngStyle]="style">{{icon.name}}</span>
|
|
</span>
|
|
<span *ngIf="visuallyHidden" class="visually-hidden">{{visuallyHidden}}</span>
|
|
</ng-container>
|
|
`
|
|
})
|
|
export class IconsComponent implements AfterViewInit, OnChanges {
|
|
private static DEFAULT_ICON_SIZE = 20;
|
|
@Input()
|
|
public icon: Icon;
|
|
public style: any;
|
|
/**
|
|
* Custom icon as SVG
|
|
*/
|
|
@Input()
|
|
public set svg(svg: string) {
|
|
if(svg) {
|
|
this.icon = {name: '', data: svg};
|
|
}
|
|
};
|
|
@Input()
|
|
public defaultSize = false;
|
|
/**
|
|
* True if this icon should have display flex (Optional, Default: false)
|
|
*
|
|
* */
|
|
@Input()
|
|
public flex = false;
|
|
/**
|
|
*
|
|
* Add custom class(es)(Optional)
|
|
* */
|
|
@Input()
|
|
public customClass = '';
|
|
/**
|
|
* Color of svg (Optional)
|
|
* */
|
|
@Input()
|
|
public fill;
|
|
/**
|
|
* Color of svg stroke (Optional)
|
|
* */
|
|
@Input()
|
|
public stroke;
|
|
/**
|
|
* Size of icon (Default: 1)
|
|
*
|
|
* Disabled if defaultSize = true
|
|
* */
|
|
@Input()
|
|
public ratio = 1;
|
|
/**
|
|
* In case of Material icon only. Type of icon (Optional)
|
|
* */
|
|
@Input()
|
|
public type: "outlined" | "round" | "sharp" | "two-tone" | null = null;
|
|
/**
|
|
* Name of icon in registry(1) or Material Icons(2)
|
|
* */
|
|
@Input()
|
|
set name(iconName: string) {
|
|
if(iconName) {
|
|
this.icon = {name: iconName, data: null};
|
|
let icon = this.iconsService.getIcon(iconName);
|
|
if (icon) {
|
|
this.icon = icon;
|
|
}
|
|
}
|
|
}
|
|
/**
|
|
* Set visually hidden name for accessibility
|
|
* */
|
|
@Input()
|
|
public visuallyHidden: string = null;
|
|
/**
|
|
* Gradient rules
|
|
* Available only for SVG!
|
|
*
|
|
* Define your css rules for stop-colors
|
|
* */
|
|
@Input()
|
|
public gradient: string = null;
|
|
@Input()
|
|
public degrees: number = 0;
|
|
@Input()
|
|
public stopRules: StopRule[]= [{class: 'start', offset: 0}, {class: 'end', offset: 100}];
|
|
@ViewChild("svgIcon")
|
|
public svgIcon: ElementRef;
|
|
|
|
constructor(private iconsService: IconsService,
|
|
private cdr: ChangeDetectorRef) {}
|
|
|
|
ngAfterViewInit() {
|
|
this.initIcon();
|
|
}
|
|
|
|
ngOnChanges(changes: SimpleChanges) {
|
|
this.initIcon();
|
|
}
|
|
|
|
initIcon() {
|
|
if(this.icon?.data && this.svgIcon) {
|
|
this.cdr.detectChanges();
|
|
let svg: Element = this.svgIcon.nativeElement.getElementsByTagName('svg').item(0);
|
|
if(!this.defaultSize && svg) {
|
|
svg.setAttribute("width", (this.ratio * IconsComponent.DEFAULT_ICON_SIZE).toString());
|
|
svg.setAttribute("height", (this.ratio * IconsComponent.DEFAULT_ICON_SIZE).toString());
|
|
}
|
|
if(this.gradient && svg) {
|
|
this.addGradient(svg);
|
|
} else {
|
|
this.style = {
|
|
fill: this.fill,
|
|
stroke: this.stroke
|
|
};
|
|
}
|
|
} else {
|
|
this.style = {
|
|
"font-size.px": this.ratio*IconsComponent.DEFAULT_ICON_SIZE
|
|
};
|
|
}
|
|
this.cdr.detectChanges();
|
|
}
|
|
|
|
addGradient(svg: Element) {
|
|
if(svg.children.length > 0 && typeof document !== "undefined") {
|
|
let gradientDefinition = document.createElementNS('http://www.w3.org/2000/svg', 'linearGradient');
|
|
gradientDefinition.setAttribute('id', this.gradient);
|
|
if(this.degrees !== 0) {
|
|
let angle = (this.degrees) * (Math.PI / 180);
|
|
gradientDefinition.setAttribute('x1', Math.round(50 + Math.sin(angle + Math.PI) * 50) + '%');
|
|
gradientDefinition.setAttribute('y1', Math.round(50 + Math.cos(angle + Math.PI) * 50) + '%');
|
|
gradientDefinition.setAttribute('x2', Math.round(50 + Math.sin(angle) * 50) + '%');
|
|
gradientDefinition.setAttribute('y2', Math.round(50 + Math.cos(angle) * 50) + '%');
|
|
}
|
|
for(let rule of this.stopRules) {
|
|
let item = document.createElementNS('http://www.w3.org/2000/svg','stop');
|
|
item.setAttribute('class', rule.class);
|
|
item.setAttribute('offset', rule.offset + '%');
|
|
gradientDefinition.appendChild(item);
|
|
}
|
|
let defs = document.createElementNS('http://www.w3.org/2000/svg','defs');
|
|
defs.appendChild(gradientDefinition);
|
|
for(let i = 0; i < svg.children.length; i++) {
|
|
let item = svg.children.item(i);
|
|
if(!item.hasAttribute('fill')) {
|
|
item.setAttribute('fill', "url('#" + this.gradient + "')");
|
|
}
|
|
}
|
|
svg.insertBefore(defs, svg.childNodes[0]);
|
|
}
|
|
}
|
|
}
|