diff --git a/packages/icons-angular/examples/app/app-interceptor.module.ts b/packages/icons-angular/examples/app/app-interceptor.ts similarity index 100% rename from packages/icons-angular/examples/app/app-interceptor.module.ts rename to packages/icons-angular/examples/app/app-interceptor.ts diff --git a/packages/icons-angular/examples/app/app.component.html b/packages/icons-angular/examples/app/app.component.html index 2456f36a3..8e46fa450 100644 --- a/packages/icons-angular/examples/app/app.component.html +++ b/packages/icons-angular/examples/app/app.component.html @@ -11,14 +11,14 @@

Ant Design Icons

- +@for (name of icons; track name) { - +} - +@if (currentTheme === 'fill') {

With Namespace

-
+} diff --git a/packages/icons-angular/examples/app/app.component.ts b/packages/icons-angular/examples/app/app.component.ts index 7183375d0..bc985e146 100644 --- a/packages/icons-angular/examples/app/app.component.ts +++ b/packages/icons-angular/examples/app/app.component.ts @@ -1,21 +1,12 @@ +import { FormsModule } from '@angular/forms'; import { Component, OnInit } from '@angular/core'; -import { manifest, IconService, Manifest, ThemeType } from '@ant-design/icons-angular'; - -// const pandaLiteral = ` -// -// -// -// -// -// -// -// -// `; +import { manifest, IconService, Manifest, ThemeType, IconDirective } from '@ant-design/icons-angular'; @Component({ - selector : 'app-root', + selector: 'app-root', + imports: [FormsModule, IconDirective], templateUrl: './app.component.html', - styleUrls : [ './app.component.css' ] + styleUrl: './app.component.css' }) export class AppComponent implements OnInit { currentTheme: ThemeType = 'fill'; diff --git a/packages/icons-angular/examples/app/app.config.ts b/packages/icons-angular/examples/app/app.config.ts new file mode 100644 index 000000000..9e9a2158f --- /dev/null +++ b/packages/icons-angular/examples/app/app.config.ts @@ -0,0 +1,17 @@ +import { HTTP_INTERCEPTORS, provideHttpClient, withInterceptorsFromDi } from '@angular/common/http'; +import { ApplicationConfig } from '@angular/core'; + +import { AppInterceptor } from './app-interceptor'; +import { provideAntIcons } from '@ant-design/icons-angular'; + +export const appConfig: ApplicationConfig = { + providers: [ + { + provide: HTTP_INTERCEPTORS, + useClass: AppInterceptor, + multi: true + }, + provideAntIcons([]), + provideHttpClient(withInterceptorsFromDi()) + ] +}; diff --git a/packages/icons-angular/examples/app/app.module.ts b/packages/icons-angular/examples/app/app.module.ts deleted file mode 100644 index 485d70f7a..000000000 --- a/packages/icons-angular/examples/app/app.module.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { BrowserModule } from '@angular/platform-browser'; -import { CommonModule } from '@angular/common'; -import { HTTP_INTERCEPTORS, provideHttpClient, withInterceptorsFromDi } from '@angular/common/http'; -import { NgModule } from '@angular/core'; -import { FormsModule } from '@angular/forms'; - -import { AppInterceptor } from './app-interceptor.module'; -import { IconModule } from '@ant-design/icons-angular'; -import { AppComponent } from './app.component'; - -@NgModule({ declarations: [ - AppComponent - ], - bootstrap: [AppComponent], imports: [CommonModule, - FormsModule, - BrowserModule, - IconModule], providers: [ - { - provide: HTTP_INTERCEPTORS, - useClass: AppInterceptor, - multi: true - }, - provideHttpClient(withInterceptorsFromDi()), - ] }) -export class AppModule { } diff --git a/packages/icons-angular/examples/main.ts b/packages/icons-angular/examples/main.ts index 91ec6da5f..cb790b31c 100644 --- a/packages/icons-angular/examples/main.ts +++ b/packages/icons-angular/examples/main.ts @@ -1,12 +1,12 @@ import { enableProdMode } from '@angular/core'; -import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; +import { bootstrapApplication } from '@angular/platform-browser'; -import { AppModule } from './app/app.module'; +import { AppComponent } from './app/app.component'; +import { appConfig } from './app/app.config'; import { environment } from './environments/environment'; if (environment.production) { enableProdMode(); } -platformBrowserDynamic().bootstrapModule(AppModule) - .catch(err => console.log(err)); +bootstrapApplication(AppComponent, appConfig).catch(err => console.log(err)); diff --git a/packages/icons-angular/package.json b/packages/icons-angular/package.json index 2c3aea259..6a8a46e70 100644 --- a/packages/icons-angular/package.json +++ b/packages/icons-angular/package.json @@ -2,7 +2,6 @@ "name": "@ant-design/icons-angular-workspace", "version": "0.0.0-NOT-VALID", "scripts": { - "ng": "ng", "start": "ng serve", "lint": "ng lint", "test": "ng test --no-watch --no-progress --browsers=ChromeHeadlessCI --code-coverage", @@ -16,35 +15,35 @@ "license": "MIT", "private": true, "dependencies": { - "@angular/common": "^18.0.0", - "@angular/core": "^18.0.0", + "@angular/common": "^19.0.0", + "@angular/core": "^19.0.0", "@ant-design/colors": "^7.0.0" }, "devDependencies": { - "@angular-devkit/build-angular": "^18.0.1", - "@angular-eslint/builder": "^18.0.0-alpha.5", - "@angular-eslint/eslint-plugin": "^18.0.0-alpha.5", - "@angular-eslint/eslint-plugin-template": "^18.0.0-alpha.5", - "@angular-eslint/schematics": "^18.0.0-alpha.5", - "@angular-eslint/template-parser": "^18.0.0-alpha.5", - "@angular/animations": "^18.0.0", - "@angular/cli": "^18.0.1", - "@angular/compiler": "^18.0.0", - "@angular/compiler-cli": "^18.0.0", - "@angular/forms": "^18.0.0", - "@angular/language-service": "^18.0.0", - "@angular/platform-browser": "^18.0.0", - "@angular/platform-browser-dynamic": "^18.0.0", - "@angular/router": "^18.0.0", + "@angular-devkit/build-angular": "^19.0.0", + "@angular-eslint/builder": "^19.0.0-alpha.0", + "@angular-eslint/eslint-plugin": "^19.0.0-alpha.0", + "@angular-eslint/eslint-plugin-template": "^19.0.0-alpha.0", + "@angular-eslint/schematics": "^19.0.0-alpha.0", + "@angular-eslint/template-parser": "^19.0.0-alpha.0", + "@angular/animations": "^19.0.0", + "@angular/cli": "^19.0.0", + "@angular/compiler": "^19.0.0", + "@angular/compiler-cli": "^19.0.0", + "@angular/forms": "^19.0.0", + "@angular/language-service": "^19.0.0", + "@angular/platform-browser": "^19.0.0", + "@angular/platform-browser-dynamic": "^19.0.0", + "@angular/router": "^19.0.0", "@ant-design/icons-svg": "^4.0.0", "@types/jasmine": "~3.6.0", "@types/jasminewd2": "~2.0.3", "@types/lodash": "^4.14.136", - "@types/node": "^13.9.3", + "@types/node": "^20.11.1", "@typescript-eslint/eslint-plugin": "^6.10.0", "@typescript-eslint/parser": "^6.10.0", "classlist": "^2.0.0", - "cross-env": "^5.2.0", + "cross-env": "^7.0.3", "eslint": "^8.53.0", "fs-extra": "^7.0.0", "globby": "^8.0.1", @@ -57,11 +56,11 @@ "karma-jasmine-html-reporter": "^2.0.0", "karma-spec-reporter": "^0.0.36", "lodash": "^4.17.11", - "ng-packagr": "^18.0.0", + "ng-packagr": "^19.0.0", "prettier": "^2.8.8", "rxjs": "^7.8.0", "ts-node": "~10.9.0", - "typescript": "~5.4.5", - "zone.js": "~0.14.2" + "typescript": "~5.6.3", + "zone.js": "~0.15.0" } -} \ No newline at end of file +} diff --git a/packages/icons-angular/src/README.md b/packages/icons-angular/src/README.md index 11bdd02b7..bc226ed3e 100644 --- a/packages/icons-angular/src/README.md +++ b/packages/icons-angular/src/README.md @@ -21,17 +21,15 @@ ng add @ant-design/icons-angular ## Usage -You should import `IconModule` in your application's root module. +You should import `IconDirective` in your component. ```ts -import { IconModule } from '@ant-design/icons-angular'; +import { IconDirective } from '@ant-design/icons-angular'; -@NgModule({ - imports: [ - IconModule - ] +@Component({ + imports: [ IconDirective ] }) -export class AppModule { } +export class AppComponent {} ``` And register the icons that you need to `IconService` (all or explicitly, we call it **static loading**): @@ -39,23 +37,22 @@ And register the icons that you need to `IconService` (all or explicitly, we cal > ATTENTION! We strongly suggest you not to register all icons. That would increase your bundle's size dramatically. ```ts -import { Component, OnInit } from '@angular/core'; -import { IconDefinition, IconService } from '@ant-design/icons-angular'; +import { Component } from '@angular/core'; +import { IconService } from '@ant-design/icons-angular'; import { AccountBookFill } from '@ant-design/icons-angular/icons' // import * as AllIcons from 'ant-icons-angular/icons'; @Component({ - selector : 'app-root', - templateUrl: './app.component.html', - styleUrls : ['./app.component.css'] + // ... }) -export class AppComponent implements OnInit { +export class AppComponent { constructor(private _iconService: IconService) { // Import all. NOT RECOMMENDED. ❌ // const antDesignIcons = AllIcons as { // [key: string]: IconDefinition; // }; // this._iconService.addIcon(...Object.keys(antDesignIcons).map(key => antDesignIcons[key])); + // Import what you need! ✔️ this._iconService.addIcon(...[ AccountBookFill ]); this._iconService.twoToneColor = { primaryColor: '#1890ff' }; @@ -63,6 +60,20 @@ export class AppComponent implements OnInit { } ``` +You can also provide icons you need in the `app.config.ts`: + +```ts +import { ApplicationConfig } from '@angular/core'; +import { provideAntIcons } from '@ant-design/icons-angular'; +import { AccountBookFill } from '@ant-design/icons-angular/icons'; + +export const appConfig: ApplicationConfig = { + providers: [ + provideAntIcons([ AccountBookFill ]) + ] +}; +``` + When you want to render an icon: ```html @@ -75,7 +86,7 @@ For icons provided by Ant Design, we provide **dynamic loading** strategy to red ### Namespace -Namespace is a new feature first introduced in `2.0.0-beta.2`. It allows users to register their own icons with simple API, support both dynamic loading and static loading. +Namespace is first introduced in `2.0.0-beta.2`. It allows users to register their own icons with simple API, support both dynamic loading and static loading. Say you want to add a `panda` icon in `animal` namespace. For static loading, you should call `addIconLiteral('animal:panda', '...')`. For dynamic loading, just put `panda.svg` under `assets/animal`. And render a panda like: ``. @@ -99,4 +110,6 @@ Run `npm run build:lib`. ### Extension -You can simply extend this package by creating directives or services that extends `IconDirective` and `IconService`. And it is worth mentioning that `_changeIcon` method returns a `Promise` using which you could add extra modifications. [ng-zorro-antd](https://github.com/NG-ZORRO/ng-zorro-antd/tree/master/components/icon) is a good example of extending this package. +You can simply extend this package by creating directives or services that extends `IconDirective` and `IconService`. And it is worth mentioning that `_changeIcon` method returns a `Promise` using which you could add extra modifications. + +[ng-zorro-antd](https://github.com/NG-ZORRO/ng-zorro-antd/tree/master/components/icon) is a good example of extending this package. diff --git a/packages/icons-angular/src/component/icon.directive.ts b/packages/icons-angular/src/component/icon.directive.ts index 765fb0768..c86dd7b83 100644 --- a/packages/icons-angular/src/component/icon.directive.ts +++ b/packages/icons-angular/src/component/icon.directive.ts @@ -1,6 +1,7 @@ import { Directive, ElementRef, + inject, Input, OnChanges, Renderer2, @@ -25,11 +26,12 @@ function checkMeta(prev: RenderMeta, after: RenderMeta): boolean { }) export class IconDirective implements OnChanges { @Input() type: string | IconDefinition; - @Input() theme?: ThemeType; @Input() twoToneColor?: string; - constructor(protected _iconService: IconService, protected _elementRef: ElementRef, protected _renderer: Renderer2) {} + protected _elementRef = inject(ElementRef); + protected _renderer = inject(Renderer2); + constructor(protected _iconService: IconService) {} ngOnChanges(changes: SimpleChanges): void { if (changes.type || changes.theme || changes.twoToneColor) { diff --git a/packages/icons-angular/src/component/icon.module.ts b/packages/icons-angular/src/component/icon.module.ts index f01f41b36..042d33676 100644 --- a/packages/icons-angular/src/component/icon.module.ts +++ b/packages/icons-angular/src/component/icon.module.ts @@ -1,10 +1,12 @@ import { NgModule } from '@angular/core'; import { IconDirective } from './icon.directive'; -import { IconService } from './icon.service'; + +/** + * @deprecated Please use `IconDirective` instead, will be removed in v20. + */ @NgModule({ - exports: [IconDirective], - declarations: [IconDirective], - providers: [IconService] + imports: [IconDirective], + exports: [IconDirective] }) -export class IconModule { } +export class IconModule {} diff --git a/packages/icons-angular/src/component/icon.provider.ts b/packages/icons-angular/src/component/icon.provider.ts new file mode 100644 index 000000000..d05c53a92 --- /dev/null +++ b/packages/icons-angular/src/component/icon.provider.ts @@ -0,0 +1,17 @@ +import { EnvironmentProviders, makeEnvironmentProviders } from "@angular/core"; +import { ANT_ICONS } from "./icon.service"; +import { IconDefinition } from "../types"; + +/** + * Provide icon definitions in root + * + * @param icons Icon definitions + */ +export function provideAntIcons(icons: IconDefinition[]): EnvironmentProviders { + return makeEnvironmentProviders([ + { + provide: ANT_ICONS, + useValue: icons + } + ]); +} diff --git a/packages/icons-angular/src/component/icon.service.ts b/packages/icons-angular/src/component/icon.service.ts index 3b60321fc..4fafaa88a 100644 --- a/packages/icons-angular/src/component/icon.service.ts +++ b/packages/icons-angular/src/component/icon.service.ts @@ -44,7 +44,9 @@ const JSONP_HANDLER_NAME = '__ant_icon_load'; export const ANT_ICONS = new InjectionToken('ant_icons'); -@Injectable() +@Injectable({ + providedIn: 'root' +}) export class IconService { defaultTheme: ThemeType = 'outline'; diff --git a/packages/icons-angular/src/component/icon.spec.ts b/packages/icons-angular/src/component/icon.spec.ts index 4635edd6d..d533d250b 100644 --- a/packages/icons-angular/src/component/icon.spec.ts +++ b/packages/icons-angular/src/component/icon.spec.ts @@ -1,10 +1,9 @@ import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http'; -import { Component } from '@angular/core'; -import { fakeAsync, flush, inject, TestBed } from '@angular/core/testing'; +import { Component, DebugElement } from '@angular/core'; +import { ComponentFixture, fakeAsync, flush, inject, TestBed } from '@angular/core/testing'; import { By } from '@angular/platform-browser'; import { AccountBookFill, AccountBookOutline, AccountBookTwoTone } from '../icons/public_api'; import { IconDirective } from './icon.directive'; -import { IconModule } from './icon.module'; import { IconService } from './icon.service'; import { ThemeType } from '@ant-design/icons-angular' @@ -25,18 +24,11 @@ const pandaLiteral = ` describe('@ant-design/icons-angular', () => { let iconService: IconService; - let testComponent; - let fixture; - let icons; + let testComponent: IconTestComponent; + let fixture: ComponentFixture; + let icons: DebugElement[]; describe('static loading', () => { - beforeEach(() => { - TestBed.configureTestingModule({ - imports : [ IconModule ], - declarations: [ IconTestComponent ] - }); - }); - beforeEach(inject([ IconService ], (is: IconService) => { iconService = is; iconService.addIcon(...staticImportIcons); @@ -136,10 +128,8 @@ describe('@ant-design/icons-angular', () => { describe('dynamic loading', () => { beforeEach(() => { TestBed.configureTestingModule({ - declarations: [IconTestComponent], - imports: [IconModule], - providers: [provideHttpClient(withInterceptorsFromDi())] -}); + providers: [provideHttpClient(withInterceptorsFromDi())] + }); }); beforeEach(inject([ IconService ], (is: IconService) => { @@ -194,9 +184,8 @@ describe('@ant-design/icons-angular', () => { @Component({ selector: 'icon-test', - template: ` - - ` + imports: [IconDirective], + template: `` }) export class IconTestComponent { type = 'account-book'; diff --git a/packages/icons-angular/src/package.json b/packages/icons-angular/src/package.json index 56b5bb5b6..98aad4764 100644 --- a/packages/icons-angular/src/package.json +++ b/packages/icons-angular/src/package.json @@ -1,22 +1,23 @@ { "name": "@ant-design/icons-angular", - "version": "18.0.0", + "version": "19.0.0", "license": "MIT", "repository": "https://github.com/ant-design/ant-design-icons/tree/master/packages/icons-angular", "contributors": [ "Wendell Hu ", "Jørn Are Hatlelid ", "MunMunMiao ", - "Nicoss54 " + "Nicoss54 ", + "Laffery " ], "dependencies": { "@ant-design/colors": "^7.0.0", "tslib": "^2.0.0" }, "peerDependencies": { - "@angular/common": "^18.0.0", - "@angular/core": "^18.0.0", - "@angular/platform-browser": "^18.0.0", - "rxjs": "^6.4.0 || ^7.4.0" + "@angular/common": "^19.0.0", + "@angular/core": "^19.0.0", + "@angular/platform-browser": "^19.0.0", + "rxjs": "^6.5.3 || ^7.4.0" } } diff --git a/packages/icons-angular/src/public_api.ts b/packages/icons-angular/src/public_api.ts index e5ae18ef1..77b9677e1 100644 --- a/packages/icons-angular/src/public_api.ts +++ b/packages/icons-angular/src/public_api.ts @@ -2,6 +2,7 @@ export * from './component/icon.module'; export * from './component/icon.service'; export * from './component/icon.directive'; export * from './component/icon.error'; +export * from './component/icon.provider'; export * from './types'; export * from './utils'; export * from './manifest'; diff --git a/packages/icons-angular/src/tests/index.spec.ts b/packages/icons-angular/src/tests/index.spec.ts index 2f3899cd6..23d3d98e1 100644 --- a/packages/icons-angular/src/tests/index.spec.ts +++ b/packages/icons-angular/src/tests/index.spec.ts @@ -1,46 +1,31 @@ -import { CommonModule } from '@angular/common'; -import { - Component, - DebugElement, - NgModule, - NO_ERRORS_SCHEMA, - Type -} from '@angular/core'; -import { - ComponentFixture, - TestBed, - TestBedStatic -} from '@angular/core/testing'; +import { Component, DebugElement } from '@angular/core'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; import { By } from '@angular/platform-browser'; -import { ANT_ICONS, IconDirective, IconModule, ThemeType } from '@ant-design/icons-angular'; +import { IconDirective, ThemeType } from '@ant-design/icons-angular'; import { DownSquareFill, DownSquareOutline, DownSquareTwoTone } from '@ant-design/icons-angular/icons'; -import { PandaIcon, pandaIconStr } from './panda'; +import { PandaIcon } from './panda'; +import { provideAntIcons } from '../component/icon.provider'; const DOWN_SQUARE_FILL_ICON = '' const DOWN_SQUARE_OUTLINE_ICON = ''; const DOWN_SQUARE_TWOTONE_ICON = ''; describe('icons-angular', () => { - describe('basics', () => { - let testBed: ComponentBed; let testComponent: TestIconComponent; let fixture: ComponentFixture; let icon: DebugElement; beforeEach(() => { - testBed = createComponentBed(TestIconComponent, { - imports: [IconModule], - providers: [ - { - provide: ANT_ICONS, - useValue: [DownSquareFill, DownSquareOutline, DownSquareTwoTone] - } - ] + TestBed.configureTestingModule({ + providers: [provideAntIcons([DownSquareFill, DownSquareOutline, DownSquareTwoTone])] }); - fixture = testBed.fixture; - testComponent = testBed.component; + }); + + beforeEach(() => { + fixture = TestBed.createComponent(TestIconComponent); + testComponent = fixture.componentInstance; icon = fixture.debugElement.query(By.directive(IconDirective)); }); @@ -64,23 +49,19 @@ describe('icons-angular', () => { }); describe('namespace', () => { - let testBed: ComponentBed; let testComponent: TestIconNamespaceComponent; let fixture: ComponentFixture; let icon: DebugElement; beforeEach(() => { - testBed = createComponentBed(TestIconNamespaceComponent, { - imports: [IconModule], - providers: [ - { - provide: ANT_ICONS, - useValue: [PandaIcon] - } - ] + TestBed.configureTestingModule({ + providers: [provideAntIcons([PandaIcon])] }); - fixture = testBed.fixture; - testComponent = testBed.component; + }); + + beforeEach(() => { + fixture = TestBed.createComponent(TestIconNamespaceComponent); + testComponent = fixture.componentInstance; icon = fixture.debugElement.query(By.directive(IconDirective)); }); @@ -92,9 +73,8 @@ describe('icons-angular', () => { }); @Component({ - template: ` - - ` + imports: [IconDirective], + template: `` }) export class TestIconComponent { public name = 'down-square'; @@ -103,50 +83,7 @@ export class TestIconComponent { } @Component({ - template: ` - - ` + imports: [IconDirective], + template: `` }) -export class TestIconNamespaceComponent { -} - -type ComponentBedOptions = Pick< - NgModule, - 'providers' | 'declarations' | 'imports' ->; - -export interface ComponentBed { - bed: TestBedStatic; - fixture: ComponentFixture; - nativeElement: HTMLElement; - debugElement: DebugElement; - component: T; -} -export function createComponentBed( - component: Type, - options: ComponentBedOptions = { - providers: [], - declarations: [], - imports: [] - } -): ComponentBed { - const { imports, declarations, providers } = options; - const config = { - imports: [CommonModule, ...(imports || [])], - declarations: [component, ...(declarations || [])], - schemas: [NO_ERRORS_SCHEMA], - providers: providers || [] - }; - const bed = TestBed.configureTestingModule(config); - const fixture = TestBed.createComponent(component); - - fixture.detectChanges(); - - return { - bed: (bed as TestBedStatic), - fixture, - nativeElement: fixture.nativeElement, - debugElement: fixture.debugElement, - component: fixture.componentInstance - }; -} +export class TestIconNamespaceComponent {}