openaire-library/utils/icons/icons.component.ts

195 lines
5.7 KiB
TypeScript

import {
AfterViewInit,
ChangeDetectorRef,
Component,
ElementRef,
Input,
OnChanges,
SimpleChanges,
ViewChild
} from "@angular/core";
import {IconsService} from "./icons.service";
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: `
<span #icon *ngIf="svg" class="uk-icon" [class.uk-preserve]="gradient || preserveColor" [class.uk-flex]="flex" [ngClass]="customClass" [ngStyle]="style" [innerHTML]="svg | safeHtml"></span>
<span *ngIf="!svg && iconName" [class.uk-flex]="flex" [ngClass]="customClass" [class.uk-display-inline-block]="!flex">
<span class="material-icons" [ngClass]="type" [ngStyle]="style">{{iconName}}</span>
</span>
<span *ngIf="visuallyHidden" class="visually-hidden">{{visuallyHidden}}</span>
`
})
export class IconsComponent implements AfterViewInit, OnChanges {
private static DEFAULT_ICON_SIZE = 20;
public iconName: string;
public style;
/**
* Custom icon as SVG
*/
@Input()
public 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 (Required)
* */
@Input()
set name(iconName: string) {
this.iconName = iconName;
this.svg = this.iconsService.getIcon(iconName);
}
/**
* 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("icon")
public icon: ElementRef;
@Input()
public preserveColor: boolean = false;
constructor(private iconsService: IconsService,
private cdr: ChangeDetectorRef) {}
ngAfterViewInit() {
this.initIcon();
}
ngOnChanges(changes: SimpleChanges) {
this.initIcon();
}
initIcon() {
if(this.svg && this.icon) {
this.cdr.detectChanges();
let svg: Element = this.icon.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]);
}
}
}