UNPKG

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
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