angular-svg-icon
Version:
Angular 8 component for inlining SVGs allowing them to be easily styled with CSS.
588 lines (582 loc) • 17.6 kB
JavaScript
import { Injectable, InjectionToken, Inject, PLATFORM_ID, Optional, SkipSelf, Component, ElementRef, KeyValueDiffers, Renderer2, ChangeDetectorRef, Input, NgModule } from '@angular/core';
import { DOCUMENT, CommonModule } from '@angular/common';
import { of, throwError } from 'rxjs';
import { map, tap, catchError, finalize, share } from 'rxjs/operators';
import { HttpClient } from '@angular/common/http';
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
/**
* @abstract
*/
var /**
* @abstract
*/
SvgLoader = /** @class */ (function () {
function SvgLoader() {
}
return SvgLoader;
}());
var SvgHttpLoader = /** @class */ (function (_super) {
__extends(SvgHttpLoader, _super);
function SvgHttpLoader(http) {
var _this = _super.call(this) || this;
_this.http = http;
return _this;
}
/**
* @param {?} url
* @return {?}
*/
SvgHttpLoader.prototype.getSvg = /**
* @param {?} url
* @return {?}
*/
function (url) {
return this.http.get(url, { responseType: 'text' });
};
SvgHttpLoader.decorators = [
{ type: Injectable }
];
/** @nocollapse */
SvgHttpLoader.ctorParameters = function () { return [
{ type: HttpClient }
]; };
return SvgHttpLoader;
}(SvgLoader));
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
/** @type {?} */
var SERVER_URL = new InjectionToken('SERVER_URL');
var SvgIconRegistryService = /** @class */ (function () {
function SvgIconRegistryService(loader, platformId, serverUrl, _document) {
this.loader = loader;
this.platformId = platformId;
this.serverUrl = serverUrl;
this._document = _document;
this.iconsByUrl = new Map();
this.iconsLoadingByUrl = new Map();
this.document = this._document;
}
/** Add a SVG to the registry by passing a name and the SVG. */
/**
* Add a SVG to the registry by passing a name and the SVG.
* @param {?} name
* @param {?} data
* @return {?}
*/
SvgIconRegistryService.prototype.addSvg = /**
* Add a SVG to the registry by passing a name and the SVG.
* @param {?} name
* @param {?} data
* @return {?}
*/
function (name, data) {
if (!this.iconsByUrl.has(name)) {
/** @type {?} */
var div = this.document.createElement('DIV');
div.innerHTML = data;
/** @type {?} */
var svg = (/** @type {?} */ (div.querySelector('svg')));
this.iconsByUrl.set(name, svg);
}
};
/** Load a SVG to the registry from a URL. */
/**
* Load a SVG to the registry from a URL.
* @param {?} url
* @param {?=} name
* @return {?}
*/
SvgIconRegistryService.prototype.loadSvg = /**
* Load a SVG to the registry from a URL.
* @param {?} url
* @param {?=} name
* @return {?}
*/
function (url, name) {
var _this = this;
if (name === void 0) { name = url; }
// not sure if there should be a possibility to use name for server usage
// so overriding it for now if provided
// maybe should separate functionality for url and name use-cases
if (this.serverUrl && url.match(/^(http(s)?):/) === null) {
url = this.serverUrl + url;
name = url;
}
if (this.iconsByUrl.has(name)) {
return of(this.iconsByUrl.get(name));
}
else if (this.iconsLoadingByUrl.has(name)) {
return this.iconsLoadingByUrl.get(name);
}
/** @type {?} */
var o = (/** @type {?} */ (this.loader.getSvg(url).pipe(map((/**
* @param {?} svg
* @return {?}
*/
function (svg) {
/** @type {?} */
var div = _this.document.createElement('DIV');
div.innerHTML = svg;
return (/** @type {?} */ (div.querySelector('svg')));
})), tap((/**
* @param {?} svg
* @return {?}
*/
function (svg) { return _this.iconsByUrl.set(name, svg); })), catchError((/**
* @param {?} err
* @return {?}
*/
function (err) {
console.error(err);
return throwError(err);
})), finalize((/**
* @return {?}
*/
function () { return _this.iconsLoadingByUrl.delete(name); })), share())));
this.iconsLoadingByUrl.set(name, o);
return o;
};
/** Get loaded SVG from registry by name. (also works by url because of blended map) */
/**
* Get loaded SVG from registry by name. (also works by url because of blended map)
* @param {?} name
* @return {?}
*/
SvgIconRegistryService.prototype.getSvgByName = /**
* Get loaded SVG from registry by name. (also works by url because of blended map)
* @param {?} name
* @return {?}
*/
function (name) {
if (this.iconsByUrl.has(name)) {
return of(this.iconsByUrl.get(name));
}
else if (this.iconsLoadingByUrl.has(name)) {
return this.iconsLoadingByUrl.get(name);
}
return throwError("No svg with name '" + name + "' has been loaded");
};
/** Remove a SVG from the registry by URL (or name). */
/**
* Remove a SVG from the registry by URL (or name).
* @param {?} url
* @return {?}
*/
SvgIconRegistryService.prototype.unloadSvg = /**
* Remove a SVG from the registry by URL (or name).
* @param {?} url
* @return {?}
*/
function (url) {
if (this.iconsByUrl.has(url)) {
this.iconsByUrl.delete(url);
}
};
SvgIconRegistryService.decorators = [
{ type: Injectable }
];
/** @nocollapse */
SvgIconRegistryService.ctorParameters = function () { return [
{ type: SvgLoader },
{ type: Object, decorators: [{ type: Inject, args: [PLATFORM_ID,] }] },
{ type: String, decorators: [{ type: Optional }, { type: Inject, args: [SERVER_URL,] }] },
{ type: undefined, decorators: [{ type: Optional }, { type: Inject, args: [DOCUMENT,] }] }
]; };
return SvgIconRegistryService;
}());
/**
* @param {?} parentRegistry
* @param {?} loader
* @param {?} platformId
* @param {?=} serverUrl
* @param {?=} document
* @return {?}
*/
function SVG_ICON_REGISTRY_PROVIDER_FACTORY(parentRegistry, loader, platformId, serverUrl, document) {
return parentRegistry || new SvgIconRegistryService(loader, platformId, serverUrl, document);
}
/** @type {?} */
var SVG_ICON_REGISTRY_PROVIDER = {
provide: SvgIconRegistryService,
deps: [[new Optional(), new SkipSelf(), SvgIconRegistryService], SvgLoader, [(/** @type {?} */ (PLATFORM_ID))],
[new Optional(), (/** @type {?} */ (SERVER_URL))], [new Optional(), (/** @type {?} */ (DOCUMENT))]
],
useFactory: SVG_ICON_REGISTRY_PROVIDER_FACTORY
};
var __read = (this && this.__read) || function (o, n) {
var m = typeof Symbol === "function" && o[Symbol.iterator];
if (!m) return o;
var i = m.call(o), r, ar = [], e;
try {
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
}
catch (error) { e = { error: error }; }
finally {
try {
if (r && !r.done && (m = i["return"])) m.call(i);
}
finally { if (e) throw e.error; }
}
return ar;
};
var SvgIconComponent = /** @class */ (function () {
function SvgIconComponent(element, differs, renderer, iconReg, cdr) {
this.element = element;
this.differs = differs;
this.renderer = renderer;
this.iconReg = iconReg;
this.cdr = cdr;
this.stretch = false;
this.applyCss = false;
}
Object.defineProperty(SvgIconComponent.prototype, "svgStyle", {
// Adapted from ngStyle
set:
// Adapted from ngStyle
/**
* @param {?} v
* @return {?}
*/
function (v) {
this._svgStyle = v;
if (!this.differ && v) {
this.differ = this.differs.find(v).create();
}
},
enumerable: true,
configurable: true
});
/**
* @return {?}
*/
SvgIconComponent.prototype.ngOnInit = /**
* @return {?}
*/
function () {
this.init();
};
/**
* @return {?}
*/
SvgIconComponent.prototype.ngOnDestroy = /**
* @return {?}
*/
function () {
this.destroy();
};
/**
* @param {?} changeRecord
* @return {?}
*/
SvgIconComponent.prototype.ngOnChanges = /**
* @param {?} changeRecord
* @return {?}
*/
function (changeRecord) {
if (changeRecord.src || changeRecord.name) {
if (this.svg) {
this.destroy();
}
this.init();
}
if (changeRecord.stretch) {
this.stylize();
}
};
/**
* @return {?}
*/
SvgIconComponent.prototype.ngDoCheck = /**
* @return {?}
*/
function () {
if (this.svg && this.differ) {
/** @type {?} */
var changes = this.differ.diff(this._svgStyle);
if (changes) {
this.applyChanges(changes);
}
}
};
/**
* @private
* @return {?}
*/
SvgIconComponent.prototype.init = /**
* @private
* @return {?}
*/
function () {
if (this.name) {
this.icnSub = this.iconReg.getSvgByName(this.name).subscribe(this.initSvg.bind(this));
}
else if (this.src) {
this.icnSub = this.iconReg.loadSvg(this.src).subscribe(this.initSvg.bind(this));
}
};
/**
* @private
* @param {?} svg
* @return {?}
*/
SvgIconComponent.prototype.initSvg = /**
* @private
* @param {?} svg
* @return {?}
*/
function (svg) {
this.setSvg(svg);
this.resetDiffer();
};
/**
* @private
* @return {?}
*/
SvgIconComponent.prototype.destroy = /**
* @private
* @return {?}
*/
function () {
this.svg = undefined;
this.differ = undefined;
if (this.icnSub) {
this.icnSub.unsubscribe();
}
};
/**
* @private
* @return {?}
*/
SvgIconComponent.prototype.resetDiffer = /**
* @private
* @return {?}
*/
function () {
if (this._svgStyle && !this.differ) {
this.differ = this.differs.find(this._svgStyle).create();
}
};
/**
* @private
* @param {?} svg
* @return {?}
*/
SvgIconComponent.prototype.setSvg = /**
* @private
* @param {?} svg
* @return {?}
*/
function (svg) {
if (svg) {
this.svg = svg;
/** @type {?} */
var icon = (/** @type {?} */ (svg.cloneNode(true)));
/** @type {?} */
var elem = this.element.nativeElement;
if (this.applyCss) {
this.copyNgContentAttribute(elem, icon);
}
elem.innerHTML = '';
this.renderer.appendChild(elem, icon);
this.stylize();
this.cdr.markForCheck();
}
};
/**
* @private
* @param {?} hostElem
* @param {?} icon
* @return {?}
*/
SvgIconComponent.prototype.copyNgContentAttribute = /**
* @private
* @param {?} hostElem
* @param {?} icon
* @return {?}
*/
function (hostElem, icon) {
/** @type {?} */
var attributes = (/** @type {?} */ (hostElem.attributes));
/** @type {?} */
var len = attributes.length;
for (var i = 0; i < len; i += 1) {
/** @type {?} */
var attribute = attributes.item(i);
if (attribute.name.startsWith('_ngcontent')) {
this.setNgContentAttribute(icon, attribute.name);
break;
}
}
};
/**
* @private
* @param {?} parent
* @param {?} attributeName
* @return {?}
*/
SvgIconComponent.prototype.setNgContentAttribute = /**
* @private
* @param {?} parent
* @param {?} attributeName
* @return {?}
*/
function (parent, attributeName) {
this.renderer.setAttribute(parent, attributeName, '');
/** @type {?} */
var len = parent.childNodes.length;
for (var i = 0; i < len; i += 1) {
/** @type {?} */
var child = parent.childNodes[i];
if (child instanceof Element) {
this.setNgContentAttribute(child, attributeName);
}
}
};
/**
* @private
* @return {?}
*/
SvgIconComponent.prototype.stylize = /**
* @private
* @return {?}
*/
function () {
if (this.svg) {
/** @type {?} */
var svg = this.element.nativeElement.firstChild;
if (this.stretch === true) {
this.renderer.setAttribute(svg, 'preserveAspectRatio', 'none');
}
else if (this.stretch === false) {
this.renderer.removeAttribute(svg, 'preserveAspectRatio');
}
}
};
/**
* @private
* @param {?} changes
* @return {?}
*/
SvgIconComponent.prototype.applyChanges = /**
* @private
* @param {?} changes
* @return {?}
*/
function (changes) {
var _this = this;
changes.forEachRemovedItem((/**
* @param {?} record
* @return {?}
*/
function (record) { return _this.setStyle(record.key, null); }));
changes.forEachAddedItem((/**
* @param {?} record
* @return {?}
*/
function (record) { return _this.setStyle(record.key, record.currentValue); }));
changes.forEachChangedItem((/**
* @param {?} record
* @return {?}
*/
function (record) { return _this.setStyle(record.key, record.currentValue); }));
};
/**
* @private
* @param {?} nameAndUnit
* @param {?} value
* @return {?}
*/
SvgIconComponent.prototype.setStyle = /**
* @private
* @param {?} nameAndUnit
* @param {?} value
* @return {?}
*/
function (nameAndUnit, value) {
var _a = __read(nameAndUnit.split('.'), 2), name = _a[0], unit = _a[1];
value = value !== null && unit ? "" + value + unit : value;
/** @type {?} */
var svg = this.element.nativeElement.firstChild;
if (value !== null) {
this.renderer.setStyle(svg, name, (/** @type {?} */ (value)));
}
else {
this.renderer.removeStyle(svg, name);
}
};
SvgIconComponent.decorators = [
{ type: Component, args: [{
selector: 'svg-icon',
template: '<ng-content></ng-content>'
}] }
];
/** @nocollapse */
SvgIconComponent.ctorParameters = function () { return [
{ type: ElementRef },
{ type: KeyValueDiffers },
{ type: Renderer2 },
{ type: SvgIconRegistryService },
{ type: ChangeDetectorRef }
]; };
SvgIconComponent.propDecorators = {
src: [{ type: Input }],
name: [{ type: Input }],
stretch: [{ type: Input }],
applyCss: [{ type: Input }],
svgStyle: [{ type: Input }]
};
return SvgIconComponent;
}());
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
var AngularSvgIconModule = /** @class */ (function () {
function AngularSvgIconModule() {
}
/**
* @param {?=} config
* @return {?}
*/
AngularSvgIconModule.forRoot = /**
* @param {?=} config
* @return {?}
*/
function (config) {
if (config === void 0) { config = {}; }
return {
ngModule: AngularSvgIconModule,
providers: [
config.loader || { provide: SvgLoader, useClass: SvgHttpLoader },
SVG_ICON_REGISTRY_PROVIDER
]
};
};
AngularSvgIconModule.decorators = [
{ type: NgModule, args: [{
imports: [
CommonModule,
],
declarations: [SvgIconComponent],
providers: [SVG_ICON_REGISTRY_PROVIDER, { provide: SvgLoader, useClass: SvgHttpLoader }],
exports: [SvgIconComponent]
},] }
];
return AngularSvgIconModule;
}());
export { AngularSvgIconModule, SERVER_URL, SVG_ICON_REGISTRY_PROVIDER, SVG_ICON_REGISTRY_PROVIDER_FACTORY, SvgHttpLoader, SvgIconComponent, SvgIconRegistryService, SvgLoader };
//# sourceMappingURL=angular-svg-icon.js.map