cron: {{ cron }}
+ ` +}) +export class NzDemoCronExpressionShortcutsComponent { + value: string = '1 1 * * *'; + cron: string = ''; + options = [ + { + label: 'Every hour', + value: '0 0-23/1 * * *' + }, + { + label: 'Every day at eight', + value: '0 8 * * *' + }, + { + label: 'Every Friday', + value: '0 0 * * 5' + } + ]; + + setValue(value: string): void { + this.value = value; + } + + getValue(value: string): void { + this.cron = value; + } +} diff --git a/components/cron-expression/demo/size.md b/components/cron-expression/demo/size.md new file mode 100644 index 00000000000..548524712b4 --- /dev/null +++ b/components/cron-expression/demo/size.md @@ -0,0 +1,14 @@ +--- +order: 1 +title: + zh-CN: 三种大小 + en-US: Three sizes of Input +--- + +## zh-CN + +我们为 `nz-cron-expression` 输入框定义了三种尺寸(大、默认、小),高度分别为 `40px`、`32px` 和 `24px`。 + +## en-US + +There are three sizes of an CronExpression box: `large` (40px)、`default` (32px) and `small` (24px). diff --git a/components/cron-expression/demo/size.ts b/components/cron-expression/demo/size.ts new file mode 100644 index 00000000000..c7ddd12699c --- /dev/null +++ b/components/cron-expression/demo/size.ts @@ -0,0 +1,20 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'nz-demo-cron-expression-size', + template: ` +++ +## When To Use + +When you want to use cron in Angular. + +### Import Module + +```ts +import { NzCronExpressionModule } from 'ng-zorro-antd/cron-expression'; +``` + +## API + +Install `cron-parser` in your project first: + +```sh +npm install cron-parser +``` + +### nz-cron-expression + +| Parameter | Description | Type | Default | +|-------------|--------------------------------------------------|-------------|---------| +| `[nzType]` | Cron rule type | `'linux'|'spring'` | `linux` | +| `[nzSize]` | The size of the input box. | `'large'|'small'|'default'` | `default` | +| `[nzCollapseDisable]` | Hide collapse | `boolean` | `false` | +| `[nzExtra]` | Render the content on the right | `TemplateRefNG-ZORRO experiments are features that are released but not yet considered stable or production ready
+Developers and users can opt-in into these features before they are fully released. But breaking changes may occur with any release.
+
++ +## 何时使用 + +需要在表单中使用 cron 格式验证时使用。 + +### 引入模块 + +```ts +import { NzCronExpressionModule } from 'ng-zorro-antd/cron-expression'; +``` + +## API + +别忘记先安装 cron-parser: + +```sh +npm install cron-parser +``` + +### nz-cron-expression + +| 参数 | 说明 | 类型 | 默认值 | +|----------------|----------------|-----------------------------|----------| +| `[nzType]` | cron 规则类型 | `'linux'|'spring'` | `linux` | +| `[nzSize]` | 设置输入框大小 | `'large'|'small'|'default'` | `default` | +| `[nzCollapseDisable]` | 隐藏折叠面板 | `boolean` | `false` | +| `[nzExtra]` | 自定义渲染右侧的内容 | `TemplateRefNG-ZORRO 实验性功能是指已发布但不稳定或者还未准备好用于生产环境的功能。
+开发者或用户可以选择在正式发布前使用这些功能,但是每次发布版本时都可能存在 breaking changes。
+
*Any value
,Separator between multiple values
-Connector for interval values
/Equally distributed
0-59Allowable range
', + minuteError: + '*Any value
,Separator between multiple values
-Connector for interval values
/Equally distributed
0-59Allowable range
', + hourError: + '*Any value
,Separator between multiple values
-Connector for interval values
/Equally distributed
0-23Allowable range
', + dayError: + '*Any value
,Separator between multiple values
-Connector for interval values
/Equally distributed
1-31Allowable range
', + monthError: + '*Any value
,Separator between multiple values
-Connector for interval values
/Equally distributed
1-12Allowable range
', + weekError: + '*Any value
,Separator between multiple values
-Connector for interval values
/Equally distributed
? Not specify0-7Allowable range (0 represents Sunday, 1-7 are Monday to Sunday)
' } }; diff --git a/components/i18n/languages/zh_CN.ts b/components/i18n/languages/zh_CN.ts index 4008de11cac..45fa17658d7 100755 --- a/components/i18n/languages/zh_CN.ts +++ b/components/i18n/languages/zh_CN.ts @@ -173,5 +173,26 @@ export default { }, Image: { preview: '预览' + }, + CronExpression: { + cronError: 'cron 表达式不合法', + second: '秒', + minute: '分钟', + hour: '小时', + day: '日', + month: '月', + week: '周', + secondError: + '*任意值
,多个值之间的分隔符
-区间值的连接符
/平均分配
0-59允许范围
', + minuteError: + '*任意值
,多个值之间的分隔符
-区间值的连接符
/平均分配
0-59允许范围
', + hourError: + '* 任意值
, 多个值之间的分隔符
- 区间值的连接符
/ 平均分配
0-23 允许范围
', + dayError: + '* 任意值
, 多个值之间的分隔符
- 区间值的连接符
/ 平均分配
1-31 允许范围
', + monthError: + '* 任意值
, 多个值之间的分隔符
- 区间值的连接符
/ 平均分配
1-12 允许范围
', + weekError: + '* 任意值
, 多个值之间的分隔符
- 区间值的连接符
/ 平均分配
? 不指定
0-7 允许范围(0代表周日,1-7依次为周一到周日)
' } }; diff --git a/components/i18n/nz-i18n.interface.ts b/components/i18n/nz-i18n.interface.ts index a427717c164..a4e000a9a8a 100644 --- a/components/i18n/nz-i18n.interface.ts +++ b/components/i18n/nz-i18n.interface.ts @@ -131,6 +131,28 @@ export interface NzTextI18nInterface { expand: string; } +export interface NzCronExpressionLabelI18n { + second?: string; + minute?: string; + hour?: string; + day?: string; + month?: string; + week?: string; + // innerHTML + secondError?: string; + minuteError?: string; + hourError?: string; + dayError?: string; + monthError?: string; + weekError?: string; +} + +export interface NzCronExpressionCronErrorI18n { + cronError?: string; +} + +export type NzCronExpressionI18nInterface = NzCronExpressionCronErrorI18n & NzCronExpressionLabelI18n; + export interface NzI18nInterface { locale: string; Pagination: NzPaginationI18nInterface; @@ -145,6 +167,7 @@ export interface NzI18nInterface { Upload: NzUploadI18nInterface; Empty: NzEmptyI18nInterface; Text?: NzTextI18nInterface; + CronExpression?: NzCronExpressionI18nInterface; } export type DateLocale = Locale; diff --git a/components/icon/icon.directive.ts b/components/icon/icon.directive.ts index f75e7eccf3d..6c97a8556bb 100644 --- a/components/icon/icon.directive.ts +++ b/components/icon/icon.directive.ts @@ -136,23 +136,32 @@ export class NzIconDirective extends IconDirective implements OnInit, OnChanges, private changeIcon2(): void { this.setClassName(); - // We don't need to re-enter the Angular zone for adding classes or attributes through the renderer. + // The Angular zone is left deliberately before the SVG is set + // since `_changeIcon` spawns asynchronous tasks as promise and + // HTTP calls. This is used to reduce the number of change detections + // while the icon is being loaded dynamically. this.ngZone.runOutsideAngular(() => { from(this._changeIcon()) .pipe(takeUntil(this.destroy$)) .subscribe({ next: svgOrRemove => { - // The _changeIcon method would call Renderer to remove the element of the old icon, - // which would call `markElementAsRemoved` eventually, - // so we should call `detectChanges` to tell Angular remove the DOM node. - // #7186 - this.changeDetectorRef.detectChanges(); - - if (svgOrRemove) { - this.setSVGData(svgOrRemove); - this.handleSpin(svgOrRemove); - this.handleRotate(svgOrRemove); - } + // Get back into the Angular zone after completing all the tasks. + // Since we manually run change detection locally, we have to re-enter + // the zone because the change detection might also be run on other local + // components, leading them to handle template functions outside of the Angular zone. + this.ngZone.run(() => { + // The _changeIcon method would call Renderer to remove the element of the old icon, + // which would call `markElementAsRemoved` eventually, + // so we should call `detectChanges` to tell Angular remove the DOM node. + // #7186 + this.changeDetectorRef.detectChanges(); + + if (svgOrRemove) { + this.setSVGData(svgOrRemove); + this.handleSpin(svgOrRemove); + this.handleRotate(svgOrRemove); + } + }); }, error: warn }); diff --git a/components/image/image-preview.component.ts b/components/image/image-preview.component.ts index 8b4dc203e8f..0e085e5c5c5 100644 --- a/components/image/image-preview.component.ts +++ b/components/image/image-preview.component.ts @@ -16,6 +16,7 @@ import { ViewChild, ViewEncapsulation } from '@angular/core'; +import { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser'; import { fromEvent } from 'rxjs'; import { takeUntil } from 'rxjs/operators'; @@ -75,7 +76,7 @@ const initialPosition = { class="ant-image-preview-img" #imgRef *ngIf="index === imageIndex" - [attr.src]="image.src" + [attr.src]="sanitizerResourceUrl(image.src)" [attr.srcset]="image.srcset" [attr.alt]="image.alt" [style.width]="image.width" @@ -197,7 +198,8 @@ export class NzImagePreviewComponent implements OnInit { public nzConfigService: NzConfigService, public config: NzImagePreviewOptions, private overlayRef: OverlayRef, - private destroy$: NzDestroyService + private destroy$: NzDestroyService, + private sanitizer: DomSanitizer ) { this.zoom = this.config.nzZoom ?? 1; this.rotate = this.config.nzRotate ?? 0; @@ -347,6 +349,10 @@ export class NzImagePreviewComponent implements OnInit { } } + sanitizerResourceUrl(url: string): SafeResourceUrl { + return this.sanitizer.bypassSecurityTrustResourceUrl(url); + } + private updatePreviewImageTransform(): void { this.previewImageTransform = `scale3d(${this.zoom}, ${this.zoom}, 1) rotate(${this.rotate}deg)`; } diff --git a/components/popconfirm/popconfirm.ts b/components/popconfirm/popconfirm.ts index d20b828939c..722f0edd603 100644 --- a/components/popconfirm/popconfirm.ts +++ b/components/popconfirm/popconfirm.ts @@ -51,6 +51,7 @@ const NZ_CONFIG_MODULE_NAME: NzConfigKey = 'popconfirm'; }) export class NzPopconfirmDirective extends NzTooltipBaseDirective { readonly _nzModuleName: NzConfigKey = NZ_CONFIG_MODULE_NAME; + static ngAcceptInputType_nzOkDanger: BooleanInput; static ngAcceptInputType_nzCondition: BooleanInput; static ngAcceptInputType_nzPopconfirmShowArrow: BooleanInput; static ngAcceptInputType_nzPopconfirmArrowPointAtCenter: BooleanInput; @@ -68,7 +69,7 @@ export class NzPopconfirmDirective extends NzTooltipBaseDirective { @Input('nzPopconfirmVisible') override visible?: boolean; @Input() nzOkText?: string; @Input() nzOkType?: string; - @Input() nzOkDanger?: boolean; + @Input() @InputBoolean() nzOkDanger?: boolean; @Input() nzCancelText?: string; @Input() nzBeforeConfirm?: () => Observable