diff --git a/LICENSE b/LICENSE index cc8a93e..d386bb4 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2020 David Czeck. +Copyright (c) 2021 David Czeck. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 38bfe6f..257da4f 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ Angular SVG Icon ========= -The **angular-svg-icon** is an Angular 11 service and component that provides a +The **angular-svg-icon** is an Angular 12 service and component that provides a means to inline SVG files to allow for them to be easily styled by CSS and code. The service provides an icon registery that loads and caches a SVG indexed by @@ -19,6 +19,7 @@ This [demo](https://czeckd.github.io/angular-svg-icon/) shows this module in act $ npm i angular-svg-icon --save ``` **Note on earlier versions of Angular:** +- For Angular 11, use angular-svg-icon@11.2.0 - For Angular 10, use angular-svg-icon@10.0.0 - For Angular 9, use angular-svg-icon@9.2.0 - For Angular 8, use angular-svg-icon@8.0.0 @@ -234,6 +235,22 @@ This is executed on browser side. Note that the fallback when no data is available uses `SvgHttpLoader`, which is also the default loader if you don't provide one. +## Example Project with Angular Universal and `angular-svg-icon` + +An Angular Universal [example project](https://github.com/edulelis/demo-universal-angular-svg-loader) is also available. The basic steps to get it work is: + +1. Add this snippet to the `package.json` file to prevent compilation issues: +```js + "browser": { + "fs": false, + "path": false, + "os": false + } +``` +2. Add `ServerTransferStateModule` to `app.server.module` +3. Add `BrowserTransferStateModule` to `app.module` +4. The loader should be different per platform, so the factory should receive the `PLATFORM_ID` and load the correct class appropriately (this is already added in the example). + ## SVG Preparation The SVG should be modified to remove the height and width attributes from the file per Sara Soueidan's advice in "[Making SVGs Responsive With diff --git a/angular.json b/angular.json index 78803eb..b8ebb36 100644 --- a/angular.json +++ b/angular.json @@ -20,7 +20,6 @@ "skipTests": true }, "@schematics/angular:module": { - "skipTests": true }, "@schematics/angular:pipe": { "skipTests": true @@ -41,7 +40,6 @@ "main": "src/main.ts", "polyfills": "src/polyfills.ts", "tsConfig": "tsconfig.app.json", - "aot": true, "assets": [ "src/favicon.ico", "src/assets" @@ -49,7 +47,13 @@ "styles": [ "src/styles.scss" ], - "scripts": [] + "scripts": [], + "vendorChunk": true, + "extractLicenses": false, + "buildOptimizer": false, + "sourceMap": true, + "optimization": false, + "namedChunks": true }, "configurations": { "production": { diff --git a/package.json b/package.json index 4c052f6..e94fe18 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "svg-icon", - "description": "Angular 11 component for inlining SVGs allowing them to be easily styled with CSS.", - "version": "11.2.0", + "description": "Angular 12 component for inlining SVGs allowing them to be easily styled with CSS.", + "version": "12.0.0", "repository": { "type": "git", "url": "https://github.com/czeckd/angular-svg-icon.git" @@ -15,9 +15,9 @@ ], "scripts": { "ng": "ng", - "start": "ng serve --prod", - "build": "ng build", - "dist": "ng build --prod angular-svg-icon && cp README.md LICENSE ./dist/angular-svg-icon/", + "start": "ng serve --configuration production", + "build": "ng build --configuration production", + "dist": "ng build --configuration production angular-svg-icon && cp README.md LICENSE ./dist/angular-svg-icon/", "test": "ng test --source-map=false angular-svg-icon", "coverage": "ng test --code-coverage angular-svg-icon", "lint": "ng lint", @@ -25,39 +25,31 @@ }, "private": true, "dependencies": { - "@angular/animations": "~11.1.2", - "@angular/common": "~11.1.2", - "@angular/compiler": "~11.1.2", - "@angular/core": "~11.1.2", - "@angular/forms": "~11.1.2", - "@angular/platform-browser": "~11.1.2", - "@angular/platform-browser-dynamic": "~11.1.2", - "@angular/router": "~11.1.2", + "@angular/animations": "~12.0.1", + "@angular/common": "~12.0.1", + "@angular/compiler": "~12.0.1", + "@angular/core": "~12.0.1", + "@angular/forms": "~12.0.1", + "@angular/platform-browser": "~12.0.1", + "@angular/platform-browser-dynamic": "~12.0.1", + "@angular/router": "~12.0.1", "rxjs": "~6.6.3", - "tslib": "^2.0.0", - "zone.js": "~0.10.3" + "tslib": "^2.2.0", + "zone.js": "~0.11.4" }, "devDependencies": { - "@angular-devkit/build-angular": "~0.1101.4", - "@angular-devkit/core": "^11.1.4", - "@angular/cli": "~11.1.4", - "@angular/compiler-cli": "~11.1.2", - "@angular/language-service": "~11.1.2", + "@angular-devkit/build-angular": "~12.0.1", + "@angular/cli": "~12.0.1", + "@angular/compiler-cli": "~12.0.1", "@types/jasmine": "~3.6.0", - "@types/jasminewd2": "~2.0.3", "@types/node": "^12.11.1", - "codelyzer": "^6.0.0", - "jasmine-core": "~3.6.0", - "jasmine-spec-reporter": "~5.0.0", - "karma": "~5.2.3", + "jasmine-core": "~3.7.0", + "karma": "~6.3.0", "karma-chrome-launcher": "~3.1.0", - "karma-coverage-istanbul-reporter": "~3.0.2", + "karma-coverage": "~2.0.3", "karma-jasmine": "~4.0.0", "karma-jasmine-html-reporter": "^1.5.0", - "ng-packagr": "^11.0.2", - "protractor": "~7.0.0", - "ts-node": "~7.0.0", - "tslint": "~6.1.0", - "typescript": "~4.0.5" + "ng-packagr": "^12.0.0", + "typescript": "~4.2.3" } } diff --git a/projects/angular-svg-icon/package.json b/projects/angular-svg-icon/package.json index 948771f..8ed880f 100644 --- a/projects/angular-svg-icon/package.json +++ b/projects/angular-svg-icon/package.json @@ -1,7 +1,7 @@ { "name": "angular-svg-icon", - "description": "Angular 11 component for inlining SVGs allowing them to be easily styled with CSS.", - "version": "11.2.0", + "description": "Angular 12 component for inlining SVGs allowing them to be easily styled with CSS.", + "version": "12.0.0", "repository": { "type": "git", "url": "https://github.com/czeckd/angular-svg-icon.git" @@ -14,11 +14,11 @@ "icon" ], "peerDependencies": { - "@angular/core": ">=11.0.0", - "@angular/common": ">=11.0.0", + "@angular/core": ">=12.0.0", + "@angular/common": ">=12.0.0", "rxjs": ">=6.6.0" }, "dependencies": { - "tslib": "^2.0.0" + "tslib": "^2.2.0" } } diff --git a/projects/angular-svg-icon/src/lib/svg-icon-registry.service.ts b/projects/angular-svg-icon/src/lib/svg-icon-registry.service.ts index 8810f0e..d5bddb2 100644 --- a/projects/angular-svg-icon/src/lib/svg-icon-registry.service.ts +++ b/projects/angular-svg-icon/src/lib/svg-icon-registry.service.ts @@ -17,7 +17,7 @@ export class SvgIconRegistryService { constructor( private loader: SvgLoader, @Inject(PLATFORM_ID) private platformId: Object, - @Optional() @Inject(SERVER_URL) protected serverUrl: string, + @Optional() @Inject(SERVER_URL) protected serverUrl: string | undefined, @Optional() @Inject(DOCUMENT) private _document: any) { this.document = this._document; } @@ -33,7 +33,7 @@ export class SvgIconRegistryService { } /** Load a SVG to the registry from a URL. */ - loadSvg(url: string, name: string = url): Observable { + loadSvg(url: string, name: string = url): Observable | undefined { // not sure if there should be a possibility to use name for server usage // so overriding it for now if provided @@ -68,7 +68,7 @@ export class SvgIconRegistryService { } /** Get loaded SVG from registry by name. (also works by url because of blended map) */ - getSvgByName(name: string): Observable { + getSvgByName(name: string): Observable | undefined { if (this.iconsByUrl.has(name)) { return observableOf(this.iconsByUrl.get(name)); } else if (this.iconsLoadingByUrl.has(name)) { @@ -91,7 +91,7 @@ export function SVG_ICON_REGISTRY_PROVIDER_FACTORY( platformId: object, serverUrl?: string, document?: any) { - return parentRegistry || new SvgIconRegistryService(loader, platformId, serverUrl, document); + return parentRegistry || new SvgIconRegistryService(loader, platformId, serverUrl, document); } export const SVG_ICON_REGISTRY_PROVIDER = { diff --git a/projects/angular-svg-icon/src/lib/svg-icon.component.ts b/projects/angular-svg-icon/src/lib/svg-icon.component.ts index 171e924..671510f 100644 --- a/projects/angular-svg-icon/src/lib/svg-icon.component.ts +++ b/projects/angular-svg-icon/src/lib/svg-icon.component.ts @@ -7,37 +7,42 @@ import { Subscription } from 'rxjs'; import { SvgIconRegistryService } from './svg-icon-registry.service'; + +class SvgIconHelper { + svg!: SVGElement; + icnSub!: Subscription; + differ?: KeyValueDiffer; + loaded = false; +} + @Component({ selector: 'svg-icon', template: '' }) export class SvgIconComponent implements OnInit, OnDestroy, OnChanges, DoCheck { - @Input() src: string; - @Input() name: string; + @Input() src!: string; + @Input() name?: string; @Input() stretch = false; @Input() applyClass = false; /** @deprecated since 9.1.0 */ @Input() applyCss = false; - @Input() svgClass: any; + @Input() svgClass?: any; // tslint:disable-next-line:no-input-rename - @Input('class') klass: any; - @Input() viewBox: string; - @Input() svgAriaLabel: string; + @Input('class') klass?: any; + @Input() viewBox?: string; + @Input() svgAriaLabel?: string; // Adapted from ngStyle (see: angular/packages/common/src/directives/ng_style.ts) @Input() - set svgStyle(v: {[key: string]: any }|null) { - this._svgStyle = v; - if (!this.differ && v) { - this.differ = this.differs.find(v).create(); + set svgStyle(values: {[klass: string]: any }|null) { + this._svgStyle = values; + if (!this.helper.differ && values) { + this.helper.differ = this.differs.find(values).create(); } } - private svg: SVGElement; - private icnSub: Subscription; - private differ: KeyValueDiffer; - private _svgStyle: {[key: string]: any}; - private loaded = false; + private helper = new SvgIconHelper(); + private _svgStyle: {[key: string]: any} | null = null; constructor( private element: ElementRef, @@ -59,7 +64,7 @@ export class SvgIconComponent implements OnInit, OnDestroy, OnChanges, DoCheck { const elemSvg = this.element.nativeElement.firstChild; if (changeRecord.src || changeRecord.name) { - if (this.loaded) { + if (this.helper.loaded) { this.destroy(); } this.init(); @@ -92,7 +97,7 @@ export class SvgIconComponent implements OnInit, OnDestroy, OnChanges, DoCheck { } if (changeRecord.viewBox) { - if (this.loaded) { + if (this.helper.loaded) { this.destroy(); } this.init(); @@ -108,8 +113,8 @@ export class SvgIconComponent implements OnInit, OnDestroy, OnChanges, DoCheck { } ngDoCheck() { - if (this.svg && this.differ) { - const changes = this.differ.diff(this._svgStyle); + if (this.helper.svg && this.helper.differ) { + const changes = this.helper.differ.diff(this._svgStyle!); if (changes) { this.applyChanges(changes); } @@ -118,9 +123,15 @@ export class SvgIconComponent implements OnInit, OnDestroy, OnChanges, DoCheck { private init() { if (this.name) { - this.icnSub = this.iconReg.getSvgByName(this.name).subscribe(this.initSvg.bind(this)); + const svgObs = this.iconReg.getSvgByName(this.name); + if (svgObs) { + this.helper.icnSub = svgObs.subscribe(svg => this.initSvg(svg)); + } } else if (this.src) { - this.icnSub = this.iconReg.loadSvg(this.src).subscribe(this.initSvg.bind(this)); + const svgObs = this.iconReg.loadSvg(this.src); + if (svgObs) { + this.helper.icnSub = svgObs.subscribe(svg => this.initSvg(svg)); + } } else { const elem = this.element.nativeElement; elem.innerHTML = ''; @@ -128,37 +139,35 @@ export class SvgIconComponent implements OnInit, OnDestroy, OnChanges, DoCheck { } } - private initSvg(svg: SVGElement): void { - if (!this.loaded) { + private initSvg(svg: SVGElement|undefined): void { + if (!this.helper.loaded && svg) { this.setSvg(svg); this.resetDiffer(); } } private destroy() { - this.svg = undefined; - this.differ = undefined; - this.loaded = false; - if (this.icnSub) { - this.icnSub.unsubscribe(); + if (this.helper.icnSub) { + this.helper.icnSub.unsubscribe(); } + this.helper = new SvgIconHelper(); } private resetDiffer() { - if (this._svgStyle && !this.differ) { - this.differ = this.differs.find(this._svgStyle).create(); + if (this._svgStyle && !this.helper.differ) { + this.helper.differ = this.differs.find(this._svgStyle).create(); } } private setSvg(svg: SVGElement) { - if (!this.loaded && svg) { - this.svg = svg; + if (!this.helper.loaded && svg) { + this.helper.svg = svg; const icon = svg.cloneNode(true) as SVGElement; const elem = this.element.nativeElement; elem.innerHTML = ''; this.renderer.appendChild(elem, icon); - this.loaded = true; + this.helper.loaded = true; this.copyNgContentAttribute(elem, icon); @@ -205,7 +214,7 @@ export class SvgIconComponent implements OnInit, OnDestroy, OnChanges, DoCheck { const len = attributes.length; for (let i = 0; i < len; i += 1) { const attribute = attributes.item(i); - if (attribute.name.startsWith('_ngcontent')) { + if (attribute && attribute.name.startsWith('_ngcontent')) { this.setNgContentAttribute(icon, attribute.name); break; } @@ -224,7 +233,7 @@ export class SvgIconComponent implements OnInit, OnDestroy, OnChanges, DoCheck { } private stylize() { - if (this.svg) { + if (this.helper.svg) { const svg = this.element.nativeElement.firstChild; if (this.stretch === true) { @@ -253,7 +262,7 @@ export class SvgIconComponent implements OnInit, OnDestroy, OnChanges, DoCheck { } } - private setClass(target: HTMLElement|SVGSVGElement, previous: string|string[], current: string|string[]) { + private setClass(target: HTMLElement|SVGSVGElement, previous: string|string[]|null, current: string|string[]|null) { if (target) { if (previous) { const klasses = Array.isArray(previous) ? previous : previous.split(' '); diff --git a/projects/angular-svg-icon/src/test.ts b/projects/angular-svg-icon/src/test.ts index cec9a66..34806cd 100644 --- a/projects/angular-svg-icon/src/test.ts +++ b/projects/angular-svg-icon/src/test.ts @@ -1,7 +1,7 @@ // This file is required by karma.conf.js and loads recursively all the .spec and framework files -import 'zone.js/dist/zone'; -import 'zone.js/dist/zone-testing'; +import 'zone.js'; +import 'zone.js/testing'; import { getTestBed } from '@angular/core/testing'; import { BrowserDynamicTestingModule, diff --git a/src/app/demo-app.component.html b/src/app/demo-app.component.html index c89d838..083c443 100644 --- a/src/app/demo-app.component.html +++ b/src/app/demo-app.component.html @@ -1,6 +1,6 @@
-
+
@@ -73,6 +73,7 @@

Working with CSS classes

+

{{klass}}

<svg-icon src="assets/images/lightbulb.svg" [svgClass]="{{svgKlass}}"
   [class]="{{klass}}" [applyClass]="{{applied|json}}">
@@ -89,7 +90,6 @@ 

Working with CSS classes

-

Apply class to different SVG parts

Elements inside the SVG (path, polygon, etc.) can be extended via the class attribute of the <svg‑icon> diff --git a/src/app/demo-app.component.scss b/src/app/demo-app.component.scss index 3a3b2eb..7177ef0 100644 --- a/src/app/demo-app.component.scss +++ b/src/app/demo-app.component.scss @@ -26,39 +26,65 @@ input { margin: 0 12px 0 4px; } +div.example { + width: 500px; + display: inline-flex; + flex-wrap: wrap; + gap: 14px; + + > * { + width: 90px; + } + + /* Have svg be correct color */ + .red { + fill: red; + } + .green { + fill: green; + } + .blue { + fill: blue; + } +} + .explain { max-width: 500px; font-size: 14px; } - +/* Text only color */ .red { - fill: red; + color: red; } -svg-icon.red { - width: 90px; - float: left; +/* svg fll color */ +svg.red { + fill: red; +// fill: #00ffff; } + +/* Text only color */ .green { - fill: green; + color: green; } -svg-icon.green { - width: 90px; - float: left; - margin-left: 20px; +/* svg fll color */ +svg.green { + fill: green; +// fill: #ff00ff; } +/* Text only color */ .blue { - fill: blue; + color: blue; } -svg-icon.blue { - width: 90px; - float: left; - margin-left: 20px; +/* svg fll color */ +svg.blue { + fill: blue; +// fill: #ffff00; } pre { @@ -140,6 +166,15 @@ code { text-align: center; } +p.bulb +{ + border: 0; + min-height: 1em; + max-height: 1em; + margin: 2px; + font-weight: 600; +} + select { margin-right: 6px; min-width: 80px; diff --git a/src/assets/images/bell.svg b/src/assets/images/bell.svg index 4a788d7..ad2cb1c 100644 --- a/src/assets/images/bell.svg +++ b/src/assets/images/bell.svg @@ -1,5 +1,5 @@ - diff --git a/src/environments/environment.ts b/src/environments/environment.ts index c5a517a..e5430da 100644 --- a/src/environments/environment.ts +++ b/src/environments/environment.ts @@ -13,4 +13,4 @@ export const environment = { * This import should be commented out in production mode because it will have a negative impact * on performance if an error is thrown. */ -// import 'zone.js/dist/zone-error'; // Included with Angular CLI. +// import 'zone.js/plugins/zone-error'; // Included with Angular CLI. diff --git a/src/index.html b/src/index.html index dd942a3..baa7e34 100644 --- a/src/index.html +++ b/src/index.html @@ -8,7 +8,7 @@ - + diff --git a/src/polyfills.ts b/src/polyfills.ts index aa665d6..e484510 100644 --- a/src/polyfills.ts +++ b/src/polyfills.ts @@ -55,7 +55,7 @@ /*************************************************************************************************** * Zone JS is required by default for Angular itself. */ -import 'zone.js/dist/zone'; // Included with Angular CLI. +import 'zone.js'; // Included with Angular CLI. /*************************************************************************************************** diff --git a/src/test.ts b/src/test.ts index ec1961f..0b99b20 100644 --- a/src/test.ts +++ b/src/test.ts @@ -1,6 +1,6 @@ // This file is required by karma.conf.js and loads recursively all the .spec and framework files -import 'zone.js/dist/zone-testing'; +import 'zone.js/testing'; import { getTestBed } from '@angular/core/testing'; import { BrowserDynamicTestingModule, diff --git a/tsconfig.json b/tsconfig.json index a2e0575..3a532e2 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,13 +1,16 @@ { "compileOnSave": false, "compilerOptions": { + "forceConsistentCasingInFileNames": true, + "strict": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, "baseUrl": "./", "outDir": "./dist/out-tsc", "sourceMap": true, "declaration": false, "module": "es2020", "moduleResolution": "node", - "emitDecoratorMetadata": true, "experimentalDecorators": true, "removeComments": false, "noImplicitAny": true, @@ -28,5 +31,10 @@ "dist/angular-svg-icon/*" ] } + }, + "angularCompilerOptions": { + "strictInjectionParameters": true, + "strictInputAccessModifiers": true, + "strictTemplates": true } }