diff --git a/components/button/button-group.component.ts b/components/button/button-group.component.ts index e570abe2fea..a17a86a51db 100644 --- a/components/button/button-group.component.ts +++ b/components/button/button-group.component.ts @@ -10,6 +10,9 @@ import { takeUntil } from 'rxjs/operators'; export type NzButtonGroupSize = 'large' | 'default' | 'small'; +/** + * @deprecated Will be removed in v20. Use `NzSpaceCompactComponent` instead. + */ @Component({ selector: 'nz-button-group', exportAs: 'nzButtonGroup', diff --git a/components/button/button.component.ts b/components/button/button.component.ts index 5f00a05d685..ac421551bbf 100644 --- a/components/button/button.component.ts +++ b/components/button/button.component.ts @@ -15,28 +15,35 @@ import { Input, NgZone, OnChanges, - OnDestroy, OnInit, Renderer2, SimpleChanges, ViewEncapsulation, - booleanAttribute + booleanAttribute, + computed, + inject, + signal } from '@angular/core'; import { Subject, fromEvent } from 'rxjs'; import { filter, startWith, takeUntil } from 'rxjs/operators'; import { NzConfigKey, NzConfigService, WithConfig } from 'ng-zorro-antd/core/config'; +import { NzDestroyService } from 'ng-zorro-antd/core/services'; +import { NzSizeLDSType } from 'ng-zorro-antd/core/types'; import { NzIconDirective, NzIconModule } from 'ng-zorro-antd/icon'; +import { NZ_SPACE_COMPACT_ITEM_TYPE, NZ_SPACE_COMPACT_SIZE, NzSpaceCompactItemDirective } from 'ng-zorro-antd/space'; export type NzButtonType = 'primary' | 'default' | 'dashed' | 'link' | 'text' | null; export type NzButtonShape = 'circle' | 'round' | null; -export type NzButtonSize = 'large' | 'default' | 'small'; +export type NzButtonSize = NzSizeLDSType; const NZ_CONFIG_MODULE_NAME: NzConfigKey = 'button'; @Component({ selector: 'button[nz-button], a[nz-button]', exportAs: 'nzButton', + standalone: true, + imports: [NzIconModule], preserveWhitespaces: false, changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, @@ -55,8 +62,8 @@ const NZ_CONFIG_MODULE_NAME: NzConfigKey = 'button'; '[class.ant-btn-text]': `nzType === 'text'`, '[class.ant-btn-circle]': `nzShape === 'circle'`, '[class.ant-btn-round]': `nzShape === 'round'`, - '[class.ant-btn-lg]': `nzSize === 'large'`, - '[class.ant-btn-sm]': `nzSize === 'small'`, + '[class.ant-btn-lg]': `finalSize() === 'large'`, + '[class.ant-btn-sm]': `finalSize() === 'small'`, '[class.ant-btn-dangerous]': `nzDanger`, '[class.ant-btn-loading]': `nzLoading`, '[class.ant-btn-background-ghost]': `nzGhost`, @@ -67,10 +74,10 @@ const NZ_CONFIG_MODULE_NAME: NzConfigKey = 'button'; '[attr.tabindex]': 'disabled ? -1 : (tabIndex === null ? null : tabIndex)', '[attr.disabled]': 'disabled || null' }, - imports: [NzIconModule], - standalone: true + hostDirectives: [NzSpaceCompactItemDirective], + providers: [NzDestroyService, { provide: NZ_SPACE_COMPACT_ITEM_TYPE, useValue: 'btn' }] }) -export class NzButtonComponent implements OnDestroy, OnChanges, AfterViewInit, AfterContentInit, OnInit { +export class NzButtonComponent implements OnChanges, AfterViewInit, AfterContentInit, OnInit { readonly _nzModuleName: NzConfigKey = NZ_CONFIG_MODULE_NAME; @ContentChild(NzIconDirective, { read: ElementRef }) nzIconDirectiveElement!: ElementRef; @@ -85,7 +92,17 @@ export class NzButtonComponent implements OnDestroy, OnChanges, AfterViewInit, A @Input() nzShape: NzButtonShape = null; @Input() @WithConfig() nzSize: NzButtonSize = 'default'; dir: Direction = 'ltr'; - private destroy$ = new Subject(); + + protected finalSize = computed(() => { + if (this.compactSize) { + return this.compactSize(); + } + return this.size(); + }); + + private size = signal(this.nzSize); + private compactSize = inject(NZ_SPACE_COMPACT_SIZE, { optional: true }); + private destroy$ = inject(NzDestroyService); private loading$ = new Subject(); insertSpan(nodes: NodeList, renderer: Renderer2): void { @@ -117,16 +134,18 @@ export class NzButtonComponent implements OnDestroy, OnChanges, AfterViewInit, A private renderer: Renderer2, public nzConfigService: NzConfigService, private directionality: Directionality - ) { + ) {} + + ngOnInit(): void { + this.size.set(this.nzSize); this.nzConfigService .getConfigChangeEventForComponent(NZ_CONFIG_MODULE_NAME) .pipe(takeUntil(this.destroy$)) .subscribe(() => { + this.size.set(this.nzSize); this.cdr.markForCheck(); }); - } - ngOnInit(): void { this.directionality.change?.pipe(takeUntil(this.destroy$)).subscribe((direction: Direction) => { this.dir = direction; this.cdr.detectChanges(); @@ -150,11 +169,13 @@ export class NzButtonComponent implements OnDestroy, OnChanges, AfterViewInit, A }); } - ngOnChanges(changes: SimpleChanges): void { - const { nzLoading } = changes; + ngOnChanges({ nzLoading, nzSize }: SimpleChanges): void { if (nzLoading) { this.loading$.next(this.nzLoading); } + if (nzSize) { + this.size.set(nzSize.currentValue); + } } ngAfterViewInit(): void { @@ -177,9 +198,4 @@ export class NzButtonComponent implements OnDestroy, OnChanges, AfterViewInit, A } }); } - - ngOnDestroy(): void { - this.destroy$.next(); - this.destroy$.complete(); - } } diff --git a/components/button/demo/button-group.md b/components/button/demo/button-group.md index 2f1368b5a95..1679f6f8f40 100755 --- a/components/button/demo/button-group.md +++ b/components/button/demo/button-group.md @@ -11,8 +11,12 @@ title: 通过设置 `nzSize` 为 `large` `small` 分别把按钮组合设为大、小尺寸。若不设置 `nzSize`,则尺寸为中。 +警告:`` 在 `v19` 中被弃用,请使用 `` 组件替代。 + ## en-US Buttons can be grouped by placing multiple `nz-button` components into a `nz-button-group`. The `nzSize` can be set to `large`, `small` or left unset resulting in a default size. + +Warning: `` is deprecated in `v19`, please use `` instead. diff --git a/components/button/style/space-compact.less b/components/button/style/space-compact.less index 0fac996dc9d..936117365a8 100644 --- a/components/button/style/space-compact.less +++ b/components/button/style/space-compact.less @@ -13,7 +13,7 @@ // Special styles for Primary Button &-compact-item.@{btn-prefix-cls}-primary { - &:not([disabled]) + &:not([disabled]) { + &:not([disabled]) + &:not([disabled]):not([ant-click-animating-without-extra-node='true']) { position: relative; &::after { @@ -69,7 +69,7 @@ // Special styles for Primary Button &-compact-vertical-item { &.@{btn-prefix-cls}-primary { - &:not([disabled]) + &:not([disabled]) { + &:not([disabled]) + &:not([disabled]):not([ant-click-animating-without-extra-node='true']) { position: relative; &::after { diff --git a/components/cascader/cascader.component.ts b/components/cascader/cascader.component.ts index 3c82b9941b7..290a15228ae 100644 --- a/components/cascader/cascader.component.ts +++ b/components/cascader/cascader.component.ts @@ -29,9 +29,11 @@ import { ViewChildren, ViewEncapsulation, booleanAttribute, + computed, forwardRef, inject, - numberAttribute + numberAttribute, + signal } from '@angular/core'; import { ControlValueAccessor, FormsModule, NG_VALUE_ACCESSOR } from '@angular/forms'; import { BehaviorSubject, EMPTY, Observable, fromEvent, of as observableOf } from 'rxjs'; @@ -48,6 +50,7 @@ import { NgClassType, NgStyleInterface, NzSafeAny, + NzSizeLDSType, NzStatus, NzValidateStatus } from 'ng-zorro-antd/core/types'; @@ -55,6 +58,7 @@ import { getStatusClassNames, toArray } from 'ng-zorro-antd/core/util'; import { NzEmptyModule } from 'ng-zorro-antd/empty'; import { NzCascaderI18nInterface, NzI18nService } from 'ng-zorro-antd/i18n'; import { NzIconModule } from 'ng-zorro-antd/icon'; +import { NZ_SPACE_COMPACT_ITEM_TYPE, NZ_SPACE_COMPACT_SIZE, NzSpaceCompactItemDirective } from 'ng-zorro-antd/space'; import { NzCascaderOptionComponent } from './cascader-li.component'; import { NzCascaderService } from './cascader.service'; @@ -78,137 +82,136 @@ const defaultDisplayRender = (labels: string[]): string => labels.join(' / '); exportAs: 'nzCascader', preserveWhitespaces: false, template: ` -
- @if (nzShowInput) { -
- - - - @if (showLabelRender) { - - @if (!isLabelRenderTemplate) { - {{ labelRenderText }} - } @else { - - } - - } @else { - {{ showPlaceholder ? nzPlaceHolder || locale?.placeholder : null }} - } -
- @if (nzShowArrow) { - - @if (!isLoading) { - + @if (nzShowInput) { +
+ + + + @if (showLabelRender) { + + @if (!isLabelRenderTemplate) { + {{ labelRenderText }} } @else { - - } - - @if (hasFeedback && !!status) { - + } + } @else { + {{ showPlaceholder ? nzPlaceHolder || locale?.placeholder : null }} } - @if (clearIconVisible) { - - - - } +
+ @if (nzShowArrow) { + + @if (!isLoading) { + + } @else { + + } + + @if (hasFeedback && !!status) { + + } + + } + @if (clearIconVisible) { + + + } - -
- -
- @if (shouldShowEmpty) { -
    -
  • - -
  • -
- } @else { - @for (options of cascaderService.columns; track options; let i = $index) { -
    - @for (option of options; track option.value) { -
  • - } +
    + @if (shouldShowEmpty) { +
      +
    • + +
    + } @else { + @for (options of cascaderService.columns; track options; let i = $index) { +
      + @for (option of options; track option.value) { +
    • + } +
    + } } - } +
-
-
+ + } + `, animations: [slideMotion], providers: [ @@ -217,14 +220,15 @@ const defaultDisplayRender = (labels: string[]): string => labels.join(' / '); useExisting: forwardRef(() => NzCascaderComponent), multi: true }, + { provide: NZ_SPACE_COMPACT_ITEM_TYPE, useValue: 'select' }, NzCascaderService, NzDestroyService ], host: { '[attr.tabIndex]': '"0"', '[class.ant-select-in-form-item]': '!!nzFormStatusService', - '[class.ant-select-lg]': 'nzSize === "large"', - '[class.ant-select-sm]': 'nzSize === "small"', + '[class.ant-select-lg]': 'finalSize() === "large"', + '[class.ant-select-sm]': 'finalSize() === "small"', '[class.ant-select-allow-clear]': 'nzAllowClear', '[class.ant-select-show-arrow]': 'nzShowArrow', '[class.ant-select-show-search]': '!!nzShowSearch', @@ -234,6 +238,7 @@ const defaultDisplayRender = (labels: string[]): string => labels.join(' / '); '[class.ant-select-single]': 'true', '[class.ant-select-rtl]': `dir === 'rtl'` }, + hostDirectives: [NzSpaceCompactItemDirective], imports: [ OverlayModule, FormsModule, @@ -346,6 +351,15 @@ export class NzCascaderComponent isComposing = false; + protected finalSize = computed(() => { + if (this.compactSize) { + return this.compactSize(); + } + return this.size(); + }); + + private size = signal(this.nzSize); + private compactSize = inject(NZ_SPACE_COMPACT_SIZE, { optional: true }); private inputString = ''; private isOpening = false; private delayMenuTimer?: ReturnType; @@ -474,10 +488,12 @@ export class NzCascaderComponent this.setLocale(); }); + this.size.set(this.nzSize); this.nzConfigService .getConfigChangeEventForComponent(NZ_CONFIG_MODULE_NAME) .pipe(takeUntil(this.destroy$)) .subscribe(() => { + this.size.set(this.nzSize); this.cdr.markForCheck(); }); @@ -491,11 +507,13 @@ export class NzCascaderComponent this.setupKeydownListener(); } - ngOnChanges(changes: SimpleChanges): void { - const { nzStatus } = changes; + ngOnChanges({ nzStatus, nzSize }: SimpleChanges): void { if (nzStatus) { this.setStatusStyles(this.nzStatus, this.hasFeedback); } + if (nzSize) { + this.size.set(nzSize.currentValue); + } } ngOnDestroy(): void { diff --git a/components/cascader/typings.ts b/components/cascader/typings.ts index 21c03aa96fc..0d62672d965 100644 --- a/components/cascader/typings.ts +++ b/components/cascader/typings.ts @@ -5,11 +5,11 @@ import { Observable } from 'rxjs'; -import { NzSafeAny } from 'ng-zorro-antd/core/types'; +import { NzSafeAny, NzSizeLDSType } from 'ng-zorro-antd/core/types'; export type NzCascaderExpandTrigger = 'click' | 'hover'; export type NzCascaderTriggerType = 'click' | 'hover'; -export type NzCascaderSize = 'small' | 'large' | 'default'; +export type NzCascaderSize = NzSizeLDSType; export type NzCascaderFilter = (searchValue: string, path: NzCascaderOption[]) => boolean; export type NzCascaderSorter = (a: NzCascaderOption[], b: NzCascaderOption[], inputValue: string) => number; diff --git a/components/core/types/size.ts b/components/core/types/size.ts index a816a7f03be..42a7bd022d1 100644 --- a/components/core/types/size.ts +++ b/components/core/types/size.ts @@ -5,5 +5,6 @@ // TODO: replace other size with this type. export type NzSizeLDSType = 'large' | 'default' | 'small'; +export type NzSizeLMSType = 'large' | 'middle' | 'small'; export type NzSizeMDSType = 'middle' | 'default' | 'small'; export type NzSizeDSType = 'default' | 'small'; diff --git a/components/date-picker/date-picker.component.ts b/components/date-picker/date-picker.component.ts index 49757269b07..1c4fc6c1464 100644 --- a/components/date-picker/date-picker.component.ts +++ b/components/date-picker/date-picker.component.ts @@ -21,6 +21,7 @@ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, + computed, ElementRef, EventEmitter, forwardRef, @@ -32,6 +33,7 @@ import { Output, QueryList, Renderer2, + signal, SimpleChanges, TemplateRef, ViewChild, @@ -56,6 +58,7 @@ import { FunctionProp, NgClassInterface, NzSafeAny, + NzSizeLDSType, NzStatus, NzValidateStatus, OnChangeType, @@ -69,6 +72,7 @@ import { NzI18nService } from 'ng-zorro-antd/i18n'; import { NzIconModule } from 'ng-zorro-antd/icon'; +import { NZ_SPACE_COMPACT_ITEM_TYPE, NZ_SPACE_COMPACT_SIZE, NzSpaceCompactItemDirective } from 'ng-zorro-antd/space'; import { DatePickerService } from './date-picker.service'; import { DateRangePopupComponent } from './date-range-popup.component'; @@ -241,17 +245,19 @@ export type NzPlacement = 'bottomLeft' | 'bottomRight' | 'topLeft' | 'topRight'; host: { '[class.ant-picker]': `true`, '[class.ant-picker-range]': `isRange`, - '[class.ant-picker-large]': `nzSize === 'large'`, - '[class.ant-picker-small]': `nzSize === 'small'`, + '[class.ant-picker-large]': `finalSize() === 'large'`, + '[class.ant-picker-small]': `finalSize() === 'small'`, '[class.ant-picker-disabled]': `nzDisabled`, '[class.ant-picker-rtl]': `dir === 'rtl'`, '[class.ant-picker-borderless]': `nzBorderless`, '[class.ant-picker-inline]': `nzInline`, '(click)': 'onClickInputBox($event)' }, + hostDirectives: [NzSpaceCompactItemDirective], providers: [ NzDestroyService, DatePickerService, + { provide: NZ_SPACE_COMPACT_ITEM_TYPE, useValue: 'picker' }, { provide: NG_VALUE_ACCESSOR, multi: true, @@ -346,7 +352,6 @@ export class NzDatePickerComponent implements OnInit, OnChanges, AfterViewInit, @ViewChild('pickerInput', { static: false }) pickerInput?: ElementRef; @ViewChildren('rangePickerInput') rangePickerInputs?: QueryList>; - private document: Document = inject(DOCUMENT); origin: CdkOverlayOrigin; inputSize: number = 12; inputWidth?: number; @@ -363,6 +368,17 @@ export class NzDatePickerComponent implements OnInit, OnChanges, AfterViewInit, return this.isOpenHandledByUser() ? !!this.nzOpen : this.overlayOpen; } + protected finalSize = computed(() => { + if (this.compactSize) { + return this.compactSize(); + } + return this.size(); + }); + + private size = signal(this.nzSize); + private compactSize = inject(NZ_SPACE_COMPACT_SIZE, { optional: true }); + private document: Document = inject(DOCUMENT); + ngAfterViewInit(): void { if (this.nzAutoFocus) { this.focus(); @@ -711,32 +727,41 @@ export class NzDatePickerComponent implements OnInit, OnChanges, AfterViewInit, }); } - ngOnChanges(changes: SimpleChanges): void { - const { nzStatus, nzPlacement } = changes; - if (changes.nzPopupStyle) { + ngOnChanges({ + nzStatus, + nzPlacement, + nzPopupStyle, + nzPlaceHolder, + nzLocale, + nzFormat, + nzRenderExtraFooter, + nzMode, + nzSize + }: SimpleChanges): void { + if (nzPopupStyle) { // Always assign the popup style patch this.nzPopupStyle = this.nzPopupStyle ? { ...this.nzPopupStyle, ...POPUP_STYLE_PATCH } : POPUP_STYLE_PATCH; } // Mark as customized placeholder by user once nzPlaceHolder assigned at the first time - if (changes.nzPlaceHolder?.currentValue) { + if (nzPlaceHolder?.currentValue) { this.isCustomPlaceHolder = true; } - if (changes.nzFormat?.currentValue) { + if (nzFormat?.currentValue) { this.isCustomFormat = true; } - if (changes.nzLocale) { + if (nzLocale) { // The nzLocale is currently handled by user this.setDefaultPlaceHolder(); } - if (changes.nzRenderExtraFooter) { + if (nzRenderExtraFooter) { this.extraFooter = valueFunctionProp(this.nzRenderExtraFooter!); } - if (changes.nzMode) { + if (nzMode) { this.setDefaultPlaceHolder(); this.setModeAndFormat(); } @@ -748,6 +773,10 @@ export class NzDatePickerComponent implements OnInit, OnChanges, AfterViewInit, if (nzPlacement) { this.setPlacement(this.nzPlacement); } + + if (nzSize) { + this.size.set(nzSize.currentValue); + } } setModeAndFormat(): void { diff --git a/components/input-number/demo/group.md b/components/input-number/demo/group.md index 53f5343b0c8..df8ac5a4be2 100644 --- a/components/input-number/demo/group.md +++ b/components/input-number/demo/group.md @@ -11,8 +11,12 @@ title: 注意:使用 `nzCompact` 模式时,不需要通过 `nz-col` 来控制宽度。 +警告:该用法在 `v19` 中被弃用,请使用 `` 或 `` 组件来实现输入框组合。 + ## en-US -InputNumber.Group example. +`` example. + +Note: You don't need `nz-col` to control the width in the `nzCompact` mode. -Note: You don't need `nz-col` to control the width in the `nzCompact` mode. \ No newline at end of file +Warning: This usage is deprecated in `v19`, please use `` or `` components to achieve the same effect. diff --git a/components/input-number/input-number-group.component.ts b/components/input-number/input-number-group.component.ts index 2731e297f4a..f194499d240 100644 --- a/components/input-number/input-number-group.component.ts +++ b/components/input-number/input-number-group.component.ts @@ -32,6 +32,7 @@ import { distinctUntilChanged, map, mergeMap, startWith, switchMap, takeUntil } import { NzFormNoStatusService, NzFormPatchModule, NzFormStatusService } from 'ng-zorro-antd/core/form'; import { NgClassInterface, NzSizeLDSType, NzStatus, NzValidateStatus } from 'ng-zorro-antd/core/types'; import { getStatusClassNames } from 'ng-zorro-antd/core/util'; +import { NZ_SPACE_COMPACT_ITEM_TYPE } from 'ng-zorro-antd/space'; import { NzInputNumberGroupSlotComponent } from './input-number-group-slot.component'; import { NzInputNumberComponent } from './input-number.component'; @@ -47,10 +48,8 @@ export class NzInputNumberGroupWhitSuffixOrPrefixDirective { @Component({ selector: 'nz-input-number-group', exportAs: 'nzInputNumberGroup', - preserveWhitespaces: false, - encapsulation: ViewEncapsulation.None, - changeDetection: ChangeDetectionStrategy.OnPush, - providers: [NzFormNoStatusService], + standalone: true, + imports: [NzInputNumberGroupSlotComponent, NgClass, NgTemplateOutlet, NzFormPatchModule], template: ` @if (isAddOn) { @@ -126,8 +125,9 @@ export class NzInputNumberGroupWhitSuffixOrPrefixDirective { '[class.ant-input-number-affix-wrapper-lg]': `!isAddOn && isAffix && isLarge`, '[class.ant-input-number-affix-wrapper-sm]': `!isAddOn && isAffix && isSmall` }, - imports: [NzInputNumberGroupSlotComponent, NgClass, NgTemplateOutlet, NzFormPatchModule], - standalone: true + encapsulation: ViewEncapsulation.None, + changeDetection: ChangeDetectionStrategy.OnPush, + providers: [NzFormNoStatusService, { provide: NZ_SPACE_COMPACT_ITEM_TYPE, useValue: 'input-number' }] }) export class NzInputNumberGroupComponent implements AfterContentInit, OnChanges, OnInit, OnDestroy { @ContentChildren(NzInputNumberComponent, { descendants: true }) @@ -142,6 +142,9 @@ export class NzInputNumberGroupComponent implements AfterContentInit, OnChanges, @Input() nzStatus: NzStatus = ''; @Input() nzSuffix?: string | TemplateRef; @Input() nzSize: NzSizeLDSType = 'default'; + /** + * @deprecated Will be removed in v20. Use `NzSpaceCompactComponent` instead. + */ @Input({ transform: booleanAttribute }) nzCompact = false; isLarge = false; isSmall = false; @@ -172,7 +175,7 @@ export class NzInputNumberGroupComponent implements AfterContentInit, OnChanges, updateChildrenInputSize(): void { if (this.listOfNzInputNumberComponent) { - this.listOfNzInputNumberComponent.forEach(item => (item.nzSize = this.nzSize)); + this.listOfNzInputNumberComponent.forEach(item => item['size'].set(this.nzSize)); } } diff --git a/components/input-number/input-number.component.ts b/components/input-number/input-number.component.ts index 3bcf56cb5e5..ed48d69bdea 100644 --- a/components/input-number/input-number.component.ts +++ b/components/input-number/input-number.component.ts @@ -24,9 +24,11 @@ import { ViewChild, ViewEncapsulation, booleanAttribute, + computed, forwardRef, inject, - numberAttribute + numberAttribute, + signal } from '@angular/core'; import { ControlValueAccessor, FormsModule, NG_VALUE_ACCESSOR } from '@angular/forms'; import { Subject, fromEvent, merge } from 'rxjs'; @@ -44,6 +46,7 @@ import { } from 'ng-zorro-antd/core/types'; import { getStatusClassNames, isNotNil } from 'ng-zorro-antd/core/util'; import { NzIconModule } from 'ng-zorro-antd/icon'; +import { NZ_SPACE_COMPACT_ITEM_TYPE, NZ_SPACE_COMPACT_SIZE, NzSpaceCompactItemDirective } from 'ng-zorro-antd/space'; @Component({ selector: 'nz-input-number', @@ -97,6 +100,7 @@ import { NzIconModule } from 'ng-zorro-antd/icon'; useExisting: forwardRef(() => NzInputNumberComponent), multi: true }, + { provide: NZ_SPACE_COMPACT_ITEM_TYPE, useValue: 'input-number' }, NzDestroyService ], changeDetection: ChangeDetectionStrategy.OnPush, @@ -105,21 +109,18 @@ import { NzIconModule } from 'ng-zorro-antd/icon'; class: 'ant-input-number', '[class.ant-input-number-in-form-item]': '!!nzFormStatusService', '[class.ant-input-number-focused]': 'isFocused', - '[class.ant-input-number-lg]': `nzSize === 'large'`, - '[class.ant-input-number-sm]': `nzSize === 'small'`, + '[class.ant-input-number-lg]': `finalSize() === 'large'`, + '[class.ant-input-number-sm]': `finalSize() === 'small'`, '[class.ant-input-number-disabled]': 'nzDisabled', '[class.ant-input-number-readonly]': 'nzReadOnly', '[class.ant-input-number-rtl]': `dir === 'rtl'`, '[class.ant-input-number-borderless]': `nzBorderless` }, imports: [NzIconModule, FormsModule, NzFormPatchModule], - standalone: true + standalone: true, + hostDirectives: [NzSpaceCompactItemDirective] }) export class NzInputNumberComponent implements ControlValueAccessor, AfterViewInit, OnChanges, OnInit, OnDestroy { - private autoStepTimer?: ReturnType; - private parsedValue?: string | number; - private value?: number; - private isNzDisableFirstChange: boolean = true; displayValue?: string | number; isFocused = false; disabled$ = new Subject(); @@ -163,6 +164,20 @@ export class NzInputNumberComponent implements ControlValueAccessor, AfterViewIn @Input({ transform: booleanAttribute }) nzBorderless: boolean = false; @Input() nzFormatter: (value: number) => string | number = value => value; + protected finalSize = computed(() => { + if (this.compactSize) { + return this.compactSize(); + } + return this.size(); + }); + + private size = signal(this.nzSize); + private compactSize = inject(NZ_SPACE_COMPACT_SIZE, { optional: true }); + private autoStepTimer?: ReturnType; + private parsedValue?: string | number; + private value?: number; + private isNzDisableFirstChange: boolean = true; + onModelChange(value: string): void { this.parsedValue = this.nzParser(value); this.inputElement.nativeElement.value = `${this.parsedValue}`; @@ -478,9 +493,8 @@ export class NzInputNumberComponent implements ControlValueAccessor, AfterViewIn }); } - ngOnChanges(changes: SimpleChanges): void { - const { nzStatus, nzDisabled } = changes; - if (changes.nzFormatter && !changes.nzFormatter.isFirstChange()) { + ngOnChanges({ nzStatus, nzDisabled, nzFormatter, nzSize }: SimpleChanges): void { + if (nzFormatter && !nzFormatter.isFirstChange()) { const validValue = this.getCurrentValidValue(this.parsedValue!); this.setValue(validValue); this.updateDisplayValue(validValue); @@ -491,6 +505,9 @@ export class NzInputNumberComponent implements ControlValueAccessor, AfterViewIn if (nzStatus) { this.setStatusStyles(this.nzStatus, this.hasFeedback); } + if (nzSize) { + this.size.set(nzSize.currentValue); + } } ngAfterViewInit(): void { diff --git a/components/input/demo/group.md b/components/input/demo/group.md index 789025fa80a..eceff776980 100755 --- a/components/input/demo/group.md +++ b/components/input/demo/group.md @@ -11,9 +11,12 @@ title: 注意:使用 `nzCompact` 模式时,不需要通过 `nz-col` 来控制宽度。 +警告:该用法在 `v19` 中被弃用,请使用 `` 或 `` 组件来实现输入框组合。 + ## en-US Input.Group example Note: You don't need `nz-col` to control the width in the `nzCompact` mode. +Warning: This usage is deprecated in `v19`, please use `` or `` components to achieve the same effect. diff --git a/components/input/input-group.component.ts b/components/input/input-group.component.ts index 7dedbe79586..e055ff8bcac 100644 --- a/components/input/input-group.component.ts +++ b/components/input/input-group.component.ts @@ -32,6 +32,7 @@ import { distinctUntilChanged, map, mergeMap, startWith, switchMap, takeUntil } import { NzFormNoStatusService, NzFormPatchModule, NzFormStatusService } from 'ng-zorro-antd/core/form'; import { NgClassInterface, NzSizeLDSType, NzStatus, NzValidateStatus } from 'ng-zorro-antd/core/types'; import { getStatusClassNames } from 'ng-zorro-antd/core/util'; +import { NZ_SPACE_COMPACT_ITEM_TYPE, NzSpaceCompactItemDirective } from 'ng-zorro-antd/space'; import { NzInputGroupSlotComponent } from './input-group-slot.component'; import { NzInputDirective } from './input.directive'; @@ -47,10 +48,10 @@ export class NzInputGroupWhitSuffixOrPrefixDirective { @Component({ selector: 'nz-input-group', exportAs: 'nzInputGroup', - preserveWhitespaces: false, + standalone: true, + imports: [NzInputGroupSlotComponent, NgClass, NgTemplateOutlet, NzFormPatchModule], encapsulation: ViewEncapsulation.None, - changeDetection: ChangeDetectionStrategy.OnPush, - providers: [NzFormNoStatusService], + providers: [NzFormNoStatusService, { provide: NZ_SPACE_COMPACT_ITEM_TYPE, useValue: 'input' }], template: ` @if (isAddOn) { @@ -131,8 +132,8 @@ export class NzInputGroupWhitSuffixOrPrefixDirective { '[class.ant-input-group-lg]': `!isAffix && !isAddOn && isLarge`, '[class.ant-input-group-sm]': `!isAffix && !isAddOn && isSmall` }, - imports: [NzInputGroupSlotComponent, NgClass, NgTemplateOutlet, NzFormPatchModule], - standalone: true + hostDirectives: [NzSpaceCompactItemDirective], + changeDetection: ChangeDetectionStrategy.OnPush }) export class NzInputGroupComponent implements AfterContentInit, OnChanges, OnInit, OnDestroy { @ContentChildren(NzInputDirective) listOfNzInputDirective!: QueryList; @@ -147,6 +148,9 @@ export class NzInputGroupComponent implements AfterContentInit, OnChanges, OnIni @Input() nzSuffix?: string | TemplateRef; @Input() nzSize: NzSizeLDSType = 'default'; @Input({ transform: booleanAttribute }) nzSearch = false; + /** + * @deprecated Will be removed in v20. Use `NzSpaceCompactComponent` instead. + */ @Input({ transform: booleanAttribute }) nzCompact = false; isLarge = false; isSmall = false; @@ -177,7 +181,7 @@ export class NzInputGroupComponent implements AfterContentInit, OnChanges, OnIni updateChildrenInputSize(): void { if (this.listOfNzInputDirective) { - this.listOfNzInputDirective.forEach(item => (item.nzSize = this.nzSize)); + this.listOfNzInputDirective.forEach(item => item['size'].set(this.nzSize)); } } diff --git a/components/input/input.directive.ts b/components/input/input.directive.ts index 44c4868fda1..38d62bff118 100644 --- a/components/input/input.directive.ts +++ b/components/input/input.directive.ts @@ -10,21 +10,24 @@ import { ElementRef, Input, OnChanges, - OnDestroy, OnInit, Renderer2, SimpleChanges, ViewContainerRef, booleanAttribute, - inject + computed, + inject, + signal } from '@angular/core'; import { NgControl } from '@angular/forms'; import { Subject } from 'rxjs'; import { distinctUntilChanged, filter, takeUntil } from 'rxjs/operators'; import { NzFormItemFeedbackIconComponent, NzFormNoStatusService, NzFormStatusService } from 'ng-zorro-antd/core/form'; +import { NzDestroyService } from 'ng-zorro-antd/core/services'; import { NgClassInterface, NzSizeLDSType, NzStatus, NzValidateStatus } from 'ng-zorro-antd/core/types'; import { getStatusClassNames } from 'ng-zorro-antd/core/util'; +import { NZ_SPACE_COMPACT_ITEM_TYPE, NZ_SPACE_COMPACT_SIZE, NzSpaceCompactItemDirective } from 'ng-zorro-antd/space'; @Directive({ selector: 'input[nz-input],textarea[nz-input]', @@ -33,15 +36,17 @@ import { getStatusClassNames } from 'ng-zorro-antd/core/util'; class: 'ant-input', '[class.ant-input-disabled]': 'disabled', '[class.ant-input-borderless]': 'nzBorderless', - '[class.ant-input-lg]': `nzSize === 'large'`, - '[class.ant-input-sm]': `nzSize === 'small'`, + '[class.ant-input-lg]': `finalSize() === 'large'`, + '[class.ant-input-sm]': `finalSize() === 'small'`, '[attr.disabled]': 'disabled || null', '[class.ant-input-rtl]': `dir=== 'rtl'`, '[class.ant-input-stepperless]': `nzStepperless` }, - standalone: true + standalone: true, + hostDirectives: [NzSpaceCompactItemDirective], + providers: [NzDestroyService, { provide: NZ_SPACE_COMPACT_ITEM_TYPE, useValue: 'input' }] }) -export class NzInputDirective implements OnChanges, OnInit, OnDestroy { +export class NzInputDirective implements OnChanges, OnInit { @Input({ transform: booleanAttribute }) nzBorderless = false; @Input() nzSize: NzSizeLDSType = 'default'; @Input({ transform: booleanAttribute }) nzStepperless: boolean = true; @@ -58,6 +63,7 @@ export class NzInputDirective implements OnChanges, OnInit, OnDestroy { } _disabled = false; disabled$ = new Subject(); + dir: Direction = 'ltr'; // status prefixCls: string = 'ant-input'; @@ -66,9 +72,18 @@ export class NzInputDirective implements OnChanges, OnInit, OnDestroy { hasFeedback: boolean = false; feedbackRef: ComponentRef | null = null; components: Array> = []; - private destroy$ = new Subject(); - ngControl = inject(NgControl, { self: true, optional: true }); + + protected finalSize = computed(() => { + if (this.compactSize) { + return this.compactSize(); + } + return this.size(); + }); + + private size = signal(this.nzSize); + private compactSize = inject(NZ_SPACE_COMPACT_SIZE, { optional: true }); + private destroy$ = inject(NzDestroyService); private nzFormStatusService = inject(NzFormStatusService, { optional: true }); private nzFormNoStatusService = inject(NzFormNoStatusService, { optional: true }); @@ -108,19 +123,16 @@ export class NzInputDirective implements OnChanges, OnInit, OnDestroy { }); } - ngOnChanges(changes: SimpleChanges): void { - const { disabled, nzStatus } = changes; + ngOnChanges({ disabled, nzStatus, nzSize }: SimpleChanges): void { if (disabled) { this.disabled$.next(this.disabled); } if (nzStatus) { this.setStatusStyles(this.nzStatus, this.hasFeedback); } - } - - ngOnDestroy(): void { - this.destroy$.next(); - this.destroy$.complete(); + if (nzSize) { + this.size.set(nzSize.currentValue); + } } private setStatusStyles(status: NzValidateStatus, hasFeedback: boolean): void { diff --git a/components/select/select.component.ts b/components/select/select.component.ts index bdcbff26c85..d24f5ae01a1 100644 --- a/components/select/select.component.ts +++ b/components/select/select.component.ts @@ -35,8 +35,10 @@ import { ViewChild, ViewEncapsulation, booleanAttribute, + computed, forwardRef, - inject + inject, + signal } from '@angular/core'; import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; import { BehaviorSubject, combineLatest, fromEvent, merge, of as observableOf } from 'rxjs'; @@ -52,12 +54,14 @@ import { NzDestroyService } from 'ng-zorro-antd/core/services'; import { NgClassInterface, NzSafeAny, + NzSizeLDSType, NzStatus, NzValidateStatus, OnChangeType, OnTouchedType } from 'ng-zorro-antd/core/types'; import { getStatusClassNames, isNotNil } from 'ng-zorro-antd/core/util'; +import { NZ_SPACE_COMPACT_ITEM_TYPE, NZ_SPACE_COMPACT_SIZE, NzSpaceCompactItemDirective } from 'ng-zorro-antd/space'; import { NzOptionContainerComponent } from './option-container.component'; import { NzOptionGroupComponent } from './option-group.component'; @@ -83,7 +87,7 @@ const defaultFilterOption: NzFilterOptionType = (searchValue: string, item: NzSe const NZ_CONFIG_MODULE_NAME: NzConfigKey = 'select'; -export type NzSelectSizeType = 'large' | 'default' | 'small'; +export type NzSelectSizeType = NzSizeLDSType; @Component({ selector: 'nz-select', @@ -95,7 +99,8 @@ export type NzSelectSizeType = 'large' | 'default' | 'small'; provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => NzSelectComponent), multi: true - } + }, + { provide: NZ_SPACE_COMPACT_ITEM_TYPE, useValue: 'select' } ], changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, @@ -191,8 +196,8 @@ export type NzSelectSizeType = 'large' | 'default' | 'small'; host: { class: 'ant-select', '[class.ant-select-in-form-item]': '!!nzFormStatusService', - '[class.ant-select-lg]': 'nzSize === "large"', - '[class.ant-select-sm]': 'nzSize === "small"', + '[class.ant-select-lg]': 'finalSize() === "large"', + '[class.ant-select-sm]': 'finalSize() === "small"', '[class.ant-select-show-arrow]': `nzShowArrow`, '[class.ant-select-disabled]': 'nzDisabled', '[class.ant-select-show-search]': `(nzShowSearch || nzMode !== 'default') && !nzDisabled`, @@ -204,6 +209,7 @@ export type NzSelectSizeType = 'large' | 'default' | 'small'; '[class.ant-select-multiple]': `nzMode !== 'default'`, '[class.ant-select-rtl]': `dir === 'rtl'` }, + hostDirectives: [NzSpaceCompactItemDirective], imports: [ NzSelectTopControlComponent, CdkOverlayOrigin, @@ -286,6 +292,16 @@ export class NzSelectComponent implements ControlValueAccessor, OnInit, AfterCon @ViewChild(NzOptionGroupComponent, { static: true, read: ElementRef }) nzOptionGroupComponentElement!: ElementRef; @ViewChild(NzSelectTopControlComponent, { static: true, read: ElementRef }) nzSelectTopControlComponentElement!: ElementRef; + + protected finalSize = computed(() => { + if (this.compactSize) { + return this.compactSize(); + } + return this.size(); + }); + + private size = signal(this.nzSize); + private compactSize = inject(NZ_SPACE_COMPACT_SIZE, { optional: true }); private listOfValue$ = new BehaviorSubject([]); private listOfTemplateItem$ = new BehaviorSubject([]); private listOfTagAndTemplateItem: NzSelectItemInterface[] = []; @@ -295,6 +311,7 @@ export class NzSelectComponent implements ControlValueAccessor, OnInit, AfterCon private _nzShowArrow: boolean | undefined; private requestId: number = -1; private isNzDisableFirstChange: boolean = true; + onChange: OnChangeType = () => {}; onTouched: OnTouchedType = () => {}; dropDownPosition: NzSelectPlacementType = 'bottomLeft'; @@ -632,8 +649,7 @@ export class NzSelectComponent implements ControlValueAccessor, OnInit, AfterCon this.cdr.markForCheck(); } - ngOnChanges(changes: SimpleChanges): void { - const { nzOpen, nzDisabled, nzOptions, nzStatus, nzPlacement } = changes; + ngOnChanges({ nzOpen, nzDisabled, nzOptions, nzStatus, nzPlacement, nzSize }: SimpleChanges): void { if (nzOpen) { this.onOpenChange(); } @@ -672,6 +688,9 @@ export class NzSelectComponent implements ControlValueAccessor, OnInit, AfterCon this.positions = listOfPlacement.map(e => POSITION_MAP[e as POSITION_TYPE]); } } + if (nzSize) { + this.size.set(nzSize.currentValue); + } } ngOnInit(): void { @@ -733,6 +752,7 @@ export class NzSelectComponent implements ControlValueAccessor, OnInit, AfterCon .getConfigChangeEventForComponent('select') .pipe(takeUntil(this.destroy$)) .subscribe(() => { + this.size.set(this.nzSize); this.cdr.markForCheck(); }); diff --git a/components/space/demo/compact-button-vertical.md b/components/space/demo/compact-button-vertical.md new file mode 100644 index 00000000000..c6109d8d757 --- /dev/null +++ b/components/space/demo/compact-button-vertical.md @@ -0,0 +1,14 @@ +--- +order: 8 +title: + zh-CN: 垂直方向紧凑布局 + en-US: Vertical Compact Mode +--- + +## zh-CN + +垂直方向的紧凑布局,目前仅支持 Button 组合。 + +## en-US + +Vertical Mode for Space.Compact, support Button only. diff --git a/components/space/demo/compact-button-vertical.ts b/components/space/demo/compact-button-vertical.ts new file mode 100644 index 00000000000..7e300b5369f --- /dev/null +++ b/components/space/demo/compact-button-vertical.ts @@ -0,0 +1,30 @@ +import { Component } from '@angular/core'; + +import { NzButtonModule } from 'ng-zorro-antd/button'; +import { NzSpaceModule } from 'ng-zorro-antd/space'; + +@Component({ + selector: 'nz-demo-space-compact-button-vertical', + standalone: true, + imports: [NzSpaceModule, NzButtonModule], + template: ` + + + + + + + + + + + + + + + + + + ` +}) +export class NzDemoSpaceCompactButtonVerticalComponent {} diff --git a/components/space/demo/compact-buttons.md b/components/space/demo/compact-buttons.md new file mode 100644 index 00000000000..b89cef2f2e3 --- /dev/null +++ b/components/space/demo/compact-buttons.md @@ -0,0 +1,14 @@ +--- +order: 7 +title: + zh-CN: Button 紧凑布局 + en-US: Button Compact Mode +--- + +## zh-CN + +Button 组件紧凑排列的示例。 + +## en-US + +Button component compact example. diff --git a/components/space/demo/compact-buttons.ts b/components/space/demo/compact-buttons.ts new file mode 100644 index 00000000000..af70c242cfb --- /dev/null +++ b/components/space/demo/compact-buttons.ts @@ -0,0 +1,94 @@ +import { Component } from '@angular/core'; + +import { NzButtonModule } from 'ng-zorro-antd/button'; +import { NzDropDownModule } from 'ng-zorro-antd/dropdown'; +import { NzIconModule } from 'ng-zorro-antd/icon'; +import { NzSpaceModule } from 'ng-zorro-antd/space'; +import { NzToolTipModule } from 'ng-zorro-antd/tooltip'; + +@Component({ + selector: 'nz-demo-space-compact-buttons', + standalone: true, + imports: [NzSpaceModule, NzButtonModule, NzIconModule, NzDropDownModule, NzToolTipModule], + template: ` + + + + + + + + + + + + +
+ + + + + + + + +
+ + + + + + + + + + + + + ` +}) +export class NzDemoSpaceCompactButtonsComponent {} diff --git a/components/space/demo/compact.md b/components/space/demo/compact.md new file mode 100644 index 00000000000..b187aebad51 --- /dev/null +++ b/components/space/demo/compact.md @@ -0,0 +1,14 @@ +--- +order: 6 +title: + zh-CN: 紧凑布局组合 + en-US: Compact Mode +--- + +## zh-CN + +使用 `` 让表单组件之间紧凑连接且合并边框。 + +## en-US + +Compact Mode for form component. diff --git a/components/space/demo/compact.ts b/components/space/demo/compact.ts new file mode 100644 index 00000000000..faba7b13c4b --- /dev/null +++ b/components/space/demo/compact.ts @@ -0,0 +1,261 @@ +import { Component } from '@angular/core'; +import { FormsModule } from '@angular/forms'; + +import { NzAutocompleteModule } from 'ng-zorro-antd/auto-complete'; +import { NzButtonModule } from 'ng-zorro-antd/button'; +import { NzCascaderModule } from 'ng-zorro-antd/cascader'; +import { NzDatePickerModule } from 'ng-zorro-antd/date-picker'; +import { NzIconModule } from 'ng-zorro-antd/icon'; +import { NzInputModule } from 'ng-zorro-antd/input'; +import { NzInputNumberModule } from 'ng-zorro-antd/input-number'; +import { NzSelectModule } from 'ng-zorro-antd/select'; +import { NzSpaceModule } from 'ng-zorro-antd/space'; +import { NzTimePickerModule } from 'ng-zorro-antd/time-picker'; +import { NzToolTipModule } from 'ng-zorro-antd/tooltip'; +import { NzTreeSelectModule } from 'ng-zorro-antd/tree-select'; + +@Component({ + selector: 'nz-demo-space-compact', + standalone: true, + imports: [ + NzSpaceModule, + NzButtonModule, + NzIconModule, + NzInputModule, + NzInputNumberModule, + NzSelectModule, + NzCascaderModule, + NzTreeSelectModule, + NzDatePickerModule, + NzTimePickerModule, + NzAutocompleteModule, + NzToolTipModule, + FormsModule + ], + template: ` + + + + +
+ + + + +
+ + + + +
+ + + + +
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + +
+ + + + + + + +
+ + + + + + + + +
+ + + + +
+ + + + + +
+ + + + +
+ + + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + +
+ + + + +
+ + + + + `, + styles: [ + ` + .site-input-split { + background-color: #fff; + } + + .site-input-right:not(.ant-input-rtl) { + border-left-width: 0; + } + + .site-input-right:not(.ant-input-rtl):hover, + .site-input-right:not(.ant-input-rtl):focus { + border-left-width: 1px; + } + + .site-input-right.ant-input-rtl { + border-right-width: 0; + } + + .site-input-right.ant-input-rtl:hover, + .site-input-right.ant-input-rtl:focus { + border-right-width: 1px; + } + ` + ] +}) +export class NzDemoSpaceCompactComponent { + cascaderOptions = [ + { + value: 'zhejiang', + label: 'Zhejiang', + children: [ + { + value: 'hangzhou', + label: 'Hangzhou', + children: [ + { + value: 'xihu', + label: 'West Lake' + } + ] + } + ] + }, + { + value: 'jiangsu', + label: 'Jiangsu', + children: [ + { + value: 'nanjing', + label: 'Nanjing', + children: [ + { + value: 'zhonghuamen', + label: 'Zhong Hua Men' + } + ] + } + ] + } + ]; + + nodes = [ + { + title: 'parent 1', + key: '100', + children: [ + { + title: 'parent 1-0', + key: '1001', + children: [ + { title: 'leaf 1-0-0', key: '10010', isLeaf: true }, + { title: 'leaf 1-0-1', key: '10011', isLeaf: true } + ] + }, + { + title: 'parent 1-1', + key: '1002', + children: [{ title: 'leaf 1-1-0', key: '10020', isLeaf: true }] + } + ] + } + ]; +} diff --git a/components/space/doc/index.en-US.md b/components/space/doc/index.en-US.md index e9f86205fc2..ec8f5311b82 100644 --- a/components/space/doc/index.en-US.md +++ b/components/space/doc/index.en-US.md @@ -4,6 +4,7 @@ type: Layout cols: 1 title: Space cover: https://gw.alipayobjects.com/zos/antfincdn/wc6%263gJ0Y8/Space.svg +tag: New --- Set components spacing. @@ -22,8 +23,26 @@ import { NzSpaceModule } from 'ng-zorro-antd/space'; | Property | Description | Type | Default | Global Config | | --------------- | ------------------------------------------- | -------------------------------------------- | ------------ | ------------- | -| `[nzSize]` | The space size | `'small' \| 'middle' \| 'large' \| number` | `small` | ✅ | +| `[nzSize]` | The space size | `'small' \| 'middle' \| 'large' \| number` | `small` | ✅ | | `[nzDirection]` | The space direction | `'vertical' \| 'horizontal'` | `horizontal` | | | `[nzAlign]` | Align items | `'start' \| 'end' \| 'baseline' \| 'center'` | - | | | `[nzWrap]` | Auto wrap line, when `horizontal` effective | `boolean` | `false` | | -| `[nzSplit]` | Set split | `TemplateRef` | - | | +| `[nzSplit]` | Set split | `TemplateRef \| string` | - | | + +### nz-space-compact:standalone + +Use `` when child form components are compactly connected and the border is collapsed. The supported components are: + +- Button +- Cascader +- DatePicker +- Input +- Select +- TimePicker +- TreeSelect +- +| 参数 | 说明 | 类型 | 默认值 | 支持全局配置 | +| --------------- | ------------------------------------------ | --------------------------------- | -------------- | ------------ | +| `[nzBlock]` | Option to fit width to its parent\'s width | `boolean` | `false` | | +| `[nzDirection]` | Set direction of layout | `'vertical' \| 'horizontal'` | `'horizontal'` | | +| `[nzSize]` | Set child component size | `'large' \| 'default' \| 'small'` | `'default'` | | diff --git a/components/space/doc/index.zh-CN.md b/components/space/doc/index.zh-CN.md index 39992fd2781..ac3b236ba9d 100644 --- a/components/space/doc/index.zh-CN.md +++ b/components/space/doc/index.zh-CN.md @@ -5,6 +5,7 @@ subtitle: 间距 title: Space cols: 1 cover: https://gw.alipayobjects.com/zos/antfincdn/wc6%263gJ0Y8/Space.svg +tag: New --- 设置组件之间的间距。 @@ -26,8 +27,26 @@ import { NzSpaceModule } from 'ng-zorro-antd/space'; | 参数 | 说明 | 类型 | 默认值 | 支持全局配置 | | --------------- | -------------------------------------- | -------------------------------------------- | ------------ | ------------ | -| `[nzSize]` | 间距大小 | `'small' \| 'middle' \| 'large' \| number` | `'small'` | ✅ | +| `[nzSize]` | 间距大小 | `'small' \| 'middle' \| 'large' \| number` | `'small'` | ✅ | | `[nzDirection]` | 间距方向 | `'vertical' \| 'horizontal'` | `horizontal` | | | `[nzAlign]` | 对齐方式 | `'start' \| 'end' \| 'baseline' \| 'center'` | - | | | `[nzWrap]` | 是否自动换行,仅在 `horizontal` 时有效 | `boolean` | `false` | | -| `[nzSplit]` | 设置分隔符 | `TemplateRef` | - | | +| `[nzSplit]` | 设置分隔符 | `TemplateRef \| string` | - | | + +### nz-space-compact:standalone + +需要表单组件之间紧凑连接且合并边框时,使用 ``。支持的组件有: + +- Button +- Cascader +- DatePicker +- Input +- Select +- TimePicker +- TreeSelect +- +| 参数 | 说明 | 类型 | 默认值 | 支持全局配置 | +| --------------- | ---------------------------- | --------------------------------- | -------------- | ------------ | +| `[nzBlock]` | 将宽度调整为父元素宽度的选项 | `boolean` | `false` | | +| `[nzDirection]` | 指定排列方向 | `'vertical' \| 'horizontal'` | `'horizontal'` | | +| `[nzSize]` | 子组件大小 | `'large' \| 'default' \| 'small'` | `'default'` | | diff --git a/components/space/public-api.ts b/components/space/public-api.ts index f29ed924b75..b14af3bd9b8 100644 --- a/components/space/public-api.ts +++ b/components/space/public-api.ts @@ -3,7 +3,10 @@ * found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE */ -export * from './space.module'; -export * from './space.component'; +export * from './space-compact-item.directive'; +export * from './space-compact.component'; +export * from './space-compact.token'; export * from './space-item.directive'; +export * from './space.component'; +export * from './space.module'; export * from './types'; diff --git a/components/space/space-compact-item.directive.ts b/components/space/space-compact-item.directive.ts new file mode 100644 index 00000000000..1f12762009c --- /dev/null +++ b/components/space/space-compact-item.directive.ts @@ -0,0 +1,85 @@ +/** + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE + */ + +import { afterNextRender, computed, Directive, ElementRef, inject, OnDestroy } from '@angular/core'; + +import { NzSpaceCompactComponent } from './space-compact.component'; +import { NZ_SPACE_COMPACT_ITEM_TYPE, NZ_SPACE_COMPACT_ITEMS } from './space-compact.token'; + +@Directive({ + exportAs: 'nzSpaceCompactItem', + standalone: true, + host: { + '[class]': 'class()' + } +}) +export class NzSpaceCompactItemDirective implements OnDestroy { + private readonly spaceCompactCmp = inject(NzSpaceCompactComponent, { host: true, optional: true }); + private readonly items = inject(NZ_SPACE_COMPACT_ITEMS, { host: true, optional: true }); + private readonly type = inject(NZ_SPACE_COMPACT_ITEM_TYPE); + + protected class = computed(() => { + // Only handle when the parent is space compact component + if (!this.spaceCompactCmp || !this.items) return null; + + const items = this.items(); + const direction = this.spaceCompactCmp.nzDirection(); + const classes = [compactItemClassOf(this.type, direction)]; + const index = items.indexOf(this); + const firstIndex = items.findIndex(element => element); + // Array [empty, item] + // In this case, the index of the first valid element is not 0, + // so we need to use findIndex to find the index value of the first valid element. + if (index === firstIndex) { + classes.push(compactFirstItemClassOf(this.type, direction)); + } else if (index === items.length - 1) { + classes.push(compactLastItemClassOf(this.type, direction)); + } + + return classes; + }); + + constructor() { + if (!this.spaceCompactCmp || !this.items) return; + + const { nativeElement }: ElementRef = inject(ElementRef); + + afterNextRender(() => { + if (nativeElement.parentElement) { + const index = Array.from(nativeElement.parentElement.children).indexOf(nativeElement); + this.items!.update(value => { + const newValue = value.slice(); + newValue.splice(index, 0, this); + return newValue; + }); + } + }); + } + + ngOnDestroy(): void { + this.items?.update(value => value.filter(o => o !== this)); + } +} + +function generateCompactClass( + type: string, + direction: 'vertical' | 'horizontal', + position: 'item' | 'first-item' | 'last-item' +): string { + const directionPrefix = direction === 'vertical' ? 'vertical-' : ''; + return `ant-${type}-compact-${directionPrefix}${position}`; +} + +function compactItemClassOf(type: string, direction: 'vertical' | 'horizontal'): string { + return generateCompactClass(type, direction, 'item'); +} + +function compactFirstItemClassOf(type: string, direction: 'vertical' | 'horizontal'): string { + return generateCompactClass(type, direction, 'first-item'); +} + +function compactLastItemClassOf(type: string, direction: 'vertical' | 'horizontal'): string { + return generateCompactClass(type, direction, 'last-item'); +} diff --git a/components/space/space-compact.component.spec.ts b/components/space/space-compact.component.spec.ts new file mode 100644 index 00000000000..6c384cb763d --- /dev/null +++ b/components/space/space-compact.component.spec.ts @@ -0,0 +1,251 @@ +import { Component } from '@angular/core'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { provideNoopAnimations } from '@angular/platform-browser/animations'; + +import { NzButtonModule } from 'ng-zorro-antd/button'; +import { NzCascaderModule } from 'ng-zorro-antd/cascader'; +import { NzSizeLDSType } from 'ng-zorro-antd/core/types'; +import { NzDatePickerModule } from 'ng-zorro-antd/date-picker'; +import { NzInputModule } from 'ng-zorro-antd/input'; +import { NzInputNumberModule } from 'ng-zorro-antd/input-number'; +import { NzSelectModule } from 'ng-zorro-antd/select'; +import { NzTimePickerModule } from 'ng-zorro-antd/time-picker'; +import { NzTreeSelectModule } from 'ng-zorro-antd/tree-select'; + +import { NzSpaceModule } from './space.module'; +import { NzSpaceDirection } from './types'; + +describe('Space compact', () => { + let component: SpaceCompactTestComponent; + let fixture: ComponentFixture; + + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [provideNoopAnimations()] + }).compileComponents(); + fixture = TestBed.createComponent(SpaceCompactTestComponent); + component = fixture.componentInstance; + fixture.autoDetectChanges(); + }); + + it('should render all child components', () => { + const spaceCompactElement: HTMLElement = fixture.nativeElement; + const nzInput = spaceCompactElement.querySelector('input[nz-input]'); + const nzInputGroup = spaceCompactElement.querySelector('nz-input-group'); + const nzInputNumber = spaceCompactElement.querySelector('nz-input-number'); + const nzDatePicker = spaceCompactElement.querySelector('nz-date-picker'); + const nzRangePicker = spaceCompactElement.querySelector('nz-range-picker'); + const nzTimePicker = spaceCompactElement.querySelector('nz-time-picker'); + const nzCascader = spaceCompactElement.querySelector('nz-cascader'); + const nzSelect = spaceCompactElement.querySelector('nz-select'); + const nzTreeSelect = spaceCompactElement.querySelector('nz-tree-select'); + const nzButton = spaceCompactElement.querySelector('button[nz-button]'); + + expect(nzInput).toBeTruthy(); + expect(nzInputNumber).toBeTruthy(); + expect(nzInputGroup).toBeTruthy(); + expect(nzDatePicker).toBeTruthy(); + expect(nzRangePicker).toBeTruthy(); + expect(nzTimePicker).toBeTruthy(); + expect(nzCascader).toBeTruthy(); + expect(nzSelect).toBeTruthy(); + expect(nzTreeSelect).toBeTruthy(); + expect(nzButton).toBeTruthy(); + + expect(nzInput!.classList).toContain('ant-input-compact-item'); + expect(nzInputGroup!.classList).toContain('ant-input-compact-item'); + + expect(nzInputNumber!.classList).toContain('ant-input-number-compact-item'); + + expect(nzDatePicker!.classList).toContain('ant-picker-compact-item'); + expect(nzRangePicker!.classList).toContain('ant-picker-compact-item'); + expect(nzTimePicker!.classList).toContain('ant-picker-compact-item'); + + expect(nzCascader!.classList).toContain('ant-select-compact-item'); + expect(nzSelect!.classList).toContain('ant-select-compact-item'); + expect(nzTreeSelect!.classList).toContain('ant-select-compact-item'); + + expect(nzButton!.classList).toContain('ant-btn-compact-item'); + }); + + it('should be possible to switch compact first / last classes', async () => { + const spaceCompactElement: HTMLElement = fixture.nativeElement; + const nzInput = spaceCompactElement.querySelector('input[nz-input]'); + const nzInputGroup = spaceCompactElement.querySelector('nz-input-group'); + const nzTreeSelect = spaceCompactElement.querySelector('nz-tree-select'); + const nzButton = spaceCompactElement.querySelector('button[nz-button]'); + + await Promise.resolve(); + + expect(nzInput!.classList).toContain('ant-input-compact-first-item'); + expect(nzButton!.classList).toContain('ant-btn-compact-last-item'); + expect(nzInputGroup!.classList).not.toContain('ant-input-compact-first-item'); + expect(nzTreeSelect!.classList).not.toContain('ant-select-compact-last-item'); + + component.showFirst = false; + component.showLast = false; + fixture.detectChanges(); + + await Promise.resolve(); + + expect(nzInputGroup!.classList).toContain('ant-input-compact-first-item'); + expect(nzTreeSelect!.classList).toContain('ant-select-compact-last-item'); + }); + + it('should be apply size class', () => { + const spaceCompactElement: HTMLElement = fixture.nativeElement; + const nzInput = spaceCompactElement.querySelector('input[nz-input]'); + const nzInputNumber = spaceCompactElement.querySelector('nz-input-number'); + const nzDatePicker = spaceCompactElement.querySelector('nz-date-picker'); + const nzRangePicker = spaceCompactElement.querySelector('nz-range-picker'); + const nzTimePicker = spaceCompactElement.querySelector('nz-time-picker'); + const nzCascader = spaceCompactElement.querySelector('nz-cascader'); + const nzSelect = spaceCompactElement.querySelector('nz-select'); + const nzTreeSelect = spaceCompactElement.querySelector('nz-tree-select'); + const nzButton = spaceCompactElement.querySelector('button[nz-button]'); + + component.size = 'small'; + fixture.detectChanges(); + + expect(nzInput!.classList).toContain('ant-input-sm'); + expect(nzInputNumber!.classList).toContain('ant-input-number-sm'); + expect(nzDatePicker!.classList).toContain('ant-picker-small'); + expect(nzRangePicker!.classList).toContain('ant-picker-small'); + expect(nzTimePicker!.classList).toContain('ant-picker-small'); + expect(nzCascader!.classList).toContain('ant-select-sm'); + expect(nzSelect!.classList).toContain('ant-select-sm'); + expect(nzTreeSelect!.classList).toContain('ant-select-sm'); + expect(nzButton!.classList).toContain('ant-btn-sm'); + + component.size = 'large'; + fixture.detectChanges(); + + expect(nzInput!.classList).toContain('ant-input-lg'); + expect(nzInputNumber!.classList).toContain('ant-input-number-lg'); + expect(nzDatePicker!.classList).toContain('ant-picker-large'); + expect(nzRangePicker!.classList).toContain('ant-picker-large'); + expect(nzTimePicker!.classList).toContain('ant-picker-large'); + expect(nzCascader!.classList).toContain('ant-select-lg'); + expect(nzSelect!.classList).toContain('ant-select-lg'); + expect(nzTreeSelect!.classList).toContain('ant-select-lg'); + expect(nzButton!.classList).toContain('ant-btn-lg'); + }); + + it('should apply block class when nzBlock is true', () => { + const spaceCompactElement = fixture.nativeElement; + expect(spaceCompactElement.querySelector('.ant-space-compact').classList).not.toContain('ant-space-compact-block'); + + component.block = true; + fixture.detectChanges(); + + expect(spaceCompactElement.querySelector('.ant-space-compact').classList).toContain('ant-space-compact-block'); + }); +}); + +describe('Space compact direction', () => { + let component: SpaceCompactDirectionTestComponent; + let fixture: ComponentFixture; + + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [provideNoopAnimations()] + }).compileComponents(); + fixture = TestBed.createComponent(SpaceCompactDirectionTestComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should be apply direction classes', () => { + const spaceCompactElement = fixture.nativeElement; + expect(spaceCompactElement.querySelector('.ant-space-compact').classList).not.toContain( + 'ant-space-compact-vertical' + ); + + component.direction = 'vertical'; + fixture.detectChanges(); + + expect(spaceCompactElement.querySelector('.ant-space-compact').classList).toContain('ant-space-compact-vertical'); + }); + + it('should be apply direction classes for child components', () => { + const spaceCompactElement: HTMLElement = fixture.nativeElement; + const nzButtons = spaceCompactElement.querySelectorAll('button[nz-button]'); + const [firstBtn, lastBtn] = Array.from(nzButtons); + + expect(firstBtn.classList).toContain('ant-btn-compact-item'); + expect(firstBtn.classList).toContain('ant-btn-compact-first-item'); + expect(lastBtn.classList).toContain('ant-btn-compact-item'); + expect(lastBtn.classList).toContain('ant-btn-compact-last-item'); + + expect(firstBtn.classList).not.toContain('ant-btn-compact-vertical-item'); + expect(firstBtn.classList).not.toContain('ant-btn-compact-vertical-first-item'); + expect(lastBtn.classList).not.toContain('ant-btn-compact-vertical-item'); + expect(lastBtn.classList).not.toContain('ant-btn-compact-vertical-last-item'); + + component.direction = 'vertical'; + fixture.detectChanges(); + + expect(firstBtn.classList).not.toContain('ant-btn-compact-item'); + expect(firstBtn.classList).not.toContain('ant-btn-compact-first-item'); + expect(lastBtn.classList).not.toContain('ant-btn-compact-item'); + expect(lastBtn.classList).not.toContain('ant-btn-compact-last-item'); + + expect(firstBtn.classList).toContain('ant-btn-compact-vertical-item'); + expect(firstBtn.classList).toContain('ant-btn-compact-vertical-first-item'); + expect(lastBtn.classList).toContain('ant-btn-compact-vertical-item'); + expect(lastBtn.classList).toContain('ant-btn-compact-vertical-last-item'); + }); +}); + +@Component({ + standalone: true, + imports: [ + NzSpaceModule, + NzButtonModule, + NzInputModule, + NzInputNumberModule, + NzSelectModule, + NzCascaderModule, + NzTreeSelectModule, + NzDatePickerModule, + NzTimePickerModule + ], + template: ` + + @if (showFirst) { + + } + + + + + + + + + @if (showLast) { + + } + + ` +}) +class SpaceCompactTestComponent { + block: boolean = false; + size: NzSizeLDSType = 'default'; + showFirst = true; + showLast = true; +} + +@Component({ + standalone: true, + imports: [NzSpaceModule, NzButtonModule], + template: ` + + + + + ` +}) +class SpaceCompactDirectionTestComponent { + direction: NzSpaceDirection = 'horizontal'; +} diff --git a/components/space/space-compact.component.ts b/components/space/space-compact.component.ts new file mode 100644 index 00000000000..8513e7aef56 --- /dev/null +++ b/components/space/space-compact.component.ts @@ -0,0 +1,32 @@ +/** + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE + */ + +import { booleanAttribute, ChangeDetectionStrategy, Component, inject, input, signal } from '@angular/core'; + +import { NzSizeLDSType } from 'ng-zorro-antd/core/types'; + +import { NZ_SPACE_COMPACT_ITEMS, NZ_SPACE_COMPACT_SIZE } from './space-compact.token'; + +@Component({ + selector: 'nz-space-compact', + exportAs: 'nzSpaceCompact', + standalone: true, + template: ``, + host: { + class: 'ant-space-compact', + '[class.ant-space-compact-block]': `nzBlock()`, + '[class.ant-space-compact-vertical]': `nzDirection() === 'vertical'` + }, + providers: [ + { provide: NZ_SPACE_COMPACT_SIZE, useFactory: () => inject(NzSpaceCompactComponent).nzSize }, + { provide: NZ_SPACE_COMPACT_ITEMS, useValue: signal([]) } + ], + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class NzSpaceCompactComponent { + nzBlock = input(false, { transform: booleanAttribute }); + nzDirection = input<'vertical' | 'horizontal'>('horizontal'); + nzSize = input('default'); +} diff --git a/components/space/space-compact.token.ts b/components/space/space-compact.token.ts new file mode 100644 index 00000000000..5be82d341f2 --- /dev/null +++ b/components/space/space-compact.token.ts @@ -0,0 +1,14 @@ +/** + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE + */ + +import { InjectionToken, Signal, WritableSignal } from '@angular/core'; + +import { NzSizeLDSType } from 'ng-zorro-antd/core/types'; + +import type { NzSpaceCompactItemDirective } from './space-compact-item.directive'; + +export const NZ_SPACE_COMPACT_SIZE = new InjectionToken>(''); +export const NZ_SPACE_COMPACT_ITEMS = new InjectionToken>(''); +export const NZ_SPACE_COMPACT_ITEM_TYPE = new InjectionToken(''); diff --git a/components/space/space-item.directive.ts b/components/space/space-item.directive.ts index 4b884c9d59d..3c76a917539 100644 --- a/components/space/space-item.directive.ts +++ b/components/space/space-item.directive.ts @@ -9,6 +9,4 @@ import { Directive } from '@angular/core'; selector: '[nzSpaceItem]', standalone: true }) -export class NzSpaceItemDirective { - constructor() {} -} +export class NzSpaceItemDirective {} diff --git a/components/space/space.component.ts b/components/space/space.component.ts index c905ff838c8..0ded777194c 100644 --- a/components/space/space.component.ts +++ b/components/space/space.component.ts @@ -21,6 +21,7 @@ import { Subject } from 'rxjs'; import { takeUntil } from 'rxjs/operators'; import { NzConfigKey, NzConfigService, WithConfig } from 'ng-zorro-antd/core/config'; +import { NzStringTemplateOutletDirective } from 'ng-zorro-antd/core/outlet'; import { NzSafeAny } from 'ng-zorro-antd/core/types'; import { NzSpaceItemDirective } from './space-item.directive'; @@ -37,7 +38,7 @@ const SPACE_SIZE: { @Component({ selector: 'nz-space, [nz-space]', - exportAs: 'NzSpace', + exportAs: 'nzSpace', changeDetection: ChangeDetectionStrategy.OnPush, template: ` @@ -55,7 +56,9 @@ const SPACE_SIZE: { [style.margin-bottom.px]="nzDirection === 'vertical' ? (last ? null : spaceSize) : null" [style.margin-right.px]="nzDirection === 'horizontal' ? (last ? null : spaceSize) : null" > - + {{ + nzSplit + }}
} } @@ -70,7 +73,7 @@ const SPACE_SIZE: { '[class.ant-space-align-baseline]': 'mergedAlign === "baseline"', '[style.flex-wrap]': 'nzWrap ? "wrap" : null' }, - imports: [NgTemplateOutlet], + imports: [NgTemplateOutlet, NzStringTemplateOutletDirective], standalone: true }) export class NzSpaceComponent implements OnChanges, OnDestroy, AfterContentInit { @@ -78,7 +81,7 @@ export class NzSpaceComponent implements OnChanges, OnDestroy, AfterContentInit @Input() nzDirection: NzSpaceDirection = 'horizontal'; @Input() nzAlign?: NzSpaceAlign; - @Input() nzSplit: TemplateRef<{ $implicit: number }> | null = null; + @Input() nzSplit: TemplateRef<{ $implicit: number }> | string | null = null; @Input({ transform: booleanAttribute }) nzWrap: boolean = false; @Input() @WithConfig() nzSize: NzSpaceSize = 'small'; diff --git a/components/space/space.module.ts b/components/space/space.module.ts index fe40a9ed883..de7a95c1058 100644 --- a/components/space/space.module.ts +++ b/components/space/space.module.ts @@ -5,11 +5,12 @@ import { NgModule } from '@angular/core'; +import { NzSpaceCompactComponent } from './space-compact.component'; import { NzSpaceItemDirective } from './space-item.directive'; import { NzSpaceComponent } from './space.component'; @NgModule({ - imports: [NzSpaceComponent, NzSpaceItemDirective], - exports: [NzSpaceComponent, NzSpaceItemDirective] + imports: [NzSpaceComponent, NzSpaceItemDirective, NzSpaceCompactComponent], + exports: [NzSpaceComponent, NzSpaceItemDirective, NzSpaceCompactComponent] }) export class NzSpaceModule {} diff --git a/components/time-picker/time-picker.component.ts b/components/time-picker/time-picker.component.ts index cda6fc78a5a..535f1660ba5 100644 --- a/components/time-picker/time-picker.component.ts +++ b/components/time-picker/time-picker.component.ts @@ -16,7 +16,6 @@ import { EventEmitter, Input, OnChanges, - OnDestroy, OnInit, Output, Renderer2, @@ -25,11 +24,13 @@ import { ViewChild, ViewEncapsulation, booleanAttribute, + computed, forwardRef, - inject + inject, + signal } from '@angular/core'; import { ControlValueAccessor, FormsModule, NG_VALUE_ACCESSOR } from '@angular/forms'; -import { Observable, Subject, of } from 'rxjs'; +import { Observable, of } from 'rxjs'; import { distinctUntilChanged, map, takeUntil, withLatestFrom } from 'rxjs/operators'; import { isValid } from 'date-fns'; @@ -40,10 +41,12 @@ import { NzFormNoStatusService, NzFormPatchModule, NzFormStatusService } from 'n import { warn } from 'ng-zorro-antd/core/logger'; import { NzOutletModule } from 'ng-zorro-antd/core/outlet'; import { NzOverlayModule } from 'ng-zorro-antd/core/overlay'; -import { NgClassInterface, NzSafeAny, NzStatus, NzValidateStatus } from 'ng-zorro-antd/core/types'; +import { NzDestroyService } from 'ng-zorro-antd/core/services'; +import { NgClassInterface, NzSafeAny, NzSizeLDSType, NzStatus, NzValidateStatus } from 'ng-zorro-antd/core/types'; import { getStatusClassNames, isNil } from 'ng-zorro-antd/core/util'; import { DateHelperService, NzI18nInterface, NzI18nService } from 'ng-zorro-antd/i18n'; import { NzIconModule } from 'ng-zorro-antd/icon'; +import { NZ_SPACE_COMPACT_ITEM_TYPE, NZ_SPACE_COMPACT_SIZE, NzSpaceCompactItemDirective } from 'ng-zorro-antd/space'; import { NzTimePickerPanelComponent } from './time-picker-panel.component'; @@ -136,16 +139,21 @@ const NZ_CONFIG_MODULE_NAME: NzConfigKey = 'timePicker'; `, host: { class: 'ant-picker', - '[class.ant-picker-large]': `nzSize === 'large'`, - '[class.ant-picker-small]': `nzSize === 'small'`, + '[class.ant-picker-large]': `finalSize() === 'large'`, + '[class.ant-picker-small]': `finalSize() === 'small'`, '[class.ant-picker-disabled]': `nzDisabled`, '[class.ant-picker-focused]': `focused`, '[class.ant-picker-rtl]': `dir === 'rtl'`, '[class.ant-picker-borderless]': `nzBorderless`, '(click)': 'open()' }, + hostDirectives: [NzSpaceCompactItemDirective], animations: [slideMotion], - providers: [{ provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => NzTimePickerComponent), multi: true }], + providers: [ + NzDestroyService, + { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => NzTimePickerComponent), multi: true }, + { provide: NZ_SPACE_COMPACT_ITEM_TYPE, useValue: 'picker' } + ], imports: [ AsyncPipe, FormsModule, @@ -159,12 +167,12 @@ const NZ_CONFIG_MODULE_NAME: NzConfigKey = 'timePicker'; ], standalone: true }) -export class NzTimePickerComponent implements ControlValueAccessor, OnInit, AfterViewInit, OnChanges, OnDestroy { +export class NzTimePickerComponent implements ControlValueAccessor, OnInit, AfterViewInit, OnChanges { readonly _nzModuleName: NzConfigKey = NZ_CONFIG_MODULE_NAME; private _onChange?: (value: Date | null) => void; private _onTouched?: () => void; - private destroy$ = new Subject(); + private destroy$ = inject(NzDestroyService); private isNzDisableFirstChange: boolean = true; isInit = false; focused = false; @@ -213,7 +221,7 @@ export class NzTimePickerComponent implements ControlValueAccessor, OnInit, Afte @ViewChild('inputElement', { static: true }) inputRef!: ElementRef; @Input() nzId: string | null = null; - @Input() nzSize: string | null = null; + @Input() nzSize: NzSizeLDSType = 'default'; @Input() nzStatus: NzStatus = ''; @Input() @WithConfig() nzHourStep: number = 1; @Input() @WithConfig() nzMinuteStep: number = 1; @@ -358,6 +366,15 @@ export class NzTimePickerComponent implements ControlValueAccessor, OnInit, Afte this.close(); } + protected finalSize = computed(() => { + if (this.compactSize) { + return this.compactSize(); + } + return this.size(); + }); + + private size = signal(this.nzSize); + private compactSize = inject(NZ_SPACE_COMPACT_SIZE, { optional: true }); private nzFormStatusService = inject(NzFormStatusService, { optional: true }); private nzFormNoStatusService = inject(NzFormNoStatusService, { optional: true }); @@ -399,13 +416,7 @@ export class NzTimePickerComponent implements ControlValueAccessor, OnInit, Afte }); } - ngOnDestroy(): void { - this.destroy$.next(); - this.destroy$.complete(); - } - - ngOnChanges(changes: SimpleChanges): void { - const { nzUse12Hours, nzFormat, nzDisabled, nzAutoFocus, nzStatus } = changes; + ngOnChanges({ nzUse12Hours, nzFormat, nzDisabled, nzAutoFocus, nzStatus, nzSize }: SimpleChanges): void { if (nzUse12Hours && !nzUse12Hours.previousValue && nzUse12Hours.currentValue && !nzFormat) { this.nzFormat = 'h:mm:ss a'; } @@ -424,6 +435,9 @@ export class NzTimePickerComponent implements ControlValueAccessor, OnInit, Afte if (nzStatus) { this.setStatusStyles(this.nzStatus, this.hasFeedback); } + if (nzSize) { + this.size.set(nzSize.currentValue); + } } parseTimeString(str: string): void { diff --git a/components/tree-select/tree-select.component.ts b/components/tree-select/tree-select.component.ts index 5f4274c6dd3..0c25d24c9de 100644 --- a/components/tree-select/tree-select.component.ts +++ b/components/tree-select/tree-select.component.ts @@ -30,9 +30,11 @@ import { TemplateRef, ViewChild, booleanAttribute, + computed, forwardRef, inject, - numberAttribute + numberAttribute, + signal } from '@angular/core'; import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; import { Subject, combineLatest, merge, of as observableOf } from 'rxjs'; @@ -44,6 +46,7 @@ import { NzFormNoStatusService, NzFormPatchModule, NzFormStatusService } from 'n import { NzNoAnimationDirective } from 'ng-zorro-antd/core/no-animation'; import { NzOverlayModule, POSITION_MAP } from 'ng-zorro-antd/core/overlay'; import { reqAnimFrame } from 'ng-zorro-antd/core/polyfill'; +import { NzDestroyService } from 'ng-zorro-antd/core/services'; import { NzFormatEmitEvent, NzTreeBase, @@ -63,6 +66,7 @@ import { import { getStatusClassNames, isNotNil } from 'ng-zorro-antd/core/util'; import { NzEmptyModule } from 'ng-zorro-antd/empty'; import { NzSelectModule, NzSelectSearchComponent } from 'ng-zorro-antd/select'; +import { NZ_SPACE_COMPACT_ITEM_TYPE, NZ_SPACE_COMPACT_SIZE, NzSpaceCompactItemDirective } from 'ng-zorro-antd/space'; import { NzTreeComponent, NzTreeModule } from 'ng-zorro-antd/tree'; import { NzTreeSelectService } from './tree-select.service'; @@ -80,6 +84,20 @@ const listOfPositions = [ @Component({ selector: 'nz-tree-select', exportAs: 'nzTreeSelect', + standalone: true, + imports: [ + NzOverlayModule, + CdkConnectedOverlay, + NgClass, + NzNoAnimationDirective, + NgStyle, + NzTreeModule, + NzEmptyModule, + CdkOverlayOrigin, + SlicePipe, + NzSelectModule, + NzFormPatchModule + ], animations: [slideMotion], template: ` `, providers: [ + NzDestroyService, NzTreeSelectService, + { provide: NZ_SPACE_COMPACT_ITEM_TYPE, useValue: 'select' }, { provide: NzTreeHigherOrderServiceToken, useExisting: NzTreeSelectService @@ -228,9 +248,9 @@ const listOfPositions = [ host: { class: 'ant-select ant-tree-select', '[class.ant-select-in-form-item]': '!!nzFormStatusService', - '[class.ant-select-lg]': 'nzSize==="large"', '[class.ant-select-rtl]': 'dir==="rtl"', - '[class.ant-select-sm]': 'nzSize==="small"', + '[class.ant-select-lg]': 'finalSize() === "large"', + '[class.ant-select-sm]': 'finalSize() === "small"', '[class.ant-select-disabled]': 'nzDisabled', '[class.ant-select-single]': '!isMultiple', '[class.ant-select-show-arrow]': '!isMultiple', @@ -242,20 +262,7 @@ const listOfPositions = [ '(click)': 'trigger()', '(keydown)': 'onKeydown($event)' }, - imports: [ - NzOverlayModule, - CdkConnectedOverlay, - NgClass, - NzNoAnimationDirective, - NgStyle, - NzTreeModule, - NzEmptyModule, - CdkOverlayOrigin, - SlicePipe, - NzSelectModule, - NzFormPatchModule - ], - standalone: true + hostDirectives: [NzSpaceCompactItemDirective] }) export class NzTreeSelectComponent extends NzTreeBase implements ControlValueAccessor, OnInit, OnDestroy, OnChanges { readonly _nzModuleName: NzConfigKey = NZ_CONFIG_MODULE_NAME; @@ -340,9 +347,17 @@ export class NzTreeSelectComponent extends NzTreeBase implements ControlValueAcc dir: Direction = 'ltr'; positions: ConnectionPositionPair[] = []; - private destroy$ = new Subject(); - private isNzDisableFirstChange: boolean = true; + protected finalSize = computed(() => { + if (this.compactSize) { + return this.compactSize(); + } + return this.size(); + }); + private size = signal(this.nzSize); + private compactSize = inject(NZ_SPACE_COMPACT_SIZE, { optional: true }); + private destroy$ = inject(NzDestroyService); + private isNzDisableFirstChange: boolean = true; private isComposingChange$ = new Subject(); private searchValueChange$ = new Subject(); @@ -374,6 +389,15 @@ export class NzTreeSelectComponent extends NzTreeBase implements ControlValueAcc } ngOnInit(): void { + this.size.set(this.nzSize); + this.nzConfigService + .getConfigChangeEventForComponent(NZ_CONFIG_MODULE_NAME) + .pipe(takeUntil(this.destroy$)) + .subscribe(() => { + this.size.set(this.nzSize); + this.cdr.markForCheck(); + }); + this.nzFormStatusService?.formStatusChanges .pipe( distinctUntilChanged((pre, cur) => { @@ -457,8 +481,7 @@ export class NzTreeSelectComponent extends NzTreeBase implements ControlValueAcc }); } - ngOnChanges(changes: SimpleChanges): void { - const { nzNodes, nzDropdownClassName, nzStatus, nzPlacement } = changes; + ngOnChanges({ nzNodes, nzDropdownClassName, nzStatus, nzPlacement, nzSize }: SimpleChanges): void { if (nzNodes) { this.updateSelectedNodes(true); } @@ -475,6 +498,9 @@ export class NzTreeSelectComponent extends NzTreeBase implements ControlValueAcc this.positions = [POSITION_MAP[this.nzPlacement]]; } } + if (nzSize) { + this.size.set(nzSize.currentValue); + } } writeValue(value: string[] | string): void {