diff --git a/DEV_DOCS.md b/DEV_DOCS.md index 408d02738f..dad29bc1ac 100644 --- a/DEV_DOCS.md +++ b/DEV_DOCS.md @@ -334,6 +334,8 @@ To give the user capability switch between live and inline representation of the # Release +0. For major version, search for `@breaking-change` to make sure all breaking changes are covered. + To start a new release (publish the framework packages on NPM) you need: 1. create a new release branch called `release:v1.0.2` diff --git a/angular.json b/angular.json index b31d35ec14..e35e497c22 100644 --- a/angular.json +++ b/angular.json @@ -26,7 +26,6 @@ "src/favicon.ico" ], "styles": [ - "node_modules/nebular-icons/scss/nebular-icons.scss", "src/styles.scss" ], "scripts": [] @@ -77,7 +76,6 @@ "tsConfig": "src/tsconfig.spec.json", "scripts": [], "styles": [ - "node_modules/nebular-icons/scss/nebular-icons.scss", "src/styles.scss" ], "assets": [ @@ -147,7 +145,6 @@ "src/favicon.ico" ], "styles": [ - "node_modules/nebular-icons/scss/nebular-icons.scss", "src/styles.scss" ], "scripts": [] @@ -198,7 +195,6 @@ "tsConfig": "src/tsconfig.spec.json", "scripts": [], "styles": [ - "node_modules/nebular-icons/scss/nebular-icons.scss", "src/styles.scss" ], "assets": [ @@ -266,7 +262,6 @@ "node_modules/bootstrap/dist/css/bootstrap.css", "node_modules/docsearch.js/dist/cdn/docsearch.min.css", "node_modules/highlight.js/styles/dracula.css", - "node_modules/nebular-icons/scss/nebular-icons.scss", "docs/app/@theme/styles/styles.scss" ], "scripts": [ @@ -321,7 +316,6 @@ ], "styles": [ "node_modules/highlight.js/styles/dracula.css", - "node_modules/nebular-icons/scss/nebular-icons.scss", "docs/app/@theme/styles/styles.scss" ], "assets": [ diff --git a/docs/app/@theme/components/header/header.component.scss b/docs/app/@theme/components/header/header.component.scss index 77eb3773bc..f2280dea68 100644 --- a/docs/app/@theme/components/header/header.component.scss +++ b/docs/app/@theme/components/header/header.component.scss @@ -22,8 +22,10 @@ flex: 1 0 auto; padding: 0 0.5rem; - .nb-menu { + nb-icon { vertical-align: middle; + font-size: 1.75rem; + color: $version-fg; } } diff --git a/docs/app/@theme/components/header/header.component.ts b/docs/app/@theme/components/header/header.component.ts index 7071250423..e630a56223 100644 --- a/docs/app/@theme/components/header/header.component.ts +++ b/docs/app/@theme/components/header/header.component.ts @@ -8,7 +8,7 @@ import { NgdVersionService } from '../../services'; template: `
- +
- + diff --git a/docs/app/blocks/components/live-example-block/live-example-block.component.scss b/docs/app/blocks/components/live-example-block/live-example-block.component.scss index 2182194854..e5ec9a957b 100644 --- a/docs/app/blocks/components/live-example-block/live-example-block.component.scss +++ b/docs/app/blocks/components/live-example-block/live-example-block.component.scss @@ -40,7 +40,7 @@ display: flex; width: 100%; - .icon { + nb-icon { font-size: 0.95rem; } } @@ -73,7 +73,7 @@ color: transparent; } - .icon { + nb-icon { color: $action-fg; position: absolute; top: 50%; @@ -156,7 +156,7 @@ padding: 0 2.5rem; } - .icon { + nb-icon { left: 1.25rem; transform: translate(0, -50%); } diff --git a/docs/app/blocks/components/pager-block/pager-block.component.scss b/docs/app/blocks/components/pager-block/pager-block.component.scss index 90e6c269fc..eea8c64725 100644 --- a/docs/app/blocks/components/pager-block/pager-block.component.scss +++ b/docs/app/blocks/components/pager-block/pager-block.component.scss @@ -32,7 +32,7 @@ font-weight: 500; font-size: 1.2rem; - i { + nb-icon { color: $arrow-fg; margin-top: 0.3rem; } diff --git a/docs/app/blocks/components/pager-block/pager-block.component.ts b/docs/app/blocks/components/pager-block/pager-block.component.ts index 2865311470..48c90e422a 100644 --- a/docs/app/blocks/components/pager-block/pager-block.component.ts +++ b/docs/app/blocks/components/pager-block/pager-block.component.ts @@ -11,7 +11,7 @@ import { NgdPaginationService } from '../../../@theme/services';
- + {{ paginationItem.prev.title }}
Previous page
@@ -23,7 +23,7 @@ import { NgdPaginationService } from '../../../@theme/services'; [attr.title]="paginationItem.next.title">
{{ paginationItem.next.title }} - +
Next page
diff --git a/docs/app/blocks/components/tabbed-example-block/tabbed-example-block.component.html b/docs/app/blocks/components/tabbed-example-block/tabbed-example-block.component.html index c449372404..432cfa3080 100644 --- a/docs/app/blocks/components/tabbed-example-block/tabbed-example-block.component.html +++ b/docs/app/blocks/components/tabbed-example-block/tabbed-example-block.component.html @@ -2,7 +2,7 @@ *ngIf="hasViewSwitch" class="btn action-item action-button" (click)="switchToLiveView()"> - + Live view diff --git a/docs/app/blocks/components/tabbed-example-block/tabbed-example-block.component.scss b/docs/app/blocks/components/tabbed-example-block/tabbed-example-block.component.scss index 253f48115e..b9a62d2c0d 100644 --- a/docs/app/blocks/components/tabbed-example-block/tabbed-example-block.component.scss +++ b/docs/app/blocks/components/tabbed-example-block/tabbed-example-block.component.scss @@ -22,8 +22,9 @@ font-weight: normal; font-size: 0.9rem; - .icon { - font-size: 0.95rem; + nb-icon { + font-size: 1rem; + vertical-align: middle; } &:focus, &:active, &:hover { diff --git a/docs/app/blocks/components/theme-block/theme-block.component.html b/docs/app/blocks/components/theme-block/theme-block.component.html index 56b9a9f7db..e35d65ee5f 100644 --- a/docs/app/blocks/components/theme-block/theme-block.component.html +++ b/docs/app/blocks/components/theme-block/theme-block.component.html @@ -31,7 +31,7 @@

{{ vm.themeTitle }} Theme

- + {{ parent.prop }} ({{ parent.theme }}) diff --git a/docs/app/blocks/components/theme-block/theme-block.component.scss b/docs/app/blocks/components/theme-block/theme-block.component.scss index 247a848abb..b952940d10 100644 --- a/docs/app/blocks/components/theme-block/theme-block.component.scss +++ b/docs/app/blocks/components/theme-block/theme-block.component.scss @@ -9,7 +9,9 @@ $selected-row-bg: nb-theme(color-gray-light); .inheritance-icon { + font-size: 1rem; margin: 0 0.25rem; + vertical-align: middle; } .inheritance-property { diff --git a/docs/app/documentation/documentation.component.scss b/docs/app/documentation/documentation.component.scss index d2b253381d..d620435cc3 100644 --- a/docs/app/documentation/documentation.component.scss +++ b/docs/app/documentation/documentation.component.scss @@ -76,6 +76,10 @@ } } + nb-icon { + display: none; + } + .menu-items .menu-item .menu-item a { &:hover, &.active, &:focus { text-shadow: 0.5px 0 0 currentColor; diff --git a/docs/app/example/example-404.component.scss b/docs/app/example/example-404.component.scss index 6457cca5a5..50b8c3282e 100644 --- a/docs/app/example/example-404.component.scss +++ b/docs/app/example/example-404.component.scss @@ -1,4 +1,4 @@ -::ng-deep nb-layout-column { +:host { align-items: center; display: flex; color: gray; diff --git a/docs/app/example/example-404.component.ts b/docs/app/example/example-404.component.ts index 727fe1ff18..28f3aabe9b 100644 --- a/docs/app/example/example-404.component.ts +++ b/docs/app/example/example-404.component.ts @@ -4,9 +4,7 @@ import { NbThemeService } from '@nebular/theme'; @Component({ selector: 'ngd-example-404', template: ` - - Example not found. - + Example not found. `, styleUrls: ['./example-404.component.scss'], }) diff --git a/docs/app/example/example.module.ts b/docs/app/example/example.module.ts index a18eaf05c1..c433862c66 100644 --- a/docs/app/example/example.module.ts +++ b/docs/app/example/example.module.ts @@ -8,14 +8,12 @@ import { NgModule } from '@angular/core'; import { NgdExampleRoutingModule } from './example-routing.module'; import { NgdExampleComponent } from './example.component'; -import { NgdThemeModule } from '../@theme/theme.module'; import { NgdExample404Component } from './example-404.component'; @NgModule({ imports: [ NgdExampleRoutingModule, - NgdThemeModule, ], declarations: [ NgdExampleComponent, diff --git a/docs/articles/install-into-existing.md b/docs/articles/install-into-existing.md index 2d369e311b..6984a14c80 100644 --- a/docs/articles/install-into-existing.md +++ b/docs/articles/install-into-existing.md @@ -59,6 +59,12 @@ At this step, we assume you already have Angular modules installed. ```bash npm install --save @nebular/theme @angular/cdk @angular/animations ``` +Also, you may want to install Eva Icons pack, which is a recommended SVG icons library starting from Nebular 4.0. +```bash +npm install --save @nebular/eva-icons +``` +More details on [how to use Nebular Eva Icons are here](docs/components/icon/overview#nbiconcomponent). + Additionally you can install Auth and Security `npm install --save @nebular/auth @nebular/security`
diff --git a/docs/assets/fonts/feather/feather.eot b/docs/assets/fonts/feather/feather.eot deleted file mode 100755 index 58371d9085..0000000000 Binary files a/docs/assets/fonts/feather/feather.eot and /dev/null differ diff --git a/docs/assets/fonts/feather/feather.svg b/docs/assets/fonts/feather/feather.svg deleted file mode 100755 index 5dda143b66..0000000000 --- a/docs/assets/fonts/feather/feather.svg +++ /dev/null @@ -1,849 +0,0 @@ - - - - - -Created by iconfont - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/docs/assets/fonts/feather/feather.ttf b/docs/assets/fonts/feather/feather.ttf deleted file mode 100755 index 0b33dac782..0000000000 Binary files a/docs/assets/fonts/feather/feather.ttf and /dev/null differ diff --git a/docs/assets/fonts/feather/feather.woff b/docs/assets/fonts/feather/feather.woff deleted file mode 100755 index 9b03a72a01..0000000000 Binary files a/docs/assets/fonts/feather/feather.woff and /dev/null differ diff --git a/docs/assets/images/components/icon.svg b/docs/assets/images/components/icon.svg new file mode 100644 index 0000000000..67fceac3f6 --- /dev/null +++ b/docs/assets/images/components/icon.svg @@ -0,0 +1,196 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/structure.ts b/docs/structure.ts index e5a9315d05..3482803ad9 100644 --- a/docs/structure.ts +++ b/docs/structure.ts @@ -420,6 +420,15 @@ export const structure = [ 'NbAlertComponent', ], }, + { + type: 'tabs', + name: 'Icon', + icon: 'icon.svg', + source: [ + 'NbIconComponent', + 'NbIconLibraries', + ], + }, { type: 'tabs', name: 'Spinner', diff --git a/docs/tsconfig.app.json b/docs/tsconfig.app.json index 52d70edd56..289ccf8992 100644 --- a/docs/tsconfig.app.json +++ b/docs/tsconfig.app.json @@ -19,7 +19,8 @@ "@nebular/theme": ["../src/framework/theme"], "@nebular/theme/*": ["../src/framework/theme/*"], "@nebular/auth": ["../src/framework/auth"], - "@nebular/security": ["../src/framework/security"] + "@nebular/security": ["../src/framework/security"], + "@nebular/eva-icons": ["../src/framework/eva-icons"] } }, "exclude": [ diff --git a/package-lock.json b/package-lock.json index 1c568ed82a..97e1816ace 100644 --- a/package-lock.json +++ b/package-lock.json @@ -683,7 +683,7 @@ }, "@google-cloud/common": { "version": "0.17.0", - "resolved": "https://registry.npmjs.org/@google-cloud/common/-/common-0.17.0.tgz", + "resolved": "http://registry.npmjs.org/@google-cloud/common/-/common-0.17.0.tgz", "integrity": "sha512-HRZLSU762E6HaKoGfJGa8W95yRjb9rY7LePhjaHK9ILAnFacMuUGVamDbTHu1csZomm1g3tZTtXfX/aAhtie/Q==", "dev": true, "optional": true, @@ -3738,13 +3738,13 @@ "dependencies": { "minimist": { "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", "dev": true }, "mkdirp": { "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "resolved": "http://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", "dev": true, "requires": { @@ -6014,6 +6014,11 @@ "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", "dev": true }, + "eva-icons": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/eva-icons/-/eva-icons-1.1.1.tgz", + "integrity": "sha512-Uf7JQMA1lWQLLtHuk1lbMlwP4i3W0jjTy5SCYBGeR6DLPub5Ls2QiJlVT59llphVObXSVwaMuofevwgUZWf1CQ==" + }, "event-emitter": { "version": "0.3.5", "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", @@ -12760,11 +12765,6 @@ "dev": true, "optional": true }, - "nebular-icons": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/nebular-icons/-/nebular-icons-1.1.0.tgz", - "integrity": "sha512-4TkJgDrjXw8qsOsc2NN6OBlRJYAJJpvqtjV+gpv+NV6amPgMPQNNuEvbenYXAMFmjs3uShgKOvmpnCX8Ua1oWQ==" - }, "negotiator": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", @@ -13509,7 +13509,7 @@ }, "onetime": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz", + "resolved": "http://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz", "integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=", "dev": true }, @@ -13610,7 +13610,7 @@ }, "p-cancelable": { "version": "0.4.1", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-0.4.1.tgz", + "resolved": "http://registry.npmjs.org/p-cancelable/-/p-cancelable-0.4.1.tgz", "integrity": "sha512-HNa1A8LvB1kie7cERyy21VNeHb2CWJJYqyyC2o3klWFfMGlFmWv2Z7sFgZH8ZiaYL95ydToKTFVXgMV/Os0bBQ==", "dev": true, "optional": true diff --git a/package.json b/package.json index 365f9b6792..ed586f509f 100644 --- a/package.json +++ b/package.json @@ -101,6 +101,7 @@ "core-js": "2.5.7", "date-fns": ">=2.0.0-alpha.16 <=2.0.0-alpha.27", "docsearch.js": "^2.5.2", + "eva-icons": "^1.1.1", "gulp-bump": "2.7.0", "highlight.js": "9.12.0", "intersection-observer": "0.5.0", @@ -109,7 +110,6 @@ "leaflet": "1.0.3", "marked": "0.3.9", "moment": "^2.22.2", - "nebular-icons": "1.1.0", "normalize.css": "8.0.0", "rxjs": "6.3.0", "socicon": "3.0.5", @@ -176,4 +176,4 @@ "uglifyjs-webpack-plugin": "1.1.5" }, "schematics": "./schematics/dist/collection.json" -} \ No newline at end of file +} diff --git a/src/app/app.module.ts b/src/app/app.module.ts index d23580d7ea..0a981cd355 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -17,6 +17,7 @@ import { LayoutDirectionToggleComponent } from './layout-direction-toggle/layout import { LayoutThemeToggleComponent } from './layout-theme-toggle/layout-theme-toggle.component'; import { ComponentsOverlayComponent } from './components-list/components-overlay.component'; import { ComponentsListComponent} from './components-list/components-list.component'; +import { NbEvaIconsModule } from '@nebular/eva-icons'; @NgModule({ imports: [ @@ -31,6 +32,7 @@ import { ComponentsListComponent} from './components-list/components-list.compon }, ], { useHash: true }), NbThemeModule.forRoot(), + NbEvaIconsModule, ], declarations: [ AppComponent, diff --git a/src/app/playground-components.ts b/src/app/playground-components.ts index 793d5594c3..68435a00b7 100644 --- a/src/app/playground-components.ts +++ b/src/app/playground-components.ts @@ -174,6 +174,12 @@ export const PLAYGROUND_COMPONENTS: ComponentLink[] = [ component: 'ButtonTypesComponent', name: 'Button Types', }, + { + path: 'button-icon.component', + link: '/button/button-icon.component', + component: 'ButtonIconComponent', + name: 'Button Icon', + }, ], }, { @@ -1320,6 +1326,23 @@ export const PLAYGROUND_COMPONENTS: ComponentLink[] = [ }, ], }, + { + path: 'icon', + children: [ + { + path: 'icon-showcase.component', + link: '/icon/icon-showcase.component', + component: 'IconShowcaseComponent', + name: 'Icon Showcase', + }, + { + path: 'icon-colors.component', + link: '/icon/icon-colors.component', + component: 'IconColorsComponent', + name: 'Icon Colors', + }, + ], + }, { path: 'context-menu', children: [ diff --git a/src/framework/auth/auth.module.ts b/src/framework/auth/auth.module.ts index b61fdaaff1..082d695b62 100644 --- a/src/framework/auth/auth.module.ts +++ b/src/framework/auth/auth.module.ts @@ -9,6 +9,7 @@ import { NbButtonModule, NbCardModule, NbCheckboxModule, + NbIconModule, NbInputModule, NbLayoutModule, } from '@nebular/theme'; @@ -94,6 +95,7 @@ export function nbNoOpInterceptorFilter(req: HttpRequest): boolean { NbButtonModule, RouterModule, FormsModule, + NbIconModule, ], declarations: [ NbAuthComponent, diff --git a/src/framework/auth/components/auth.component.scss b/src/framework/auth/components/auth.component.scss index d2c4f9f20b..ce6b0c4cb1 100644 --- a/src/framework/auth/components/auth.component.scss +++ b/src/framework/auth/components/auth.component.scss @@ -11,7 +11,7 @@ .navigation .link { text-decoration: none; - .icon { + nb-icon { font-size: 2rem; } } diff --git a/src/framework/auth/components/auth.component.ts b/src/framework/auth/components/auth.component.ts index cd5e020619..aa432f7722 100644 --- a/src/framework/auth/components/auth.component.ts +++ b/src/framework/auth/components/auth.component.ts @@ -18,7 +18,7 @@ import { takeWhile } from 'rxjs/operators'; diff --git a/src/framework/eva-icons/README.md b/src/framework/eva-icons/README.md new file mode 100644 index 0000000000..6b7e7c8727 --- /dev/null +++ b/src/framework/eva-icons/README.md @@ -0,0 +1 @@ +### @nebular/eva-icons module, more details https://akveo.github.io/nebular/ diff --git a/src/framework/eva-icons/eva-icons.module.ts b/src/framework/eva-icons/eva-icons.module.ts new file mode 100644 index 0000000000..b81fccde8f --- /dev/null +++ b/src/framework/eva-icons/eva-icons.module.ts @@ -0,0 +1,63 @@ +/* + * @license + * Copyright Akveo. All Rights Reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + */ + +import { NgModule } from '@angular/core'; +import { NbIconLibraries, NbSvgIcon, NbIcons } from '@nebular/theme'; +import { icons } from 'eva-icons'; + +interface NbOriginalEvaIcon { + toSvg(options: NbEvaIconOptions); +} + +export interface NbEvaIconOptions { + width: string, + height: string, + fill: string, + animation: { + type: string, + hover: boolean, + infinite: boolean, + }, +} + +export class NbEvaSvgIcon extends NbSvgIcon { + + constructor(protected name, protected content: NbOriginalEvaIcon) { + super(name, ''); + } + + getContent(options): string { + return this.content.toSvg({ + width: '100%', + height: '100%', + fill: 'currentColor', + ...options, + }); + } +} + +@NgModule({}) +export class NbEvaIconsModule { + + private NAME = 'eva'; + + constructor(iconLibrary: NbIconLibraries) { + iconLibrary.registerSvgPack(this.NAME, this.createIcons()); + iconLibrary.setDefaultPack(this.NAME); + } + + private createIcons(): NbIcons { + return Object + .entries(icons) + .map(([name, icon]) => { + return [name, new NbEvaSvgIcon(name, icon)] as [string, NbSvgIcon]; + }) + .reduce((newIcons, [name, icon]: [string, NbSvgIcon]) => { + newIcons[name] = icon; + return newIcons; + }, {}); + } +} diff --git a/src/framework/eva-icons/index.ts b/src/framework/eva-icons/index.ts new file mode 100644 index 0000000000..68fc82358e --- /dev/null +++ b/src/framework/eva-icons/index.ts @@ -0,0 +1,7 @@ +/* + * @license + * Copyright Akveo. All Rights Reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + */ + +export * from './eva-icons.module'; diff --git a/src/framework/eva-icons/package.json b/src/framework/eva-icons/package.json new file mode 100644 index 0000000000..7b5792cc10 --- /dev/null +++ b/src/framework/eva-icons/package.json @@ -0,0 +1,32 @@ +{ + "name": "@nebular/eva-icons", + "version": "3.4.0", + "description": "@nebular/eva-icons", + "main": "./bundles/eva-icons.umd.js", + "module": "./index.js", + "typings": "./index.d.ts", + "author": "akveo", + "license": "MIT", + "repository": { + "type": "git", + "url": "git+https://github.com/akveo/nebular.git" + }, + "bugs": { + "url": "https://github.com/akveo/nebular/issues" + }, + "homepage": "https://github.com/akveo/nebular#readme", + "keywords": [ + "angular", + "typescript", + "ng2-admin", + "ngx-admin", + "icons", + "theme", + "nebular", + "eva-icons" + ], + "peerDependencies": { + "@nebular/theme": "3.4.0", + "eva-icons": "^1.1.1" + } +} diff --git a/src/framework/icons/README.md b/src/framework/icons/README.md index 06f56454f9..e7c53dbc2b 100644 --- a/src/framework/icons/README.md +++ b/src/framework/icons/README.md @@ -1 +1,3 @@ # Nebular icons - amazing icons crafted with love by Akveo team + +### This package is deprecated. Please check [Eva Icons](https://akveo.github.io/eva-icons/) and new [nb-icon](https://akveo.github.io/nebular/docs/components/icon) component. diff --git a/src/framework/icons/package.json b/src/framework/icons/package.json index 7025c732c8..40b1c3b554 100644 --- a/src/framework/icons/package.json +++ b/src/framework/icons/package.json @@ -1,6 +1,6 @@ { "name": "nebular-icons", - "version": "1.1.0", + "version": "1.1.1", "description": "Nebular icons - amazing icons crafted with love by Akveo team", "author": "akveo", "license": "MIT", diff --git a/src/framework/theme/components/accordion/_accordion.component.theme.scss b/src/framework/theme/components/accordion/_accordion.component.theme.scss index 0f56682f43..f494583a47 100644 --- a/src/framework/theme/components/accordion/_accordion.component.theme.scss +++ b/src/framework/theme/components/accordion/_accordion.component.theme.scss @@ -29,7 +29,7 @@ position: relative; @include nb-accordion-item-header(); - i { + nb-icon { position: absolute; @include nb-ltr(right, 1rem); @include nb-rtl(left, 1rem); diff --git a/src/framework/theme/components/accordion/accordion-item-header.component.ts b/src/framework/theme/components/accordion/accordion-item-header.component.ts index cd6f0e87b1..3dc2567b9d 100644 --- a/src/framework/theme/components/accordion/accordion-item-header.component.ts +++ b/src/framework/theme/components/accordion/accordion-item-header.component.ts @@ -29,7 +29,8 @@ import { NbAccordionItemComponent } from './accordion-item.component'; - + + `, animations: [ trigger('expansionIndicator', [ diff --git a/src/framework/theme/components/accordion/accordion.module.ts b/src/framework/theme/components/accordion/accordion.module.ts index 34ab98cdb9..ccf8a104f0 100644 --- a/src/framework/theme/components/accordion/accordion.module.ts +++ b/src/framework/theme/components/accordion/accordion.module.ts @@ -7,6 +7,7 @@ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; +import { NbIconModule } from '../icon/icon.module'; import { NbAccordionComponent } from './accordion.component'; import { NbAccordionItemComponent } from './accordion-item.component'; import { NbAccordionItemHeaderComponent } from './accordion-item-header.component'; @@ -20,7 +21,7 @@ const NB_ACCORDION_COMPONENTS = [ ]; @NgModule({ - imports: [CommonModule], + imports: [CommonModule, NbIconModule], exports: [...NB_ACCORDION_COMPONENTS], declarations: [...NB_ACCORDION_COMPONENTS], providers: [], diff --git a/src/framework/theme/components/actions/_actions.component.theme.scss b/src/framework/theme/components/actions/_actions.component.theme.scss index e5d34588c0..ce441a046e 100644 --- a/src/framework/theme/components/actions/_actions.component.theme.scss +++ b/src/framework/theme/components/actions/_actions.component.theme.scss @@ -26,7 +26,7 @@ } } - i.control-icon { + nb-icon { color: nb-theme(actions-fg); font-size: nb-theme(actions-size-small); } @@ -39,7 +39,7 @@ &.inverse { nb-action { - i.control-icon { + nb-icon { color: nb-theme(actions-bg); } @@ -51,7 +51,7 @@ &.small { nb-action { height: nb-theme(actions-size-small); - i.control-icon { + nb-icon { font-size: nb-theme(actions-size-small); } } @@ -59,7 +59,7 @@ &.medium { nb-action { height: nb-theme(actions-size-medium); - i.control-icon { + nb-icon { font-size: nb-theme(actions-size-medium); } } @@ -67,7 +67,7 @@ &.large { nb-action { height: nb-theme(actions-size-large); - i.control-icon { + nb-icon { font-size: nb-theme(actions-size-large); } } diff --git a/src/framework/theme/components/actions/actions.component.scss b/src/framework/theme/components/actions/actions.component.scss index 34afbf7e0e..a4a401a9ba 100644 --- a/src/framework/theme/components/actions/actions.component.scss +++ b/src/framework/theme/components/actions/actions.component.scss @@ -15,10 +15,8 @@ align-items: center; position: relative; - i.control-icon { - &:hover { - cursor: pointer; - } + nb-icon:hover { + cursor: pointer; } &.disabled { diff --git a/src/framework/theme/components/actions/actions.component.ts b/src/framework/theme/components/actions/actions.component.ts index bbf187c1a7..174aee57cf 100644 --- a/src/framework/theme/components/actions/actions.component.ts +++ b/src/framework/theme/components/actions/actions.component.ts @@ -19,20 +19,20 @@ import { convertToBoolProperty } from '../helpers'; [routerLink]="link" [title]="title" *ngIf="link"> - + - + - + diff --git a/src/framework/theme/components/actions/actions.module.ts b/src/framework/theme/components/actions/actions.module.ts index d8c08654a0..853c9aebe3 100644 --- a/src/framework/theme/components/actions/actions.module.ts +++ b/src/framework/theme/components/actions/actions.module.ts @@ -11,6 +11,7 @@ import { NbSharedModule } from '../shared/shared.module'; import { NbActionComponent, NbActionsComponent } from './actions.component'; import { NbBadgeModule } from '../badge/badge.module'; +import { NbIconModule } from '../icon/icon.module'; const NB_ACTIONS_COMPONENTS = [ NbActionComponent, @@ -21,6 +22,7 @@ const NB_ACTIONS_COMPONENTS = [ imports: [ NbSharedModule, NbBadgeModule, + NbIconModule, ], declarations: [ ...NB_ACTIONS_COMPONENTS, diff --git a/src/framework/theme/components/button/button.component.scss b/src/framework/theme/components/button/button.component.scss index cbbae4913f..b6387efaf1 100644 --- a/src/framework/theme/components/button/button.component.scss +++ b/src/framework/theme/components/button/button.component.scss @@ -4,6 +4,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. */ +@import '../../styles/core/mixins'; + :host { text-transform: uppercase; letter-spacing: 0.4px; @@ -26,4 +28,18 @@ &.btn-full-width { width: 100%; } + + ::ng-deep nb-icon { + vertical-align: top; + } + + &.icon-start:not(.icon-end) ::ng-deep nb-icon { + @include nb-ltr(margin-right, 0.75rem); + @include nb-rtl(margin-left, 0.75rem); + } + + &.icon-end:not(.icon-start) ::ng-deep nb-icon { + @include nb-ltr(margin-left, 0.75rem); + @include nb-rtl(margin-right, 0.75rem); + } } diff --git a/src/framework/theme/components/button/button.component.ts b/src/framework/theme/components/button/button.component.ts index b4a2ffb98a..7148c7c162 100644 --- a/src/framework/theme/components/button/button.component.ts +++ b/src/framework/theme/components/button/button.component.ts @@ -55,6 +55,9 @@ import { convertToBoolProperty } from '../helpers'; * Button can be made `fullWidth`: * @stacked-example(Full Width Button, button/button-full-width.component.html) * + * Icon can be placed inside of a button as a child element: + * @stacked-example(Icon Button, button/button-icon.component.html) + * * @styles * * btn-fg: @@ -209,6 +212,20 @@ export class NbButtonComponent { return this.disabled ? '-1' : '0'; } + @HostBinding('class.icon-start') + get iconLeft(): boolean { + const el = this.hostElement.nativeElement; + const icon = this.iconElement; + return !!(icon && el.firstChild === icon); + } + + @HostBinding('class.icon-end') + get iconRight(): boolean { + const el = this.hostElement.nativeElement; + const icon = this.iconElement; + return !!(icon && el.lastChild === icon); + } + @HostBinding('class.btn-full-width') fullWidth = false; @@ -241,6 +258,11 @@ export class NbButtonComponent { this.shape = val; } + private get iconElement() { + const el = this.hostElement.nativeElement; + return el.querySelector('nb-icon'); + } + /** * Adds `hero` styles * @param {boolean} val diff --git a/src/framework/theme/components/card/card.module.ts b/src/framework/theme/components/card/card.module.ts index 5a1873058f..deeef96ee4 100644 --- a/src/framework/theme/components/card/card.module.ts +++ b/src/framework/theme/components/card/card.module.ts @@ -7,7 +7,7 @@ import { NgModule } from '@angular/core'; import { NbSharedModule } from '../shared/shared.module'; - +import { NbIconModule } from '../icon/icon.module'; import { NbCardComponent, NbCardBodyComponent, @@ -33,6 +33,7 @@ const NB_CARD_COMPONENTS = [ @NgModule({ imports: [ NbSharedModule, + NbIconModule, ], declarations: [ ...NB_CARD_COMPONENTS, diff --git a/src/framework/theme/components/card/flip-card/flip-card.component.ts b/src/framework/theme/components/card/flip-card/flip-card.component.ts index f8ceff734d..1f6d2dab4e 100644 --- a/src/framework/theme/components/card/flip-card/flip-card.component.ts +++ b/src/framework/theme/components/card/flip-card/flip-card.component.ts @@ -63,13 +63,13 @@ import { Component, Input, HostBinding } from '@angular/core'; diff --git a/src/framework/theme/components/card/reveal-card/reveal-card.component.ts b/src/framework/theme/components/card/reveal-card/reveal-card.component.ts index 6ddf270c8e..dd7f79ef3e 100644 --- a/src/framework/theme/components/card/reveal-card/reveal-card.component.ts +++ b/src/framework/theme/components/card/reveal-card/reveal-card.component.ts @@ -63,7 +63,7 @@ import { Component, Input, HostBinding } from '@angular/core'; - + `, }) diff --git a/src/framework/theme/components/chat/_chat.component.theme.scss b/src/framework/theme/components/chat/_chat.component.theme.scss index 99b305b5ab..3d618fda0f 100644 --- a/src/framework/theme/components/chat/_chat.component.theme.scss +++ b/src/framework/theme/components/chat/_chat.component.theme.scss @@ -12,6 +12,10 @@ border-radius: nb-theme(chat-border-radius); box-shadow: nb-theme(chat-shadow); + nb-icon { + font-size: inherit; + } + .header { color: nb-theme(chat-fg-text); padding: nb-theme(chat-padding); @@ -261,12 +265,17 @@ @include nb-rtl(border-bottom-right-radius, 0); @include nb-rtl(border-top-right-radius, 0); padding: 0 1.5rem; + color: white; &.with-icon { - font-size: 3rem; + font-size: 1.25rem; line-height: 1; padding: 0 1.25rem 0 0.875rem; text-align: center; + + nb-icon { + vertical-align: middle; + } } } @@ -293,7 +302,6 @@ margin-bottom: 0.5rem; border: 1px solid nb-theme(chat-form-fg); text-align: center; - line-height: 3rem; font-size: 2rem; color: nb-theme(chat-form-fg); position: relative; @@ -306,6 +314,10 @@ line-height: 1; cursor: pointer; } + + nb-icon { + vertical-align: middle; + } } } } diff --git a/src/framework/theme/components/chat/chat-form.component.ts b/src/framework/theme/components/chat/chat-form.component.ts index 1399b240e2..73fc7a31cc 100644 --- a/src/framework/theme/components/chat/chat-form.component.ts +++ b/src/framework/theme/components/chat/chat-form.component.ts @@ -60,7 +60,9 @@ import { DomSanitizer } from '@angular/platform-browser';
×
-
+ +
+ ×
@@ -72,7 +74,7 @@ import { DomSanitizer } from '@angular/platform-browser'; placeholder="{{ fileOver ? 'Drop file to send' : 'Type a message' }}" (keyup.enter)="sendMessage()">
`, @@ -99,7 +101,7 @@ export class NbChatFormComponent { * Send button icon, shown if `buttonTitle` is empty * @type {string} */ - @Input() buttonIcon: string = 'nb-paper-plane'; + @Input() buttonIcon: string = 'paper-plane-outline'; /** * Show send button diff --git a/src/framework/theme/components/chat/chat-message-file.component.ts b/src/framework/theme/components/chat/chat-message-file.component.ts index 6d8cdd1f67..09ed9e4959 100644 --- a/src/framework/theme/components/chat/chat-message-file.component.ts +++ b/src/framework/theme/components/chat/chat-message-file.component.ts @@ -23,16 +23,16 @@ import { DomSanitizer } from '@angular/platform-browser'; - -
+ +
`, diff --git a/src/framework/theme/components/chat/chat-message-map.component.ts b/src/framework/theme/components/chat/chat-message-map.component.ts index e55ee24e0c..3cf6c75285 100644 --- a/src/framework/theme/components/chat/chat-message-map.component.ts +++ b/src/framework/theme/components/chat/chat-message-map.component.ts @@ -57,7 +57,7 @@ export class NbChatMessageMapComponent { // tslint:disable-next-line url: `https://maps.googleapis.com/maps/api/staticmap?center=${this.latitude},${this.longitude}&zoom=12&size=400x400&key=${this.mapKey}`, type: 'image/png', - icon: 'nb-location', + icon: 'location', }; } diff --git a/src/framework/theme/components/chat/chat.module.ts b/src/framework/theme/components/chat/chat.module.ts index 25e4d9be06..a571bbd51c 100644 --- a/src/framework/theme/components/chat/chat.module.ts +++ b/src/framework/theme/components/chat/chat.module.ts @@ -7,6 +7,7 @@ import { ModuleWithProviders, NgModule } from '@angular/core'; import { NbSharedModule } from '../shared/shared.module'; +import { NbIconModule } from '../icon/icon.module'; import { NbChatComponent } from './chat.component'; import { NbChatMessageComponent } from './chat-message.component'; @@ -30,6 +31,7 @@ const NB_CHAT_COMPONENTS = [ @NgModule({ imports: [ NbSharedModule, + NbIconModule, ], declarations: [ ...NB_CHAT_COMPONENTS, diff --git a/src/framework/theme/components/icon/_icon.component.theme.scss b/src/framework/theme/components/icon/_icon.component.theme.scss new file mode 100644 index 0000000000..7b7e2a3a1a --- /dev/null +++ b/src/framework/theme/components/icon/_icon.component.theme.scss @@ -0,0 +1,31 @@ +/** + * @license + * Copyright Akveo. All Rights Reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + */ + +@mixin nb-icon-theme() { + nb-icon { + font-size: nb-theme(icon-font-size); + line-height: nb-theme(icon-line-height); + width: 1em; + height: 1em; + + &.primary-icon { + color: nb-theme(icon-primary-fg); + } + &.info-icon { + color: nb-theme(icon-info-fg); + } + &.success-icon { + color: nb-theme(icon-success-fg); + } + &.warning-icon { + color: nb-theme(icon-warning-fg); + } + &.danger-icon { + color: nb-theme(icon-danger-fg); + } + } +} + diff --git a/src/framework/theme/components/icon/icon-libraries.ts b/src/framework/theme/components/icon/icon-libraries.ts new file mode 100644 index 0000000000..b1c24a1bfc --- /dev/null +++ b/src/framework/theme/components/icon/icon-libraries.ts @@ -0,0 +1,189 @@ +/** + * @license + * Copyright Akveo. All Rights Reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + */ + +import { Injectable } from '@angular/core'; +import { NbFontIconPackParams, NbIconPack, NbIconPackParams, NbIconPackType, NbIcons } from './icon-pack'; +import { NbFontIcon, NbIcon, NbSvgIcon } from './icon'; + +export class NbIconDefinition { + name: string; + type: string; + pack: string; + icon: NbIcon; +} + +function throwPackNotFoundError(name: string) { + throw Error(`Icon Pack '${name}' is not registered`); +} + +function throwNoDefaultPackError() { + throw Error('Default pack is not registered.'); +} + +function throwIconNotFoundError(name: string, pack: string) { + throw Error(`Icon '${name}' is not registered in pack '${pack}'. Check icon name or consider switching icon pack.`); +} + +function throwWrongPackTypeError(name: string, type: string, desiredType: string) { + throw Error(`Pack '${name}' is not an '${desiredType}' Pack and its type is '${type}'`); +} + +/** + * This service allows to register multiple icon packs to use them later within `` component. + */ +@Injectable({providedIn: 'root'}) +export class NbIconLibraries { + + protected packs: Map = new Map(); + protected defaultPack: NbIconPack; + + /** + * Registers new Svg icon pack + * @param {string} name + * @param {NbIcon} icons + * @param {NbIconPackParams} params + */ + registerSvgPack(name: string, icons: NbIcons, params: NbIconPackParams= {}) { + this.packs.set(name, { + name, + icons: new Map(Object.entries(icons)), + params, + type: NbIconPackType.SVG, + }); + } + + /** + * Registers new font pack + * @param {string} name + * @param {NbIconPackParams} params + */ + registerFontPack(name: string, params: NbFontIconPackParams = {}) { + this.packs.set(name, { + name, + params, + icons: new Map(), + type: NbIconPackType.FONT, + }); + } + + /** + * Returns pack by name + * @param {string} name + */ + getPack(name: string): NbIconPack { + return this.packs.get(name); + } + + /** + * Sets pack as a default + * @param {string} name + */ + setDefaultPack(name: string) { + if (!this.packs.has(name)) { + throwPackNotFoundError(name); + } + + this.defaultPack = this.packs.get(name); + } + + /** + * Returns Svg icon + * @param {string} name + * @param {string} pack + * + * @returns NbIconDefinition + */ + getSvgIcon(name: string, pack?: string): NbIconDefinition { + const iconsPack = pack ? this.getPackOrThrow(pack) : this.getDefaultPackOrThrow(); + + if (iconsPack.type !== NbIconPackType.SVG) { + throwWrongPackTypeError(iconsPack.name, iconsPack.type, 'SVG'); + } + + const icon = this.getIconFromPack(name, iconsPack); + + return { + name, + pack: iconsPack.name, + type: NbIconPackType.SVG, + icon: this.createSvgIcon(name, icon, iconsPack.params), + }; + } + + /** + * Returns Font icon + * @param {string} name + * @param {string} pack + * + * @returns NbIconDefinition + */ + getFontIcon(name: string, pack?: string): NbIconDefinition { + const iconsPack = pack ? this.getPackOrThrow(pack) : this.getDefaultPackOrThrow(); + + if (iconsPack.type !== NbIconPackType.FONT) { + throwWrongPackTypeError(iconsPack.name, iconsPack.type, 'Font'); + } + + const icon = this.getIconFromPack(name, iconsPack, false); + + return { + name, + pack: iconsPack.name, + type: NbIconPackType.FONT, + icon: this.createFontIcon(name, icon ? icon : '', iconsPack.params), + }; + } + + /** + * Returns an icon + * @param {string} name + * @param {string} pack + * + * @returns NbIconDefinition + */ + getIcon(name: string, pack?: string): NbIconDefinition { + const iconsPack = pack ? this.getPackOrThrow(pack) : this.getDefaultPackOrThrow(); + + if (iconsPack.type === NbIconPackType.SVG) { + return this.getSvgIcon(name, pack); + } + + return this.getFontIcon(name, pack); + } + + protected createSvgIcon(name: string, content: NbIcon | string, params: NbIconPackParams): NbSvgIcon { + return content instanceof NbSvgIcon ? content : new NbSvgIcon(name, content, params); + } + + protected createFontIcon(name: string, content: NbIcon | string, params: NbFontIconPackParams): NbFontIcon { + return content instanceof NbFontIcon ? content : new NbFontIcon(name, content, params); + } + + protected getPackOrThrow(name: string): NbIconPack { + + const pack: NbIconPack = this.packs.get(name); + if (!pack) { + throwPackNotFoundError(name); + } + return pack; + } + + protected getDefaultPackOrThrow(): NbIconPack { + + if (!this.defaultPack) { + throwNoDefaultPackError(); + } + return this.defaultPack; + } + + protected getIconFromPack(name: string, pack: NbIconPack, shouldThrow = true): NbIcon | string { + if (shouldThrow && !pack.icons.has(name)) { + throwIconNotFoundError(name, pack.name); + } + + return pack.icons.get(name); + } +} diff --git a/src/framework/theme/components/icon/icon-pack.ts b/src/framework/theme/components/icon/icon-pack.ts new file mode 100644 index 0000000000..fde06381f8 --- /dev/null +++ b/src/framework/theme/components/icon/icon-pack.ts @@ -0,0 +1,26 @@ +import { NbIcon } from './icon'; + +export interface NbIcons { + [key: string]: NbIcon | string; +} + +export enum NbIconPackType { + SVG = 'svg', + FONT = 'font', +} + +export interface NbIconPackParams { + packClass?: string, + [name: string]: any, +} + +export interface NbFontIconPackParams extends NbIconPackParams { + iconClassPrefix?: string, +} + +export interface NbIconPack { + name: string; + type: NbIconPackType; + icons: Map; + params: NbIconPackParams, +} diff --git a/src/framework/theme/components/icon/icon.component.scss b/src/framework/theme/components/icon/icon.component.scss new file mode 100644 index 0000000000..48bb062ea7 --- /dev/null +++ b/src/framework/theme/components/icon/icon.component.scss @@ -0,0 +1,3 @@ +:host { + display: inline-block; +} diff --git a/src/framework/theme/components/icon/icon.component.spec.ts b/src/framework/theme/components/icon/icon.component.spec.ts new file mode 100644 index 0000000000..e6a38b9449 --- /dev/null +++ b/src/framework/theme/components/icon/icon.component.spec.ts @@ -0,0 +1,59 @@ +/** + * @license + * Copyright Akveo. All Rights Reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + */ + +import { Component, ElementRef, Input, ViewChild } from '@angular/core'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { NbIconModule } from './icon.module'; +import { NbIconLibraries } from './icon-libraries'; + + +@Component({ + template: ` + + `, +}) +class IconTestComponent { + @ViewChild('iconEl', { read: ElementRef }) iconElement; + + @Input() icon; +} + +describe('Component: NbIcon', () => { + + let iconTestComponent: IconTestComponent; + let fixture: ComponentFixture; + let iconElement: ElementRef; + let iconsLibrary: NbIconLibraries; + + beforeEach(() => { + + const bed = TestBed.configureTestingModule({ + imports: [ NbIconModule ], + providers: [ NbIconLibraries ], + declarations: [ IconTestComponent ], + }); + + fixture = bed.createComponent(IconTestComponent); + iconsLibrary = bed.get(NbIconLibraries); + + iconsLibrary + .registerSvgPack('svg-pack', { home: '' }, { packClass: 'custom-pack' }); + iconsLibrary.setDefaultPack('svg-pack'); + + iconTestComponent = fixture.componentInstance; + iconElement = iconTestComponent.iconElement; + }); + + it('should render icon', () => { + iconTestComponent.icon = 'home'; + fixture.detectChanges(); + const svg = iconElement.nativeElement.querySelector('svg'); + + expect(iconElement.nativeElement.classList.contains('custom-pack')).toBeTruthy(); + expect(svg.innerHTML).toContain(''); + }); +}); diff --git a/src/framework/theme/components/icon/icon.component.ts b/src/framework/theme/components/icon/icon.component.ts new file mode 100644 index 0000000000..d99c3d38f1 --- /dev/null +++ b/src/framework/theme/components/icon/icon.component.ts @@ -0,0 +1,206 @@ +/** + * @license + * Copyright Akveo. All Rights Reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + */ + +import { + ChangeDetectionStrategy, + Component, + ElementRef, + HostBinding, + Input, + OnChanges, + OnInit, + Renderer2, +} from '@angular/core'; +import { DomSanitizer, SafeHtml } from '@angular/platform-browser'; + +import { NbIconLibraries } from './icon-libraries'; + +/** + * Icon component. Allows to render both `svg` and `font` icons. + * Starting from Nebular 4.0 uses [Eva Icons](https://akveo.github.io/eva-icons/) pack by default. + * + * Basic icon example: + * @stacked-example(Showcase, icon/icon-showcase.component) + * + * Icon configuration: + * + * ```html + * + * ``` + * ### Installation + * + * By default Nebular comes without any pre-installed icon pack. + * Starting with Nebular 4.0.0 we ship separate package called `@nebular/eva-icons` + * which integrates SVG [Eva Icons](https://akveo.github.io/eva-icons/) pack to Nebular. To add it to your + * project run: + * ```sh + * npm i @nebular/eva-icons + * ``` + * This command will install Nebular Eva Icons pack. Then register `NbEvaIconsModule` into your app module or any child + * module you need to have the icons in: + * ```ts + * import { NbEvaIconsModule } form '@nebular/eva-icons'; + * + * @NgModule({ + * imports: [ + * // ... + * NbEvaIconsModule, + * ], + * }) + * export class PageModule { } + * ``` + * Last thing, import `NbIconModule` to your feature module where you need to show an icon: + * ```ts + * @NgModule({ + * imports: [ + * // ... + * NbIconModule, + * ], + * }) + * export class PageModule { } + * ``` + * ### Usage + * + * Icon can be colored using `status` input: + * ```html + * + * ``` + * + * Colored icons: + * @stacked-example(Colored Icons, icon/icon-colors.component) + * + * In case you need to specify an icon from a specific icon pack, this could be done using `pack` input property: + * ```html + * + * ``` + * Additional icon settings (if available by the icon pack) could be passed using `options` input: + * + * ```html + * + * ``` + * + * @styles + * + * icon-font-size: + * icon-width: + * icon-height: + * icon-primary-fg: + * icon-info-fg: + * icon-success-fg: + * icon-warning-fg: + * icon-danger-fg: + */ +@Component({ + selector: 'nb-icon', + styleUrls: [`./icon.component.scss`], + template: '', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class NbIconComponent implements OnChanges, OnInit { + + static readonly STATUS_PRIMARY = 'primary'; + static readonly STATUS_INFO = 'info'; + static readonly STATUS_SUCCESS = 'success'; + static readonly STATUS_WARNING = 'warning'; + static readonly STATUS_DANGER = 'danger'; + + private iconDef; + private prevClasses = []; + + @HostBinding('innerHtml') + html: SafeHtml; + + @HostBinding('class.primary-icon') + get primary() { + return this.status === NbIconComponent.STATUS_PRIMARY; + } + + @HostBinding('class.info-icon') + get info() { + return this.status === NbIconComponent.STATUS_INFO; + } + + @HostBinding('class.success-icon') + get success() { + return this.status === NbIconComponent.STATUS_SUCCESS; + } + + @HostBinding('class.warning-icon') + get warning() { + return this.status === NbIconComponent.STATUS_WARNING; + } + + @HostBinding('class.danger-icon') + get danger() { + return this.status === NbIconComponent.STATUS_DANGER; + } + + /** + * Icon name + * @param {string} status + */ + @Input() icon: string; + + /** + * Icon pack name + * @param {string} status + */ + @Input() pack: string; + + /** + * Additional icon settings + * @param {[name: string]: any} + */ + @Input() options: { [name: string]: any }; + + /** + * Icon status (adds specific styles): + * primary, info, success, warning, danger + * @param {string} status + */ + @Input() status: string; + + constructor( + private sanitizer: DomSanitizer, + private iconLibrary: NbIconLibraries, + private el: ElementRef, + private renderer: Renderer2, + ) {} + + ngOnInit() { + this.iconDef = this.renderIcon(this.icon, this.pack, this.options); + } + + ngOnChanges() { + if (this.iconDef) { + this.iconDef = this.renderIcon(this.icon, this.pack, this.options); + } + } + + renderIcon(name: string, pack?: string, options?: { [name: string]: any }) { + const iconDefinition = this.iconLibrary.getIcon(name, pack); + + const content = iconDefinition.icon.getContent(options); + if (content) { + this.html = this.sanitizer.bypassSecurityTrustHtml(content); + } + + this.assignClasses(iconDefinition.icon.getClasses(options)); + return iconDefinition; + } + + protected assignClasses(classes: string[]) { + this.prevClasses.forEach((className: string) => { + this.renderer.removeClass(this.el.nativeElement, className); + }); + + classes.forEach((className: string) => { + this.renderer.addClass(this.el.nativeElement, className); + }); + + this.prevClasses = classes; + } +} diff --git a/src/framework/theme/components/icon/icon.module.ts b/src/framework/theme/components/icon/icon.module.ts new file mode 100644 index 0000000000..781672e992 --- /dev/null +++ b/src/framework/theme/components/icon/icon.module.ts @@ -0,0 +1,56 @@ +/** + * @license + * Copyright Akveo. All Rights Reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + */ + +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; + +import { NbIconComponent } from './icon.component'; +import { NbIconLibraries } from './icon-libraries'; + +@NgModule({ + imports: [ + CommonModule, + ], + declarations: [ + NbIconComponent, + ], + exports: [ + NbIconComponent, + ], +}) +export class NbIconModule { + + private essentialsPackName = 'nebular-essentials'; + + constructor(private iconsLibrary: NbIconLibraries) { + + // in case of consequent calls we don't need to enable `nebular-essentials` pack again + if (this.iconsLibrary.getPack(this.essentialsPackName)) { + return; + } + + // tslint:disable:max-line-length + this.iconsLibrary.registerSvgPack(this.essentialsPackName, { + 'chevron-down-outline': '', + 'chevron-up-outline': '', + 'chevron-left-outline': '', + 'chevron-right-outline': '', + 'checkmark-outline': '', + 'paper-plane-outline': '', + 'file-text-outline': '', + 'alert-triangle-outline': '', + 'question-mark-outline': '', + 'email-outline': '', + 'flash-outline': '', + 'search-outline': '', + 'close-outline': '', + 'collapse-outline': '', + 'expand-outline': '', + 'minus-outline': '', + }); + // tslint:enable:max-line-length + } +} diff --git a/src/framework/theme/components/icon/icon.spec.ts b/src/framework/theme/components/icon/icon.spec.ts new file mode 100644 index 0000000000..12e1424ee4 --- /dev/null +++ b/src/framework/theme/components/icon/icon.spec.ts @@ -0,0 +1,75 @@ +/** + * @license + * Copyright Akveo. All Rights Reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + */ + +import { NbFontIcon, NbSvgIcon } from './icon'; + + +describe('icon', () => { + let fontIcon: NbFontIcon; + let svgIcon: NbSvgIcon; + + + it(`font icon renders`, () => { + + fontIcon = new NbFontIcon('home', 'custom', { + packClass: 'custom-pack', + iconClassPrefix: 'cp', + }); + + expect(fontIcon.getContent()).toEqual('custom'); + }); + + it(`font icon getClasses return classes`, () => { + + fontIcon = new NbFontIcon('home', '', { + packClass: 'custom-pack', + }); + + expect(fontIcon.getClasses()).toEqual(['custom-pack', 'home']); + }); + + it(`font icon getClasses return class with prefix`, () => { + + fontIcon = new NbFontIcon('home', '', { + packClass: 'custom-pack', + iconClassPrefix: 'cp', + }); + + expect(fontIcon.getClasses()).toEqual(['custom-pack', 'cp-home']); + }); + + it(`font icon getClasses return class with name only`, () => { + + fontIcon = new NbFontIcon('home', ''); + + expect(fontIcon.getClasses()).toEqual(['home']); + }); + + it(`svg icon renders`, () => { + + svgIcon = new NbSvgIcon('home', 'content', { + packClass: 'custom-pack', + }); + + expect(svgIcon.getContent()).toEqual('content'); + }); + + it(`svg icon getClasses return class`, () => { + + svgIcon = new NbSvgIcon('home', '', { + packClass: 'custom-pack', + }); + + expect(svgIcon.getClasses()).toEqual(['custom-pack']); + }); + + it(`svg icon getClasses return class without name`, () => { + + svgIcon = new NbSvgIcon('home', ''); + + expect(svgIcon.getClasses()).toEqual([]); + }); +}); diff --git a/src/framework/theme/components/icon/icon.ts b/src/framework/theme/components/icon/icon.ts new file mode 100644 index 0000000000..a598f50c31 --- /dev/null +++ b/src/framework/theme/components/icon/icon.ts @@ -0,0 +1,49 @@ +import { NbFontIconPackParams, NbIconPackParams } from './icon-pack'; + +export interface NbIconOptions { + [name: string]: any; +} + +export interface NbIcon { + getClasses(options?: NbIconOptions): string[]; + getContent(options?: NbIconOptions): string; +} + +export class NbFontIcon implements NbIcon { + + constructor(protected name, protected content: any, protected params: NbFontIconPackParams = {}) {} + + getClasses(options?: NbIconOptions): string[] { + const classes = []; + + if (this.params.packClass) { + classes.push(this.params.packClass); + } + + const name = this.params.iconClassPrefix ? `${this.params.iconClassPrefix}-${this.name}` : this.name; + classes.push(name); + return classes; + } + + getContent(options?: NbIconOptions): string { + return this.content; + } +} + +export class NbSvgIcon implements NbIcon { + + constructor(protected name, protected content: any, protected params: NbIconPackParams = {}) {} + + getClasses(options?: NbIconOptions): string[] { + const classes = []; + + if (this.params.packClass) { + classes.push(this.params.packClass); + } + return classes; + } + + getContent(options?: NbIconOptions): string { + return this.content; + } +} diff --git a/src/framework/theme/components/icon/icons-libraries.spec.ts b/src/framework/theme/components/icon/icons-libraries.spec.ts new file mode 100644 index 0000000000..bdba40e237 --- /dev/null +++ b/src/framework/theme/components/icon/icons-libraries.spec.ts @@ -0,0 +1,153 @@ +import { TestBed } from '@angular/core/testing'; + +import { NbIconLibraries } from './icon-libraries'; +import { NbSvgIcon } from './icon'; + + +describe('icons-library', () => { + let iconsLibrary: NbIconLibraries; + + beforeEach(() => { + TestBed.resetTestingModule(); + const bed = TestBed.configureTestingModule({ + providers: [ + NbIconLibraries, + ], + }); + iconsLibrary = bed.get(NbIconLibraries); + }); + + it('should register raw svg icon', () => { + + iconsLibrary.registerSvgPack('super-pack', { home: '', gear: '' }); + iconsLibrary.setDefaultPack('super-pack'); + + const icon = iconsLibrary.getSvgIcon('home'); + + expect(icon.icon.getContent()).toEqual(''); + expect(icon.name).toEqual('home'); + expect(icon.pack).toEqual('super-pack'); + expect(icon.type).toEqual('svg'); + }); + + it('should register NbSvgIcon svg icon', () => { + + iconsLibrary.registerSvgPack('super-pack', { + home: new NbSvgIcon('home', '', { packClass: 'sp' }), + }); + iconsLibrary.setDefaultPack('super-pack'); + + const icon = iconsLibrary.getSvgIcon('home'); + + expect(icon.icon.getContent()).toEqual(''); + expect(icon.icon.getClasses()).toEqual(['sp']); + expect(icon.name).toEqual('home'); + expect(icon.pack).toEqual('super-pack'); + expect(icon.type).toEqual('svg'); + }); + + it('should register custom svg icon', () => { + + class CustomSvgIcon extends NbSvgIcon { + getContent() { + return 'custom'; + } + } + + iconsLibrary.registerSvgPack('super-pack', { + home: new CustomSvgIcon('home', '', { packClass: 'sp' }), + }); + iconsLibrary.setDefaultPack('super-pack'); + + const icon = iconsLibrary.getSvgIcon('home'); + + expect(icon.icon.getContent()).toEqual('custom'); + expect(icon.icon.getClasses()).toEqual(['sp']); + expect(icon.name).toEqual('home'); + expect(icon.pack).toEqual('super-pack'); + expect(icon.type).toEqual('svg'); + }); + + it('should throw for unknown svg icon', () => { + + iconsLibrary.registerSvgPack('super-pack', { home: '', gear: '' }); + iconsLibrary.setDefaultPack('super-pack'); + + + expect(() => iconsLibrary.getSvgIcon('unknown')) + // tslint:disable-next-line:max-line-length + .toThrowError(`Icon 'unknown' is not registered in pack 'super-pack'. Check icon name or consider switching icon pack.`); + }); + + it('should throw for no default pack', () => { + expect(() => iconsLibrary.getSvgIcon('unknown')).toThrowError('Default pack is not registered.'); + }); + + it('should throw for wrong pack type', () => { + + iconsLibrary.registerSvgPack('super-pack', { home: '', gear: '' }); + iconsLibrary.registerFontPack('font-pack'); + iconsLibrary.setDefaultPack('font-pack'); + + + expect(() => iconsLibrary.getSvgIcon('unknown')) + .toThrowError(`Pack 'font-pack' is not an 'SVG' Pack and its type is 'font'`); + }); + + it('should throw for wrong pack', () => { + + iconsLibrary.registerSvgPack('super-pack', { home: '', gear: '' }); + iconsLibrary.registerFontPack('font-pack'); + iconsLibrary.setDefaultPack('super-pack'); + + expect(() => iconsLibrary.getSvgIcon('unknown')) + // tslint:disable-next-line:max-line-length + .toThrowError(`Icon 'unknown' is not registered in pack 'super-pack'. Check icon name or consider switching icon pack.`); + }); + + it('should throw for wrong pack when setting default', () => { + + iconsLibrary.registerFontPack('font-pack'); + + expect(() => iconsLibrary.setDefaultPack('super-pack')) + .toThrowError(`Icon Pack 'super-pack' is not registered`); + }); + + it('should register font icon', () => { + + iconsLibrary.registerFontPack('font-pack', { packClass: 'font', iconClassPrefix: 'fp' }); + iconsLibrary.setDefaultPack('font-pack'); + + const icon = iconsLibrary.getFontIcon('home'); + + expect(icon.icon.getContent()).toEqual(''); + expect(icon.icon.getClasses()).toEqual(['font', 'fp-home']); + expect(icon.name).toEqual('home'); + expect(icon.pack).toEqual('font-pack'); + expect(icon.type).toEqual('font'); + }); + + + it('should return icon', () => { + + iconsLibrary.registerSvgPack('super-pack', { home: '', gear: '' }); + iconsLibrary.registerFontPack('font-pack', { packClass: 'font', iconClassPrefix: 'fp' }); + iconsLibrary.setDefaultPack('font-pack'); + + const icon = iconsLibrary.getIcon('home'); + const svgIcon = iconsLibrary.getIcon('home', 'super-pack'); + + expect(icon.icon.getContent()).toEqual(''); + expect(icon.icon.getClasses()).toEqual(['font', 'fp-home']); + expect(icon.icon.getClasses()).toEqual(['font', 'fp-home']); + expect(icon.name).toEqual('home'); + expect(icon.pack).toEqual('font-pack'); + expect(icon.type).toEqual('font'); + + expect(svgIcon.icon.getContent()).toEqual(''); + expect(svgIcon.name).toEqual('home'); + expect(svgIcon.pack).toEqual('super-pack'); + expect(svgIcon.type).toEqual('svg'); + }); + +}); diff --git a/src/framework/theme/components/input/input.spec.ts b/src/framework/theme/components/input/input.spec.ts index 7c373cc584..68e15d6f18 100644 --- a/src/framework/theme/components/input/input.spec.ts +++ b/src/framework/theme/components/input/input.spec.ts @@ -44,7 +44,7 @@ describe('Directive: NbInput', () => { let inputElement: Element; let textareaElement: Element; - beforeEach(() => {; + beforeEach(() => { fixture = TestBed.configureTestingModule({ imports: [ NbInputModule ], diff --git a/src/framework/theme/components/menu/menu-item.component.html b/src/framework/theme/components/menu/menu-item.component.html index c3bb5f4081..4918e43bdf 100644 --- a/src/framework/theme/components/menu/menu-item.component.html +++ b/src/framework/theme/components/menu/menu-item.component.html @@ -1,5 +1,5 @@ - + {{ menuItem.title }} - + {{ menuItem.title }} - + {{ menuItem.title }} - + {{ menuItem.title }} - + {{ menuItem.title }} - + +
    some-icon' }); + iconLibs.setDefaultPack('test') } function createSingleMenuComponent(menuItems, menuTag = 'menu') { @@ -97,9 +102,9 @@ describe('NbMenuItem', () => { }); it('should set icon to menu item', () => { - const { fixture } = createSingleMenuComponent([{ title: 'Home', icon: 'test-icon' }]); + const { fixture } = createSingleMenuComponent([{ title: 'Home', icon: 'some-icon' }]); const iconWrapper = fixture.nativeElement.querySelector('.menu-icon'); - expect(iconWrapper.classList).toContain('test-icon'); + expect(iconWrapper.textContent).toContain('some-icon'); }); it('should set title to menu item', () => { diff --git a/src/framework/theme/components/route-tabset/route-tabset.component.scss b/src/framework/theme/components/route-tabset/route-tabset.component.scss index fb30b44a16..ce53f16ebd 100644 --- a/src/framework/theme/components/route-tabset/route-tabset.component.scss +++ b/src/framework/theme/components/route-tabset/route-tabset.component.scss @@ -36,12 +36,12 @@ left: 0; } - i { + nb-icon { font-size: 1.5rem; vertical-align: middle; } - i + span { + nb-icon + span { @include nb-ltr(margin-left, 0.5rem); @include nb-rtl(margin-right, 0.5rem); } diff --git a/src/framework/theme/components/route-tabset/route-tabset.component.ts b/src/framework/theme/components/route-tabset/route-tabset.component.ts index 270cddd9a3..7a8ee62e73 100644 --- a/src/framework/theme/components/route-tabset/route-tabset.component.ts +++ b/src/framework/theme/components/route-tabset/route-tabset.component.ts @@ -16,7 +16,7 @@ import { convertToBoolProperty } from '../helpers'; * { * title: 'Route tab #1', * route: '/pages/description', - * icon: 'nb-home', + * icon: 'home', * responsive: true, // hide title before `route-tabs-icon-only-max-width` value * }, * { @@ -68,7 +68,7 @@ import { convertToBoolProperty } from '../helpers'; class="route-tab disabled" tabindex="-1"> - + {{ tab.title }} @@ -82,7 +82,7 @@ import { convertToBoolProperty } from '../helpers'; tabindex="0" class="route-tab"> - + {{ tab.title }} diff --git a/src/framework/theme/components/route-tabset/route-tabset.module.ts b/src/framework/theme/components/route-tabset/route-tabset.module.ts index a0f96e883f..47c5b54d3d 100644 --- a/src/framework/theme/components/route-tabset/route-tabset.module.ts +++ b/src/framework/theme/components/route-tabset/route-tabset.module.ts @@ -9,10 +9,12 @@ import { NgModule } from '@angular/core'; import { NbSharedModule } from '../shared/shared.module'; import { NbRouteTabsetComponent } from './route-tabset.component'; +import { NbIconModule } from '../icon/icon.module'; @NgModule({ imports: [ NbSharedModule, + NbIconModule, ], declarations: [ NbRouteTabsetComponent, diff --git a/src/framework/theme/components/search/search.component.ts b/src/framework/theme/components/search/search.component.ts index 6cfe819763..22b30ad159 100644 --- a/src/framework/theme/components/search/search.component.ts +++ b/src/framework/theme/components/search/search.component.ts @@ -48,7 +48,7 @@ import { NbOverlayService, NbOverlayRef, NbPortalDirective } from '../cdk'; template: `