From 208771ea1e2fc2e60ab4fbc36d59bfc689cbbb71 Mon Sep 17 00:00:00 2001 From: cipchk Date: Sun, 12 Nov 2023 13:13:58 +0800 Subject: [PATCH 1/2] feat(abc:cell): add `provideCellWidgets` --- .prettierignore | 3 + packages/abc/cell/cell-host.directive.ts | 3 +- packages/abc/cell/cell.component.ts | 128 +++++++++++++---------- packages/abc/cell/cell.module.ts | 5 +- packages/abc/cell/index.en-US.md | 30 ++---- packages/abc/cell/index.ts | 1 + packages/abc/cell/index.zh-CN.md | 30 ++---- packages/abc/cell/provide.ts | 18 ++++ src/app/app.config.ts | 7 +- src/app/shared/cell-widget/index.ts | 3 + src/app/shared/cell-widget/module.ts | 19 ---- src/app/shared/cell-widget/test.ts | 5 +- 12 files changed, 124 insertions(+), 128 deletions(-) create mode 100644 packages/abc/cell/provide.ts create mode 100644 src/app/shared/cell-widget/index.ts delete mode 100644 src/app/shared/cell-widget/module.ts diff --git a/.prettierignore b/.prettierignore index 9424ef9c8..4de0f1014 100644 --- a/.prettierignore +++ b/.prettierignore @@ -21,3 +21,6 @@ packages/theme/system/mixins/_functions.less theme-default.less theme-dark.less theme-compact.less + +# TODO: https://github.com/prettier/prettier/pull/15606 +# packages/**/* diff --git a/packages/abc/cell/cell-host.directive.ts b/packages/abc/cell/cell-host.directive.ts index 1b2069cc2..3b4d140c9 100644 --- a/packages/abc/cell/cell-host.directive.ts +++ b/packages/abc/cell/cell-host.directive.ts @@ -6,7 +6,8 @@ import { CellService } from './cell.service'; import { CellWidgetData } from './cell.types'; @Directive({ - selector: '[cell-widget-host]' + selector: '[cell-widget-host]', + standalone: true }) export class CellHostDirective implements OnInit { @Input() data!: CellWidgetData; diff --git a/packages/abc/cell/cell.component.ts b/packages/abc/cell/cell.component.ts index 6f99e839f..2359a312f 100644 --- a/packages/abc/cell/cell.component.ts +++ b/packages/abc/cell/cell.component.ts @@ -1,3 +1,4 @@ +import { NgTemplateOutlet } from '@angular/common'; import { ChangeDetectionStrategy, ChangeDetectorRef, @@ -13,6 +14,7 @@ import { SimpleChange, ViewEncapsulation } from '@angular/core'; +import { FormsModule } from '@angular/forms'; import type { SafeValue } from '@angular/platform-browser'; import { Router } from '@angular/router'; import { Subscription } from 'rxjs'; @@ -20,9 +22,15 @@ import { Subscription } from 'rxjs'; import { updateHostClass } from '@delon/util/browser'; import { BooleanInput, InputBoolean } from '@delon/util/decorator'; import { WINDOW } from '@delon/util/token'; +import { NzBadgeModule } from 'ng-zorro-antd/badge'; import type { NzSafeAny } from 'ng-zorro-antd/core/types'; +import { NzIconModule } from 'ng-zorro-antd/icon'; import { NzImage, NzImageService } from 'ng-zorro-antd/image'; +import { NzRadioModule } from 'ng-zorro-antd/radio'; +import { NzTagModule } from 'ng-zorro-antd/tag'; +import { NzToolTipModule } from 'ng-zorro-antd/tooltip'; +import { CellHostDirective } from './cell-host.directive'; import { CellService } from './cell.service'; import type { CellDefaultText, CellOptions, CellTextResult, CellValue, CellWidgetData } from './cell.types'; @@ -30,69 +38,73 @@ import type { CellDefaultText, CellOptions, CellTextResult, CellValue, CellWidge selector: 'cell, [cell]', template: ` - - - - - - - - - - - - - - - - {{ _unit }} - - + @switch(safeOpt.type) { @case('checkbox') { + + } @case('radio') { + + } @case('link') { + + } @case('tag') { + + + + } @case('badge') { + + } @case('widget') { + + } @case('img') { @for (i of $any(_text); track $index) { + + } } @default { @if(isText) { + + } @else { + + } @if(_unit) { + {{ _unit }} + } } } - {{ safeOpt.default?.text }} - - - - - + @if (showDefault) { + {{ safeOpt.default?.text }} + } @else { @if (safeOpt.tooltip) { + + + + } @else { + + } } - + @if (loading) { + + } @else { + + } `, exportAs: 'cell', preserveWhitespaces: false, changeDetection: ChangeDetectionStrategy.OnPush, - encapsulation: ViewEncapsulation.None + encapsulation: ViewEncapsulation.None, + standalone: true, + imports: [ + FormsModule, + NgTemplateOutlet, + NzRadioModule, + NzIconModule, + NzTagModule, + NzBadgeModule, + NzToolTipModule, + CellHostDirective + ] }) export class CellComponent implements OnChanges, OnDestroy { static ngAcceptInputType_loading: BooleanInput; @@ -151,7 +163,7 @@ export class CellComponent implements OnChanges, OnDestroy { private setClass(): void { const { el, renderer } = this; - const { renderType, size } = this.safeOpt; + const { renderType, size, type } = this.safeOpt; updateHostClass(el.nativeElement, renderer, { [`cell`]: true, [`cell__${renderType}`]: renderType != null, @@ -160,7 +172,7 @@ export class CellComponent implements OnChanges, OnDestroy { [`cell__has-default`]: this.showDefault, [`cell__disabled`]: this.disabled }); - el.nativeElement.dataset.type = this.safeOpt.type; + el.nativeElement.setAttribute('data-type', `${type}`); } ngOnChanges(changes: { [p in keyof CellComponent]?: SimpleChange }): void { diff --git a/packages/abc/cell/cell.module.ts b/packages/abc/cell/cell.module.ts index 67f835083..7d4590c98 100644 --- a/packages/abc/cell/cell.module.ts +++ b/packages/abc/cell/cell.module.ts @@ -25,9 +25,10 @@ const COMPS = [CellComponent]; NzTagModule, NzToolTipModule, NzIconModule, - NzImageModule + NzImageModule, + ...COMPS, + CellHostDirective ], - declarations: [...COMPS, CellHostDirective], exports: COMPS }) export class CellModule {} diff --git a/packages/abc/cell/index.en-US.md b/packages/abc/cell/index.en-US.md index 47063de66..d5efedd24 100644 --- a/packages/abc/cell/index.en-US.md +++ b/packages/abc/cell/index.en-US.md @@ -62,7 +62,7 @@ Cell formatting is supported for multiple data types, and supports widget mode. - `enum` Enum - `widget` Custom widget -**Custom widget** +## Custom widget Just implement the `CellWidgetInstance` interface, for example: @@ -95,28 +95,14 @@ export class CellTestWidget implements CellWidgetInstance { `data` is a fixed parameter, including `value`, `options` configuration items. -Secondly, you also need to call `CellService.registerWidget` to register the widget; usually a new module will be built separately, for example: +Finally, register the widget through `provideCellWidgets` under `app.config.ts`, for example: ```ts -import { NgModule } from '@angular/core'; - -import { CellService } from '@delon/abc/cell'; - -import { CellTestWidget } from './test'; -import { SharedModule } from '../shared.module'; - -export const CELL_WIDGET_COMPONENTS = [CellTestWidget]; - -@NgModule({ - declarations: CELL_WIDGET_COMPONENTS, - imports: [SharedModule], - exports: CELL_WIDGET_COMPONENTS -}) -export class CellWidgetModule { - constructor(srv: CellService) { - srv.registerWidget(CellTestWidget.KEY, CellTestWidget); - } +export const appConfig: ApplicationConfig = { + providers: [ + provideCellWidgets( + { KEY: CellTestWidget.KEY, type: CellTestWidget } + ), + ] } ``` - -Finally, just register `CellWidgetModule` under the root module. diff --git a/packages/abc/cell/index.ts b/packages/abc/cell/index.ts index 397ff56e1..e16b30379 100644 --- a/packages/abc/cell/index.ts +++ b/packages/abc/cell/index.ts @@ -3,3 +3,4 @@ export * from './cell-host.directive'; export * from './cell.module'; export * from './cell.service'; export * from './cell.types'; +export * from './provide'; diff --git a/packages/abc/cell/index.zh-CN.md b/packages/abc/cell/index.zh-CN.md index 576768624..49bf73a56 100644 --- a/packages/abc/cell/index.zh-CN.md +++ b/packages/abc/cell/index.zh-CN.md @@ -62,7 +62,7 @@ module: import { CellModule } from '@delon/abc/cell'; - `enum` 枚举转换 - `widget` 自定义小部件 -**自定义小部件** +## 自定义小部件 实现 `CellWidgetInstance` 接口即可,例如: @@ -95,28 +95,14 @@ export class CellTestWidget implements CellWidgetInstance { 其中 `data` 为固定参数,包含 `value`、`options` 配置项。 -其次,还需要调用 `CellService.registerWidget` 注册小部件;通常会单独构建一个新的模块,例如: +最后在 `app.config.ts` 下通过 `provideCellWidgets` 注册小部件,例如: ```ts -import { NgModule } from '@angular/core'; - -import { CellService } from '@delon/abc/cell'; - -import { CellTestWidget } from './test'; -import { SharedModule } from '../shared.module'; - -export const CELL_WIDGET_COMPONENTS = [CellTestWidget]; - -@NgModule({ - declarations: CELL_WIDGET_COMPONENTS, - imports: [SharedModule], - exports: CELL_WIDGET_COMPONENTS -}) -export class CellWidgetModule { - constructor(srv: CellService) { - srv.registerWidget(CellTestWidget.KEY, CellTestWidget); - } +export const appConfig: ApplicationConfig = { + providers: [ + provideCellWidgets( + { KEY: CellTestWidget.KEY, type: CellTestWidget } + ), + ] } ``` - -最后,将 `CellWidgetModule` 注册到根模块下即可。 diff --git a/packages/abc/cell/provide.ts b/packages/abc/cell/provide.ts new file mode 100644 index 000000000..a0b0c91f6 --- /dev/null +++ b/packages/abc/cell/provide.ts @@ -0,0 +1,18 @@ +import { ENVIRONMENT_INITIALIZER, EnvironmentProviders, inject, makeEnvironmentProviders } from '@angular/core'; + +import type { NzSafeAny } from 'ng-zorro-antd/core/types'; + +import { CellService } from './cell.service'; + +export function provideCellWidgets(...widgets: Array<{ KEY: string; type: NzSafeAny }>): EnvironmentProviders { + return makeEnvironmentProviders([ + { + provide: ENVIRONMENT_INITIALIZER, + multi: true, + useValue: () => { + const srv = inject(CellService); + widgets.forEach(widget => srv.registerWidget(widget.KEY, widget.type)); + } + } + ]); +} diff --git a/src/app/app.config.ts b/src/app/app.config.ts index d21d82ee2..3613e81eb 100644 --- a/src/app/app.config.ts +++ b/src/app/app.config.ts @@ -21,6 +21,7 @@ import { provideNuMonacoEditorConfig } from '@ng-util/monaco-editor'; import { zhCN as dateLang } from 'date-fns/locale'; import { provideTinymce } from 'ngx-tinymce'; +import { provideCellWidgets } from '@delon/abc/cell'; import { mockInterceptor, provideDelonMockConfig } from '@delon/mock'; import { ALAIN_I18N_TOKEN, provideAlain } from '@delon/theme'; import { AlainConfig } from '@delon/util/config'; @@ -32,7 +33,7 @@ import { I18NService, StartupService } from '@core'; import { CustomErrorHandler } from './core/error-handler'; import { EXAMPLE_COMPONENTS } from './routes/gen/examples'; import { routes } from './routes/routes'; -import { CellWidgetModule } from './shared/cell-widget/module'; +import { CELL_WIDGETS } from './shared/cell-widget'; import { IconComponent } from './shared/components/icon/icon.component'; import { JsonSchemaModule } from './shared/json-schema/json-schema.module'; import { STWidgetModule } from './shared/st-widget/st-widget.module'; @@ -100,7 +101,7 @@ export const appConfig: ApplicationConfig = { provideHttpClient(withInterceptors([mockInterceptor]), withFetch()), provideAnimations(), provideRouter(routes, withComponentInputBinding()), - // provideClientHydration(), + // provideClientHydration(), // 暂时不开启水合,除了编译时间长,还有就是对DOM要求比较高 provideAlain(alainConfig), provideNzConfig(ngZorroConfig), provideDelonMockConfig({ data: MOCKDATA }), @@ -108,8 +109,8 @@ export const appConfig: ApplicationConfig = { provideTinymce({ baseURL: 'https://cdnjs.cloudflare.com/ajax/libs/tinymce/4.9.2/' }), + provideCellWidgets(...CELL_WIDGETS), importProvidersFrom( - CellWidgetModule, JsonSchemaModule, STWidgetModule, ServiceWorkerModule.register('ngsw-worker.js', { enabled: environment.production }) diff --git a/src/app/shared/cell-widget/index.ts b/src/app/shared/cell-widget/index.ts new file mode 100644 index 000000000..b6d92e767 --- /dev/null +++ b/src/app/shared/cell-widget/index.ts @@ -0,0 +1,3 @@ +import { CellTestWidget } from './test'; + +export const CELL_WIDGETS = [{ KEY: CellTestWidget.KEY, type: CellTestWidget }]; diff --git a/src/app/shared/cell-widget/module.ts b/src/app/shared/cell-widget/module.ts deleted file mode 100644 index e5316bef8..000000000 --- a/src/app/shared/cell-widget/module.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { NgModule } from '@angular/core'; - -import { CellService } from '@delon/abc/cell'; - -import { CellTestWidget } from './test'; -import { SharedModule } from '../shared.module'; - -export const CELL_WIDGET_COMPONENTS = [CellTestWidget]; - -@NgModule({ - declarations: CELL_WIDGET_COMPONENTS, - imports: [SharedModule], - exports: CELL_WIDGET_COMPONENTS -}) -export class CellWidgetModule { - constructor(srv: CellService) { - srv.registerWidget(CellTestWidget.KEY, CellTestWidget); - } -} diff --git a/src/app/shared/cell-widget/test.ts b/src/app/shared/cell-widget/test.ts index 22bb329ba..51f337465 100644 --- a/src/app/shared/cell-widget/test.ts +++ b/src/app/shared/cell-widget/test.ts @@ -2,6 +2,7 @@ import { ChangeDetectionStrategy, Component } from '@angular/core'; import type { CellWidgetData, CellWidgetInstance } from '@delon/abc/cell'; import { NzMessageService } from 'ng-zorro-antd/message'; +import { NzToolTipModule } from 'ng-zorro-antd/tooltip'; @Component({ selector: 'cell-widget-test', @@ -9,7 +10,9 @@ import { NzMessageService } from 'ng-zorro-antd/message'; host: { '(click)': 'show()' }, - changeDetection: ChangeDetectionStrategy.OnPush + changeDetection: ChangeDetectionStrategy.OnPush, + standalone: true, + imports: [ NzToolTipModule ] }) export class CellTestWidget implements CellWidgetInstance { static readonly KEY = 'test'; From f68c9168cee9b0c05aede3280052757f72997a76 Mon Sep 17 00:00:00 2001 From: cipchk Date: Sun, 12 Nov 2023 13:22:34 +0800 Subject: [PATCH 2/2] chore: fix test --- packages/abc/cell/cell.component.ts | 6 ++++-- packages/abc/cell/cell.spec.ts | 14 ++++++++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/packages/abc/cell/cell.component.ts b/packages/abc/cell/cell.component.ts index 2359a312f..d660239f6 100644 --- a/packages/abc/cell/cell.component.ts +++ b/packages/abc/cell/cell.component.ts @@ -23,6 +23,7 @@ import { updateHostClass } from '@delon/util/browser'; import { BooleanInput, InputBoolean } from '@delon/util/decorator'; import { WINDOW } from '@delon/util/token'; import { NzBadgeModule } from 'ng-zorro-antd/badge'; +import { NzCheckboxModule } from 'ng-zorro-antd/checkbox'; import type { NzSafeAny } from 'ng-zorro-antd/core/types'; import { NzIconModule } from 'ng-zorro-antd/icon'; import { NzImage, NzImageService } from 'ng-zorro-antd/image'; @@ -39,8 +40,8 @@ import type { CellDefaultText, CellOptions, CellTextResult, CellValue, CellWidge template: ` @switch(safeOpt.type) { @case('checkbox') { -