From 5fe4ad7fbae9216065f2a7d8a5d02c35b57d6010 Mon Sep 17 00:00:00 2001 From: Saeed Rahimi Date: Mon, 20 Jan 2020 08:28:15 +0330 Subject: [PATCH 01/21] feat(module:nz-table): add rtl support feat(module:table): add rtl tests and some rtl fixes feat(module:grid): add rtl support feat(module:demo): add some rtl fixes to whole demo fix(module:layout): fix rtl button issue feat(module:cascader): added rtl support feat(module:carousel): added rtl direction feat(module:button): added rtl direction feat(module:layout): added rtl direction feat(module:descriptions): added rtl direction feat(module:menu): added rtl direction fix(module:cascader): fix arrow direction in RTL feat(module:breadcrumb): add rtl direction feat(module:badge): add rtl direction feat(module:comment): add rtl direction feat(module:drawer): add rtl direction feat(module:steps): add rtl direction feat(module:tag): add rtl direction feat(module:treeselect): add rtl direction feat(module:typography): add rtl direction feat(module:progress): add rtl direction feat(module:rate): add rtl direction feat(module:result): add rtl direction feat(module:statistic): add rtl direction feat(module:statistic): add rtl direction feat(module:tooltip): wip add rtl-direction feat(module:table): some rtl fixes after rebase feat(module:popover): some rtl fixes after rebase feat(module:tooltip): add rtl-direction feat(module:affix): add rtl direction feat(module:dropdown): add rtl direction feat(module:pagination): add rtl direction feat(module:select): add rtl direction fix(module:breadcrumb): float in rtl feat(module:page-header): add rtl direction feat(module:autocomplete): add rtl direction feat(module:checkbox): add rtl direction feat(module:radio): add rtl direction feat(module:switch): add rtl direction feat(module:popover): add rtl direction feat(module:datepicker): add rtl direction --- components/affix/affix.component.ts | 28 +++- .../auto-complete/autocomplete.component.ts | 20 ++- components/badge/badge.component.ts | 32 ++++- components/badge/demo/change.ts | 5 + components/breadcrumb/breadcrumb.component.ts | 26 +++- components/breadcrumb/style/patch.less | 8 ++ components/button/button-group.component.ts | 34 ++++- components/button/button.component.ts | 13 +- components/carousel/carousel.component.ts | 12 +- components/carousel/style/entry.less | 2 +- components/carousel/style/patch.less | 7 + components/carousel/typings.ts | 2 + components/cascader/cascader-li.component.ts | 18 ++- components/cascader/cascader.component.ts | 28 +++- components/cascader/cascader.spec.ts | 18 ++- .../checkbox/checkbox-group.component.ts | 28 +++- .../checkbox/checkbox-wrapper.component.ts | 40 +++++- components/comment/comment.component.ts | 57 +++++++- components/comment/demo/basic.ts | 4 + .../date-picker/date-picker.component.ts | 25 +++- .../date-picker/date-range-popup.component.ts | 4 +- components/date-picker/picker.component.ts | 19 +++ .../descriptions/descriptions.component.ts | 22 ++- components/drawer/drawer.component.ts | 26 +++- .../dropdown/dropdown-menu.component.ts | 26 +++- components/grid/col.directive.ts | 27 +++- components/grid/row.directive.ts | 26 +++- components/layout/layout.component.ts | 37 ++++- components/menu/menu-item.directive.ts | 20 ++- components/menu/menu.directive.ts | 17 ++- .../menu/submenu-inline-child.component.ts | 24 +++- .../submenu-non-inline-child.component.ts | 21 ++- components/menu/submenu-title.component.ts | 42 +++++- .../page-header/page-header.component.ts | 43 +++++- .../pagination-default.component.ts | 40 +++++- .../pagination/pagination-item.component.ts | 21 ++- .../pagination/pagination-simple.component.ts | 41 +++++- components/popconfirm/popconfirm.ts | 10 +- components/popover/popover.ts | 11 +- components/progress/progress.component.ts | 26 +++- components/radio/radio-group.component.ts | 25 +++- components/radio/radio.component.ts | 14 +- components/rate/rate.component.ts | 18 ++- components/result/result.component.ts | 34 ++++- components/select/select.component.ts | 19 ++- components/statistic/countdown.component.ts | 11 +- components/statistic/statistic.component.ts | 35 ++++- components/steps/steps.component.ts | 22 ++- components/switch/switch.component.ts | 25 +++- components/table/src/table/table.component.ts | 16 ++- components/table/src/testing/table.spec.ts | 7 + components/table/style/patch.less | 4 + components/tag/tag.component.ts | 45 +++++- components/timeline/timeline.component.ts | 13 +- components/tooltip/base.ts | 30 +++- components/tooltip/demo/template.ts | 10 +- components/tooltip/tooltip.ts | 10 +- .../tree-select/tree-select.component.ts | 26 +++- components/typography/typography.component.ts | 13 +- scripts/site/_site/doc/app/app.component.html | 128 ++++++++++-------- scripts/site/_site/doc/app/app.component.ts | 21 ++- scripts/site/_site/doc/app/app.module.ts | 17 +-- .../_site/doc/app/header/header.component.ts | 13 ++ scripts/site/_site/doc/style/common.less | 1 + scripts/site/_site/doc/style/docsearch.less | 3 + scripts/site/_site/doc/style/footer.less | 1 - scripts/site/_site/doc/style/toc.less | 8 ++ 67 files changed, 1280 insertions(+), 199 deletions(-) diff --git a/components/affix/affix.component.ts b/components/affix/affix.component.ts index a08f63ffa50..0bda8ad308f 100644 --- a/components/affix/affix.component.ts +++ b/components/affix/affix.component.ts @@ -11,6 +11,7 @@ import { DOCUMENT } from '@angular/common'; import { AfterViewInit, ChangeDetectionStrategy, + ChangeDetectorRef, Component, ElementRef, EventEmitter, @@ -19,6 +20,7 @@ import { NgZone, OnChanges, OnDestroy, + Optional, Output, Renderer2, SimpleChanges, @@ -33,6 +35,7 @@ import { getStyleAsText, InputNumber, shallowEqual } from 'ng-zorro-antd/core/ut import { fromEvent, merge, ReplaySubject, Subject, Subscription } from 'rxjs'; import { auditTime, map, takeUntil } from 'rxjs/operators'; +import { Direction, Directionality } from '@angular/cdk/bidi'; import { AffixRespondEvents } from './respond-events'; import { getTargetRect, SimpleRect } from './utils'; @@ -72,6 +75,9 @@ export class NzAffixComponent implements AfterViewInit, OnChanges, OnDestroy { @Output() readonly nzChange = new EventEmitter(); + dir: Direction; + private directionChangeSubscription: Subscription = Subscription.EMPTY; + private readonly placeholderNode: HTMLElement; private affixStyle?: NgStyleInterface; @@ -94,11 +100,21 @@ export class NzAffixComponent implements AfterViewInit, OnChanges, OnDestroy { private scrollSrv: NzScrollService, private ngZone: NgZone, private platform: Platform, - private renderer: Renderer2 + private renderer: Renderer2, + cdr: ChangeDetectorRef, + @Optional() directionality: Directionality ) { // The wrapper would stay at the original position as a placeholder. this.placeholderNode = el.nativeElement; this.document = doc; + + this.directionChangeSubscription = directionality.change.subscribe(() => { + this.dir = directionality.value; + this.updatePosition({} as Event); + cdr.detectChanges(); + }); + + this.dir = directionality.value; } ngOnChanges(changes: SimpleChanges): void { @@ -118,6 +134,7 @@ export class NzAffixComponent implements AfterViewInit, OnChanges, OnDestroy { ngOnDestroy(): void { this.removeListeners(); + this.directionChangeSubscription.unsubscribe(); } private registerListeners(): void { @@ -178,10 +195,17 @@ export class NzAffixComponent implements AfterViewInit, OnChanges, OnDestroy { this.affixStyle = affixStyle; if (fixed) { wrapEl.classList.add(NZ_AFFIX_CLS_PREFIX); + + if (this.dir === 'rtl') { + wrapEl.classList.add(`${NZ_AFFIX_CLS_PREFIX}-rtl`); + } } else { wrapEl.classList.remove(NZ_AFFIX_CLS_PREFIX); - } + if (this.dir === 'rtl') { + wrapEl.classList.remove(`${NZ_AFFIX_CLS_PREFIX}-rtl`); + } + } if ((affixStyle && !originalAffixStyle) || (!affixStyle && originalAffixStyle)) { this.nzChange.emit(fixed); } diff --git a/components/auto-complete/autocomplete.component.ts b/components/auto-complete/autocomplete.component.ts index eb69bb8f0ba..35131243df1 100644 --- a/components/auto-complete/autocomplete.component.ts +++ b/components/auto-complete/autocomplete.component.ts @@ -31,9 +31,10 @@ import { slideMotion } from 'ng-zorro-antd/core/animation'; import { NzNoAnimationDirective } from 'ng-zorro-antd/core/no-animation'; import { BooleanInput, CompareWith, NzDropDownPosition, NzSafeAny } from 'ng-zorro-antd/core/types'; import { InputBoolean } from 'ng-zorro-antd/core/util'; -import { defer, merge, Observable, Subscription } from 'rxjs'; -import { filter, switchMap, take } from 'rxjs/operators'; +import { defer, merge, Observable, Subject, Subscription } from 'rxjs'; +import { filter, switchMap, take, takeUntil } from 'rxjs/operators'; +import { Direction, Directionality } from '@angular/cdk/bidi'; import { NzAutocompleteOptionComponent, NzOptionSelectionChange } from './autocomplete-option.component'; export interface AutocompleteDataSourceItem { @@ -55,6 +56,7 @@ export type AutocompleteDataSource = Array(); /** * Options accessor, its source may be content or dataSource @@ -152,8 +156,16 @@ export class NzAutocompleteComponent implements AfterContentInit, AfterViewInit, constructor( private changeDetectorRef: ChangeDetectorRef, private ngZone: NgZone, + @Optional() directionality: Directionality, @Host() @Optional() public noAnimation?: NzNoAnimationDirective - ) {} + ) { + directionality.change.pipe(takeUntil(this.destroy$)).subscribe(() => { + this.dir = directionality.value; + changeDetectorRef.detectChanges(); + }); + + this.dir = directionality.value; + } ngAfterContentInit(): void { if (!this.nzDataSource) { @@ -171,6 +183,8 @@ export class NzAutocompleteComponent implements AfterContentInit, AfterViewInit, this.dataSourceChangeSubscription.unsubscribe(); this.selectionChangeSubscription.unsubscribe(); this.optionMouseEnterSubscription.unsubscribe(); + this.destroy$.next(); + this.destroy$.complete(); } setVisibility(): void { diff --git a/components/badge/badge.component.ts b/components/badge/badge.component.ts index f9d35ca3ca4..6d05fbe6a53 100644 --- a/components/badge/badge.component.ts +++ b/components/badge/badge.component.ts @@ -6,6 +6,7 @@ * found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE */ +import { Direction, Directionality } from '@angular/cdk/bidi'; import { ContentObserver } from '@angular/cdk/observers'; import { AfterViewInit, @@ -18,6 +19,7 @@ import { OnChanges, OnDestroy, OnInit, + Optional, Renderer2, SimpleChanges, TemplateRef, @@ -101,6 +103,8 @@ export class NzBadgeComponent implements OnInit, AfterViewInit, OnChanges, OnDes countSingleArray = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; presetColor: string | null = null; count: number = 0; + dir: Direction; + @ViewChild('contentElement', { static: false }) contentElement?: ElementRef; @Input() @InputBoolean() nzShowZero: boolean = false; @Input() @InputBoolean() nzShowDot = true; @@ -137,8 +141,20 @@ export class NzBadgeComponent implements OnInit, AfterViewInit, OnChanges, OnDes private elementRef: ElementRef, private contentObserver: ContentObserver, private cdr: ChangeDetectorRef, - private ngZone: NgZone - ) {} + private ngZone: NgZone, + @Optional() directionality: Directionality + ) { + directionality.change.pipe(takeUntil(this.destroy$)).subscribe(() => { + this.dir = directionality.value; + this.prepareBadgeForRtl(); + this.cdr.detectChanges(); + }); + + renderer.addClass(elementRef.nativeElement, 'ant-badge'); + + this.dir = directionality.value; + this.prepareBadgeForRtl(); + } ngOnInit(): void { this.generateMaxNumberArray(); @@ -179,4 +195,16 @@ export class NzBadgeComponent implements OnInit, AfterViewInit, OnChanges, OnDes this.destroy$.next(); this.destroy$.complete(); } + + private prepareBadgeForRtl(): void { + if (this.isRtlLayout) { + this.renderer.addClass(this.elementRef.nativeElement, 'ant-badge-rtl'); + } else { + this.renderer.removeClass(this.elementRef.nativeElement, 'ant-badge-rtl'); + } + } + + get isRtlLayout(): boolean { + return this.dir === 'rtl'; + } } diff --git a/components/badge/demo/change.ts b/components/badge/demo/change.ts index e033e6e9252..33be1b27abd 100644 --- a/components/badge/demo/change.ts +++ b/components/badge/demo/change.ts @@ -26,6 +26,11 @@ import { Component } from '@angular/core'; margin-right: 20px; } + nz-badge.ant-badge-rtl { + margin-right: 0; + margin-left: 20px; + } + .head-example { width: 42px; height: 42px; diff --git a/components/breadcrumb/breadcrumb.component.ts b/components/breadcrumb/breadcrumb.component.ts index 08cc3807578..69eeab1020c 100644 --- a/components/breadcrumb/breadcrumb.component.ts +++ b/components/breadcrumb/breadcrumb.component.ts @@ -6,6 +6,7 @@ * found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE */ +import { Direction, Directionality } from '@angular/cdk/bidi'; import { ChangeDetectionStrategy, ChangeDetectorRef, @@ -16,6 +17,7 @@ import { NgZone, OnDestroy, OnInit, + Optional, Renderer2, TemplateRef, ViewEncapsulation @@ -57,6 +59,7 @@ export class NzBreadCrumbComponent implements OnInit, OnDestroy { @Input() nzRouteLabel: string = 'breadcrumb'; breadcrumbs: BreadcrumbOption[] | undefined = []; + dir: Direction; private destroy$ = new Subject(); @@ -64,10 +67,20 @@ export class NzBreadCrumbComponent implements OnInit, OnDestroy { private injector: Injector, private ngZone: NgZone, private cdr: ChangeDetectorRef, - elementRef: ElementRef, - renderer: Renderer2 + private elementRef: ElementRef, + private renderer: Renderer2, + @Optional() directionality: Directionality ) { + directionality.change.pipe(takeUntil(this.destroy$)).subscribe(() => { + this.dir = directionality.value; + this.prepareComponentForRtl(); + cdr.detectChanges(); + }); + renderer.addClass(elementRef.nativeElement, 'ant-breadcrumb'); + + this.dir = directionality.value; + this.prepareComponentForRtl(); } ngOnInit(): void { @@ -136,4 +149,13 @@ export class NzBreadCrumbComponent implements OnInit, OnDestroy { } return undefined; } + + private prepareComponentForRtl(): void { + if (this.dir === "rtl") { + this.renderer.addClass(this.elementRef.nativeElement, 'ant-breadcrumb-rtl'); + } else { + this.renderer.removeClass(this.elementRef.nativeElement, 'ant-breadcrumb-rtl'); + } + } + } diff --git a/components/breadcrumb/style/patch.less b/components/breadcrumb/style/patch.less index 457dd0f36f0..8de0eb8c200 100644 --- a/components/breadcrumb/style/patch.less +++ b/components/breadcrumb/style/patch.less @@ -25,3 +25,11 @@ nz-breadcrumb-item:last-child { nz-breadcrumb-item:last-child .ant-breadcrumb-separator { display: none; } + +.@{breadcrumb-prefix-cls} { + &-rtl { + > nz-breadcrumb-item { + float: right; + } + } +} diff --git a/components/button/button-group.component.ts b/components/button/button-group.component.ts index eb3e627afc8..07d323bee70 100644 --- a/components/button/button-group.component.ts +++ b/components/button/button-group.component.ts @@ -6,7 +6,18 @@ * found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE */ -import { ChangeDetectionStrategy, Component, Input, ViewEncapsulation } from '@angular/core'; +/** + * @license + * Copyright Alibaba.com All Rights Reserved. + * + * 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 { Direction, Directionality } from '@angular/cdk/bidi'; +import { ChangeDetectionStrategy, Component, Input, OnDestroy, ViewEncapsulation } from '@angular/core'; +import { Subject } from 'rxjs'; +import { takeUntil } from 'rxjs/operators'; export type NzButtonGroupSize = 'large' | 'default' | 'small'; @@ -18,13 +29,30 @@ export type NzButtonGroupSize = 'large' | 'default' | 'small'; host: { '[class.ant-btn-group]': `true`, '[class.ant-btn-group-lg]': `nzSize === 'large'`, - '[class.ant-btn-group-sm]': `nzSize === 'small'` + '[class.ant-btn-group-sm]': `nzSize === 'small'`, + '[class.ant-btn-group-rtl]': `dir === 'rtl'` }, preserveWhitespaces: false, template: ` ` }) -export class NzButtonGroupComponent { +export class NzButtonGroupComponent implements OnDestroy { @Input() nzSize: NzButtonGroupSize = 'default'; + + dir: Direction; + + private destroy$ = new Subject(); + + constructor(directionality: Directionality) { + this.dir = directionality.value; + directionality.change.pipe(takeUntil(this.destroy$)).subscribe(() => { + this.dir = directionality.value; + }); + } + + ngOnDestroy(): void { + this.destroy$.next(); + this.destroy$.complete(); + } } diff --git a/components/button/button.component.ts b/components/button/button.component.ts index f2e86244ff0..e0e3bfbd9e3 100644 --- a/components/button/button.component.ts +++ b/components/button/button.component.ts @@ -6,6 +6,7 @@ * found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE */ +import { Direction, Directionality } from '@angular/cdk/bidi'; import { AfterContentInit, AfterViewInit, @@ -63,6 +64,7 @@ const NZ_CONFIG_COMPONENT_NAME = 'button'; '[attr.tabindex]': 'disabled ? -1 : (tabIndex === null ? null : tabIndex)', '[attr.disabled]': 'disabled || null', '(click)': 'haltDisabledEvents($event)' + '[class.ant-btn-rtl]': `dir === 'rtl'` } }) export class NzButtonComponent implements OnDestroy, OnChanges, AfterViewInit, AfterContentInit { @@ -84,6 +86,9 @@ export class NzButtonComponent implements OnDestroy, OnChanges, AfterViewInit, A @Input() nzType: NzButtonType = null; @Input() nzShape: NzButtonShape = null; @Input() @WithConfig(NZ_CONFIG_COMPONENT_NAME) nzSize: NzButtonSize = 'default'; + + dir: Direction; + private destroy$ = new Subject(); private loading$ = new Subject(); @@ -120,7 +125,8 @@ export class NzButtonComponent implements OnDestroy, OnChanges, AfterViewInit, A private elementRef: ElementRef, private cdr: ChangeDetectorRef, private renderer: Renderer2, - public nzConfigService: NzConfigService + public nzConfigService: NzConfigService, + directionality: Directionality ) { this.nzConfigService .getConfigChangeEventForComponent(NZ_CONFIG_COMPONENT_NAME) @@ -128,6 +134,11 @@ export class NzButtonComponent implements OnDestroy, OnChanges, AfterViewInit, A .subscribe(() => { this.cdr.markForCheck(); }); + + this.dir = directionality.value; + directionality.change.pipe(takeUntil(this.destroy$)).subscribe(() => { + this.dir = directionality.value; + }); } ngOnChanges(changes: SimpleChanges): void { diff --git a/components/carousel/carousel.component.ts b/components/carousel/carousel.component.ts index 1ec9208652a..2df50daed07 100644 --- a/components/carousel/carousel.component.ts +++ b/components/carousel/carousel.component.ts @@ -6,6 +6,7 @@ * found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE */ +import { Direction, Directionality } from '@angular/cdk/bidi'; import { LEFT_ARROW, RIGHT_ARROW } from '@angular/cdk/keycodes'; import { Platform } from '@angular/cdk/platform'; import { @@ -93,7 +94,8 @@ const NZ_CONFIG_COMPONENT_NAME = 'carousel'; `, host: { - '[class.ant-carousel-vertical]': 'vertical' + '[class.ant-carousel-vertical]': 'vertical', + '[class.ant-carousel-rtl]': `dir ==='rtl'` } }) export class NzCarouselComponent implements AfterContentInit, AfterViewInit, OnDestroy, OnChanges { @@ -144,6 +146,7 @@ export class NzCarouselComponent implements AfterContentInit, AfterViewInit, OnD strategy?: NzCarouselBaseStrategy; vertical = false; transitionInProgress: number | null = null; + dir: Direction; private destroy$ = new Subject(); private gestureRect: ClientRect | null = null; @@ -159,12 +162,19 @@ export class NzCarouselComponent implements AfterContentInit, AfterViewInit, OnD private readonly platform: Platform, private readonly resizeService: NzResizeService, private readonly nzDragService: NzDragService, + directionality: Directionality, @Optional() @Inject(NZ_CAROUSEL_CUSTOM_STRATEGIES) private customStrategies: NzCarouselStrategyRegistryItem[] ) { this.nzDotPosition = 'bottom'; this.renderer.addClass(elementRef.nativeElement, 'ant-carousel'); this.el = elementRef.nativeElement; + + this.dir = directionality.value; + directionality.change.pipe(takeUntil(this.destroy$)).subscribe(() => { + this.dir = directionality.value; + this.syncStrategy(); + }); } ngAfterContentInit(): void { diff --git a/components/carousel/style/entry.less b/components/carousel/style/entry.less index b05658da9a0..3e686bd526f 100644 --- a/components/carousel/style/entry.less +++ b/components/carousel/style/entry.less @@ -1,2 +1,2 @@ @import './index.less'; -@import "./patch"; \ No newline at end of file +@import './patch'; diff --git a/components/carousel/style/patch.less b/components/carousel/style/patch.less index fc517e00129..d8fa6997187 100644 --- a/components/carousel/style/patch.less +++ b/components/carousel/style/patch.less @@ -13,3 +13,10 @@ nz-carousel { .slick-track { opacity: 1; } + +.slick-list { + direction: ltr; + .slick-slide { + float: right; + } +} diff --git a/components/carousel/typings.ts b/components/carousel/typings.ts index 71aa765a1ed..ed72aaec41f 100644 --- a/components/carousel/typings.ts +++ b/components/carousel/typings.ts @@ -6,6 +6,7 @@ * found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE */ +import { Direction } from '@angular/cdk/bidi'; import { InjectionToken, QueryList } from '@angular/core'; import { NzCarouselContentDirective } from './carousel-content.directive'; import { NzCarouselBaseStrategy } from './strategies/base-strategy'; @@ -21,6 +22,7 @@ export interface NzCarouselComponentAsSource { slickListEl: HTMLElement; slickTrackEl: HTMLElement; activeIndex: number; + dir: Direction; } export interface NzCarouselStrategyRegistryItem { diff --git a/components/cascader/cascader-li.component.ts b/components/cascader/cascader-li.component.ts index 3d42824685d..0a540e6fd0a 100644 --- a/components/cascader/cascader-li.component.ts +++ b/components/cascader/cascader-li.component.ts @@ -18,6 +18,8 @@ import { } from '@angular/core'; import { NzCascaderOption } from './typings'; +// tslint:disable-next-line: ordered-imports +import { Directionality, Direction } from '@angular/cdk/bidi'; @Component({ changeDetection: ChangeDetectionStrategy.OnPush, @@ -32,7 +34,7 @@ import { NzCascaderOption } from './typings'; - + `, host: { @@ -49,9 +51,15 @@ export class NzCascaderOptionComponent { @Input() highlightText!: string; @Input() nzLabelProperty = 'label'; @Input() columnIndex!: number; + dir: Direction; - constructor(private cdr: ChangeDetectorRef, elementRef: ElementRef, renderer: Renderer2) { + constructor(private cdr: ChangeDetectorRef, elementRef: ElementRef, renderer: Renderer2, directionality: Directionality) { renderer.addClass(elementRef.nativeElement, 'ant-cascader-menu-item'); + + this.dir = directionality.value; + directionality.change.subscribe(() => { + this.dir = directionality.value; + }); } get optionLabel(): string { @@ -61,4 +69,10 @@ export class NzCascaderOptionComponent { markForCheck(): void { this.cdr.markForCheck(); } + getArrowByDirection(): string { + if (this.dir === 'rtl') { + return 'left'; + } + return 'right'; + } } diff --git a/components/cascader/cascader.component.ts b/components/cascader/cascader.component.ts index 68ab353d138..9e29659629c 100644 --- a/components/cascader/cascader.component.ts +++ b/components/cascader/cascader.component.ts @@ -6,6 +6,7 @@ * found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE */ +import { Direction, Directionality } from '@angular/cdk/bidi'; import { BACKSPACE, DOWN_ARROW, ENTER, ESCAPE, LEFT_ARROW, RIGHT_ARROW, UP_ARROW } from '@angular/cdk/keycodes'; import { CdkConnectedOverlay, ConnectedOverlayPositionChange, ConnectionPositionPair } from '@angular/cdk/overlay'; import { @@ -126,6 +127,7 @@ const defaultDisplayRender = (labels: string[]) => labels.join(' / ');
labels.join(' / '); '[class.ant-cascader-picker-disabled]': 'nzDisabled', '[class.ant-cascader-picker-open]': 'menuVisible', '[class.ant-cascader-picker-with-value]': '!!inputValue', - '[class.ant-cascader-focused]': 'isFocused' - } + '[class.ant-cascader-focused]': 'isFocused', + '[class.ant-cascader-rtl]': `dir ==='rtl'`, + '[class.ant-cascader-picker-rtl]': `dir ==='rtl'` + }, + styles: [ + ` + .ant-cascader-menus { + margin-top: 4px; + margin-bottom: 4px; + top: 100%; + left: 0; + position: relative; + width: 100%; + } + ` + ] }) export class NzCascaderComponent implements NzCascaderComponentAsSource, OnInit, OnDestroy, ControlValueAccessor { static ngAcceptInputType_nzShowInput: BooleanInput; @@ -264,6 +280,7 @@ export class NzCascaderComponent implements NzCascaderComponentAsSource, OnInit, isFocused = false; locale!: NzCascaderI18nInterface; + dir: Direction; private destroy$ = new Subject(); private inputString = ''; @@ -319,12 +336,18 @@ export class NzCascaderComponent implements NzCascaderComponentAsSource, OnInit, private cdr: ChangeDetectorRef, elementRef: ElementRef, renderer: Renderer2, + directionality: Directionality, @Host() @Optional() public noAnimation?: NzNoAnimationDirective ) { this.el = elementRef.nativeElement; this.cascaderService.withComponent(this); renderer.addClass(elementRef.nativeElement, 'ant-cascader'); renderer.addClass(elementRef.nativeElement, 'ant-cascader-picker'); + + this.dir = directionality.value; + directionality.change.pipe(takeUntil(this.destroy$)).subscribe(() => { + this.dir = directionality.value; + }); } ngOnInit(): void { @@ -700,7 +723,6 @@ export class NzCascaderComponent implements NzCascaderComponentAsSource, OnInit, this.cdr.detectChanges(); } } - /** * Reposition the cascader panel. When a menu opens, the cascader expands * and may exceed the boundary of browser's window. diff --git a/components/cascader/cascader.spec.ts b/components/cascader/cascader.spec.ts index 19f78f03dbb..7883ecbb801 100644 --- a/components/cascader/cascader.spec.ts +++ b/components/cascader/cascader.spec.ts @@ -547,11 +547,7 @@ describe('cascader', () => { expect(values![0]).toBe('zhejiang'); expect(values![1]).toBe('hangzhou'); expect(values![2]).toBe('xihu'); - control.writeValue([ - { value: 'zhejiang', text: 'Zj' }, - { value: 'hangzhou', text: 'Hz' }, - { value: 'xihu', text: 'Xh' } - ]); + control.writeValue([{ value: 'zhejiang', text: 'Zj' }, { value: 'hangzhou', text: 'Hz' }, { value: 'xihu', text: 'Xh' }]); fixture.detectChanges(); expect(control.getSubmitValue().length).toBe(3); const values2 = control.getSubmitValue(); @@ -571,11 +567,7 @@ describe('cascader', () => { expect(values3[2]).toBe('xihu'); expect(control.labelRenderText).toBe('zhejiang / hangzhou / xihu'); - control.writeValue([ - { value: 'zhejiang', label: 'ZJ' }, - { value: 'hangzhou', label: 'HZ' }, - { value: 'xihu', label: 'XH' } - ]); // so these values are not match + control.writeValue([{ value: 'zhejiang', label: 'ZJ' }, { value: 'hangzhou', label: 'HZ' }, { value: 'xihu', label: 'XH' }]); // so these values are not match fixture.detectChanges(); expect(control.getSubmitValue().length).toBe(3); const values4 = control.getSubmitValue(); @@ -1570,6 +1562,12 @@ describe('cascader', () => { itemEl1 = getItemAtColumnAndRow(1, 1)!; expect(itemEl1.innerText).toBe('Option1 / Option11'); }); + + it('#RTL', fakeAsync(() => { + document.body.setAttribute('dir', 'rtl'); + fixture.detectChanges(); + expect(cascader.nativeElement.classList).not.toContain('ant-cascader-rtl'); + })); }); describe('load data lazily', () => { diff --git a/components/checkbox/checkbox-group.component.ts b/components/checkbox/checkbox-group.component.ts index 7229a05719e..6d17751ad25 100644 --- a/components/checkbox/checkbox-group.component.ts +++ b/components/checkbox/checkbox-group.component.ts @@ -7,10 +7,13 @@ */ import { FocusMonitor } from '@angular/cdk/a11y'; -import { ChangeDetectorRef, Component, ElementRef, forwardRef, Input, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core'; +import { Direction, Directionality } from '@angular/cdk/bidi'; +import { ChangeDetectorRef, Component, ElementRef, forwardRef, Input, OnDestroy, OnInit, Optional, ViewEncapsulation } from '@angular/core'; import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; import { BooleanInput, OnChangeType, OnTouchedType } from 'ng-zorro-antd/core/types'; import { InputBoolean } from 'ng-zorro-antd/core/util'; +import { Subject } from 'rxjs'; +import { takeUntil } from 'rxjs/operators'; export interface NzCheckBoxOptionInterface { label: string; @@ -44,7 +47,8 @@ export interface NzCheckBoxOptionInterface { } ], host: { - '[class.ant-checkbox-group]': 'true' + '[class.ant-checkbox-group]': 'true', + '[class.ant-checkbox-group-rtl]': `dir === 'rtl'` } }) export class NzCheckboxGroupComponent implements ControlValueAccessor, OnInit, OnDestroy { @@ -55,6 +59,10 @@ export class NzCheckboxGroupComponent implements ControlValueAccessor, OnInit, O options: NzCheckBoxOptionInterface[] = []; @Input() @InputBoolean() nzDisabled = false; + dir: Direction; + + private destroy$ = new Subject(); + trackByOption(_: number, option: NzCheckBoxOptionInterface): string { return option.value; } @@ -64,7 +72,19 @@ export class NzCheckboxGroupComponent implements ControlValueAccessor, OnInit, O this.onChange(this.options); } - constructor(private elementRef: ElementRef, private focusMonitor: FocusMonitor, private cdr: ChangeDetectorRef) {} + constructor( + private elementRef: ElementRef, + private focusMonitor: FocusMonitor, + private cdr: ChangeDetectorRef, + @Optional() directionality: Directionality + ) { + directionality.change.pipe(takeUntil(this.destroy$)).subscribe(() => { + this.dir = directionality.value; + cdr.detectChanges(); + }); + + this.dir = directionality.value; + } ngOnInit(): void { this.focusMonitor.monitor(this.elementRef, true).subscribe(focusOrigin => { @@ -76,6 +96,8 @@ export class NzCheckboxGroupComponent implements ControlValueAccessor, OnInit, O ngOnDestroy(): void { this.focusMonitor.stopMonitoring(this.elementRef); + this.destroy$.next(); + this.destroy$.complete(); } writeValue(value: NzCheckBoxOptionInterface[]): void { diff --git a/components/checkbox/checkbox-wrapper.component.ts b/components/checkbox/checkbox-wrapper.component.ts index f4d5f40b0ac..2ca6712f7bd 100644 --- a/components/checkbox/checkbox-wrapper.component.ts +++ b/components/checkbox/checkbox-wrapper.component.ts @@ -6,8 +6,20 @@ * found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE */ -import { ChangeDetectionStrategy, Component, ElementRef, EventEmitter, Output, Renderer2, ViewEncapsulation } from '@angular/core'; +import { Direction, Directionality } from '@angular/cdk/bidi'; +import { + ChangeDetectionStrategy, + ChangeDetectorRef, + Component, + EventEmitter, + OnDestroy, + Optional, + Output, + ViewEncapsulation +} from '@angular/core'; import { NzSafeAny } from 'ng-zorro-antd/core/types'; +import { Subject } from 'rxjs'; +import { takeUntil } from 'rxjs/operators'; import { NzCheckboxComponent } from './checkbox.component'; @Component({ @@ -18,12 +30,20 @@ import { NzCheckboxComponent } from './checkbox.component'; encapsulation: ViewEncapsulation.None, template: ` - ` + `, + host: { + '[class.ant-checkbox-group]': 'true', + '[class.ant-checkbox-group-rtl]': `dir === 'rtl'` + } }) -export class NzCheckboxWrapperComponent { +export class NzCheckboxWrapperComponent implements OnDestroy { @Output() readonly nzOnChange = new EventEmitter(); private checkboxList: NzCheckboxComponent[] = []; + dir: Direction; + + private destroy$ = new Subject(); + addCheckbox(value: NzCheckboxComponent): void { this.checkboxList.push(value); } @@ -37,7 +57,17 @@ export class NzCheckboxWrapperComponent { this.nzOnChange.emit(listOfCheckedValue); } - constructor(renderer: Renderer2, elementRef: ElementRef) { - renderer.addClass(elementRef.nativeElement, 'ant-checkbox-group'); + ngOnDestroy(): void { + this.destroy$.next(); + this.destroy$.complete(); + } + + constructor(cdr: ChangeDetectorRef, @Optional() directionality: Directionality) { + directionality.change.pipe(takeUntil(this.destroy$)).subscribe(() => { + this.dir = directionality.value; + cdr.detectChanges(); + }); + + this.dir = directionality.value; } } diff --git a/components/comment/comment.component.ts b/components/comment/comment.component.ts index 7ce883efb46..2f9b10c0ee8 100644 --- a/components/comment/comment.component.ts +++ b/components/comment/comment.component.ts @@ -6,8 +6,24 @@ * found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE */ -import { ChangeDetectionStrategy, Component, ContentChildren, Input, QueryList, TemplateRef, ViewEncapsulation } from '@angular/core'; +import { + ChangeDetectionStrategy, + ChangeDetectorRef, + Component, + ContentChildren, + ElementRef, + Input, + OnDestroy, + Optional, + QueryList, + Renderer2, + TemplateRef, + ViewEncapsulation +} from '@angular/core'; +import { Direction, Directionality } from '@angular/cdk/bidi'; +import { Subject } from 'rxjs'; +import { takeUntil } from 'rxjs/operators'; import { NzCommentActionComponent as CommentAction } from './comment-cells'; @Component({ @@ -42,13 +58,46 @@ import { NzCommentActionComponent as CommentAction } from './comment-cells'; encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, host: { - class: 'ant-comment' + '[class.ant-comment]': `true`, + '[class.ant-comment-rtl]': `dir === "rtl"` } }) -export class NzCommentComponent { +export class NzCommentComponent implements OnDestroy { @Input() nzAuthor?: string | TemplateRef; @Input() nzDatetime?: string | TemplateRef; + dir: Direction; + + private destroy$ = new Subject(); @ContentChildren(CommentAction) actions!: QueryList; - constructor() {} + constructor( + cdr: ChangeDetectorRef, + private elementRef: ElementRef, + private renderer: Renderer2, + @Optional() directionality: Directionality + ) { + directionality.change.pipe(takeUntil(this.destroy$)).subscribe(() => { + this.dir = directionality.value; + this.prepareComponentForRtl(); + cdr.detectChanges(); + }); + + renderer.addClass(elementRef.nativeElement, 'ant-comment'); + + this.dir = directionality.value; + this.prepareComponentForRtl(); + } + + ngOnDestroy(): void { + this.destroy$.next(); + this.destroy$.complete(); + } + + private prepareComponentForRtl(): void { + if (this.dir === 'rtl') { + this.renderer.addClass(this.elementRef.nativeElement, 'ant-comment-rtl'); + } else { + this.renderer.removeClass(this.elementRef.nativeElement, 'ant-comment-rtl'); + } + } } diff --git a/components/comment/demo/basic.ts b/components/comment/demo/basic.ts index 8b6e8a38691..fc263df432e 100644 --- a/components/comment/demo/basic.ts +++ b/components/comment/demo/basic.ts @@ -29,6 +29,10 @@ import { formatDistance } from 'date-fns'; padding-left: 8px; cursor: auto; } + .ant-comment-rtl .count { + padding-right: 8px; + padding-left: 0; + } ` ] }) diff --git a/components/date-picker/date-picker.component.ts b/components/date-picker/date-picker.component.ts index 2afe528a1da..09dfb3aec61 100644 --- a/components/date-picker/date-picker.component.ts +++ b/components/date-picker/date-picker.component.ts @@ -38,6 +38,7 @@ import { takeUntil } from 'rxjs/operators'; import { DatePickerService } from './date-picker.service'; import { NzConfigService, WithConfig } from 'ng-zorro-antd/core/config'; +import { Direction, Directionality } from '@angular/cdk/bidi'; import { NzPickerComponent } from './picker.component'; import { CompatibleDate, DisabledTimeFn, PanelMode, PresetRanges, SupportTimeOptions } from './standard-types'; @@ -125,6 +126,7 @@ export class NzDatePickerComponent implements OnInit, OnChanges, OnDestroy, Cont focused: boolean = false; extraFooter?: TemplateRef | string; hostClassMap: NgClassInterface = {}; + dir: Direction; protected destroyed$: Subject = new Subject(); protected isCustomPlaceHolder: boolean = false; @@ -179,6 +181,18 @@ export class NzDatePickerComponent implements OnInit, OnChanges, OnDestroy, Cont return this.picker.animationOpenState; } // Use picker's real open state to let re-render the picker's content when shown up + updateHostClass(): void { + this.hostClassMap = { + [`ant-picker`]: true, + [`ant-picker-range`]: this.isRange, + [`ant-picker-large`]: this.nzSize === 'large', + [`ant-picker-small`]: this.nzSize === 'small', + [`ant-picker-focused`]: this.focused, + [`ant-picker-disabled`]: this.nzDisabled, + [`ant-picker-rtl`]: this.dir === 'rtl' + }; + } + constructor( public nzConfigService: NzConfigService, public datePickerService: DatePickerService, @@ -187,8 +201,17 @@ export class NzDatePickerComponent implements OnInit, OnChanges, OnDestroy, Cont private renderer: Renderer2, private elementRef: ElementRef, protected dateHelper: DateHelperService, + @Optional() directionality: Directionality, @Host() @Optional() public noAnimation?: NzNoAnimationDirective - ) {} + ) { + directionality.change.pipe(takeUntil(this.destroyed$)).subscribe(() => { + this.dir = directionality.value; + this.updateHostClass(); + cdr.detectChanges(); + }); + this.dir = directionality.value; + this.updateHostClass(); + } ngOnInit(): void { // Subscribe the every locale change if the nzLocale is not handled by user diff --git a/components/date-picker/date-range-popup.component.ts b/components/date-picker/date-range-popup.component.ts index 5013d18e8f7..825bad65283 100644 --- a/components/date-picker/date-range-popup.component.ts +++ b/components/date-picker/date-range-popup.component.ts @@ -22,6 +22,7 @@ import { } from '@angular/core'; import { CandyDate, cloneDate, CompatibleValue, SingleValue, sortRangeValue } from 'ng-zorro-antd/core/time'; +import { Direction } from '@angular/cdk/bidi'; import { FunctionProp } from 'ng-zorro-antd/core/types'; import { NzCalendarI18nInterface } from 'ng-zorro-antd/i18n'; import { Subject } from 'rxjs'; @@ -113,7 +114,7 @@ import { getTimeConfig, isAllowedDate, PREFIX_CLASS } from './util'; -
+
@@ -150,6 +151,7 @@ export class DateRangePopupComponent implements OnInit, OnChanges, OnDestroy { @Output() readonly panelModeChange = new EventEmitter(); @Output() readonly calendarChange = new EventEmitter(); @Output() readonly resultOk = new EventEmitter(); // Emitted when done with date selecting + @Input() dir: Direction; prefixCls: string = PREFIX_CLASS; endPanelMode: PanelMode | PanelMode[] = 'date'; diff --git a/components/date-picker/picker.component.ts b/components/date-picker/picker.component.ts index 876b28ce891..ee981fb9860 100644 --- a/components/date-picker/picker.component.ts +++ b/components/date-picker/picker.component.ts @@ -38,6 +38,7 @@ import { } from '@angular/core'; import { slideMotion } from 'ng-zorro-antd/core/animation'; +import { Direction } from '@angular/cdk/bidi'; import { CandyDate, CompatibleValue } from 'ng-zorro-antd/core/time'; import { NgStyleInterface, NzSafeAny } from 'ng-zorro-antd/core/types'; import { DateHelperService } from 'ng-zorro-antd/i18n'; @@ -169,6 +170,7 @@ export class NzPickerComponent implements OnInit, AfterViewInit, OnChanges, OnDe @Input() popupStyle: NgStyleInterface | null = null; @Input() dropdownClassName?: string; @Input() suffixIcon?: string | TemplateRef; + @Input() dir: Direction; @Output() readonly focusChange = new EventEmitter(); @Output() readonly valueChange = new EventEmitter(); @@ -280,6 +282,23 @@ export class NzPickerComponent implements OnInit, AfterViewInit, OnChanges, OnDe this.panel?.cdr.markForCheck(); this.changeDetector.markForCheck(); }); + + if (this.dir === 'rtl') { + this.datePickerService.arrowPositionStyle = { + right: this.datePickerService.activeInput === 'right' ? `${arrowLeft}px` : '10px', + left: 'auto' + }; + } else { + this.datePickerService.arrowPositionStyle = { + left: this.datePickerService.activeInput === 'left' ? '0px' : `${arrowLeft}px` + }; + } + this.activeBarStyle = { + ...this.activeBarStyle, + ...this.datePickerService.arrowPositionStyle, + width: `${inputWidth}px` + }; + this.changeDetector.markForCheck(); } ngOnDestroy(): void { diff --git a/components/descriptions/descriptions.component.ts b/components/descriptions/descriptions.component.ts index 3a4751d8714..751e5a95ce8 100644 --- a/components/descriptions/descriptions.component.ts +++ b/components/descriptions/descriptions.component.ts @@ -6,6 +6,7 @@ * found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE */ +import { Direction, Directionality } from '@angular/cdk/bidi'; import { AfterContentInit, ChangeDetectionStrategy, @@ -141,7 +142,8 @@ const defaultColumnMap: { [key in NzBreakpointEnum]: number } = { class: 'ant-descriptions', '[class.ant-descriptions-bordered]': 'nzBordered', '[class.ant-descriptions-middle]': 'nzSize === "middle"', - '[class.ant-descriptions-small]': 'nzSize === "small"' + '[class.ant-descriptions-small]': 'nzSize === "small"', + '[class.ant-descriptions-rtl]': 'dir === "rtl"' } }) export class NzDescriptionsComponent implements OnChanges, OnDestroy, AfterContentInit { @@ -159,11 +161,22 @@ export class NzDescriptionsComponent implements OnChanges, OnDestroy, AfterConte itemMatrix: NzDescriptionsItemRenderProps[][] = []; realColumn = 3; + dir: Direction; private breakpoint: NzBreakpointEnum = NzBreakpointEnum.md; private destroy$ = new Subject(); - constructor(public nzConfigService: NzConfigService, private cdr: ChangeDetectorRef, private breakpointService: NzBreakpointService) {} + constructor( + public nzConfigService: NzConfigService, + private cdr: ChangeDetectorRef, + private breakpointService: NzBreakpointService, + directionality: Directionality) { + + this.dir = directionality.value; + directionality.change.pipe(takeUntil(this.destroy$)).subscribe(() => { + this.dir = directionality.value; + }); + } ngOnChanges(changes: SimpleChanges): void { if (changes.nzColumn) { @@ -172,7 +185,10 @@ export class NzDescriptionsComponent implements OnChanges, OnDestroy, AfterConte } ngAfterContentInit(): void { - const contentChange$ = this.items.changes.pipe(startWith(this.items), takeUntil(this.destroy$)); + const contentChange$ = this.items.changes.pipe( + startWith(this.items), + takeUntil(this.destroy$) + ); merge( contentChange$, diff --git a/components/drawer/drawer.component.ts b/components/drawer/drawer.component.ts index 1d6154a3490..7e4848cd740 100644 --- a/components/drawer/drawer.component.ts +++ b/components/drawer/drawer.component.ts @@ -7,6 +7,7 @@ */ import { ConfigurableFocusTrapFactory, FocusTrap } from '@angular/cdk/a11y'; +import { Direction, Directionality } from '@angular/cdk/bidi'; import { ESCAPE } from '@angular/cdk/keycodes'; import { Overlay, OverlayConfig, OverlayKeyboardDispatcher, OverlayRef } from '@angular/cdk/overlay'; import { CdkPortalOutlet, ComponentPortal, PortalInjector, TemplatePortal } from '@angular/cdk/portal'; @@ -16,6 +17,7 @@ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, + ElementRef, EventEmitter, Inject, Injector, @@ -53,6 +55,7 @@ const NZ_CONFIG_COMPONENT_NAME = 'drawer';
exte @Input() nzZIndex = 1000; @Input() nzOffsetX = 0; @Input() nzOffsetY = 0; + dir: Direction; @Input() set nzVisible(value: boolean) { @@ -213,17 +217,29 @@ export class NzDrawerComponent exte } constructor( + cdr: ChangeDetectorRef, + // tslint:disable-next-line:no-any @Optional() @Inject(DOCUMENT) private document: NzSafeAny, public nzConfigService: NzConfigService, private renderer: Renderer2, + private elementRef: ElementRef, private overlay: Overlay, private injector: Injector, private changeDetectorRef: ChangeDetectorRef, private focusTrapFactory: ConfigurableFocusTrapFactory, private viewContainerRef: ViewContainerRef, - private overlayKeyboardDispatcher: OverlayKeyboardDispatcher + private overlayKeyboardDispatcher: OverlayKeyboardDispatcher, + @Optional() directionality: Directionality ) { super(); + + directionality.change.pipe(takeUntil(this.destroy$)).subscribe(() => { + this.dir = directionality.value; + this.prepareComponentForRtl(); + cdr.detectChanges(); + }); + + this.dir = directionality.value; } ngOnInit(): void { @@ -408,4 +424,12 @@ export class NzDrawerComponent exte this.focusTrap.destroy(); } } + + private prepareComponentForRtl(): void { + if (this.dir === 'rtl') { + this.renderer.addClass(this.elementRef.nativeElement, 'ant-drawer-rtl'); + } else { + this.renderer.removeClass(this.elementRef.nativeElement, 'ant-drawer-rtl'); + } + } } diff --git a/components/dropdown/dropdown-menu.component.ts b/components/dropdown/dropdown-menu.component.ts index 4b0878b88d3..2a0d7503b6a 100644 --- a/components/dropdown/dropdown-menu.component.ts +++ b/components/dropdown/dropdown-menu.component.ts @@ -6,6 +6,7 @@ * found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE */ +import { Direction, Directionality } from '@angular/cdk/bidi'; import { AfterContentInit, ChangeDetectionStrategy, @@ -13,6 +14,7 @@ import { Component, ElementRef, Host, + OnDestroy, Optional, Renderer2, TemplateRef, @@ -24,7 +26,8 @@ import { slideMotion } from 'ng-zorro-antd/core/animation'; import { NzNoAnimationDirective } from 'ng-zorro-antd/core/no-animation'; import { IndexableObject, NzSafeAny } from 'ng-zorro-antd/core/types'; import { MenuService, NzIsMenuInsideDropDownToken } from 'ng-zorro-antd/menu'; -import { BehaviorSubject } from 'rxjs'; +import { BehaviorSubject, Subject } from 'rxjs'; +import { takeUntil } from 'rxjs/operators'; export type NzPlacementType = 'bottomLeft' | 'bottomCenter' | 'bottomRight' | 'topLeft' | 'topCenter' | 'topRight'; @@ -45,6 +48,7 @@ export type NzPlacementType = 'bottomLeft' | 'bottomCenter' | 'bottomRight' | 't
(false); isChildSubMenuOpen$ = this.nzMenuService.isChildSubMenuOpen$; @@ -69,6 +73,9 @@ export class NzDropdownMenuComponent implements AfterContentInit { nzOverlayStyle: IndexableObject = {}; @ViewChild(TemplateRef, { static: true }) templateRef!: TemplateRef; + dir: Direction; + private destroy$ = new Subject(); + setMouseState(visible: boolean): void { this.mouseState$.next(visible); } @@ -84,10 +91,23 @@ export class NzDropdownMenuComponent implements AfterContentInit { private renderer: Renderer2, public viewContainerRef: ViewContainerRef, public nzMenuService: MenuService, + @Optional() directionality: Directionality, @Host() @Optional() public noAnimation?: NzNoAnimationDirective - ) {} + ) { + directionality.change.pipe(takeUntil(this.destroy$)).subscribe(() => { + this.dir = directionality.value; + cdr.detectChanges(); + }); + + this.dir = directionality.value; + } ngAfterContentInit(): void { this.renderer.removeChild(this.renderer.parentNode(this.elementRef.nativeElement), this.elementRef.nativeElement); } + + ngOnDestroy(): void { + this.destroy$.next(); + this.destroy$.complete(); + } } diff --git a/components/grid/col.directive.ts b/components/grid/col.directive.ts index cb93a315be0..e2eaee7ff26 100644 --- a/components/grid/col.directive.ts +++ b/components/grid/col.directive.ts @@ -6,6 +6,15 @@ * found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE */ +/** + * @license + * Copyright Alibaba.com All Rights Reserved. + * + * 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 { Direction, Directionality } from '@angular/cdk/bidi'; import { AfterViewInit, Directive, @@ -44,6 +53,7 @@ export class NzColDirective implements OnInit, OnChanges, AfterViewInit, OnDestr private classMap: { [key: string]: boolean } = {}; private destroy$ = new Subject(); hostFlexStyle: string | null = null; + dir: Direction; @Input() nzFlex: string | number | null = null; @Input() nzSpan: number | null = null; @Input() nzOrder: number | null = null; @@ -65,6 +75,7 @@ export class NzColDirective implements OnInit, OnChanges, AfterViewInit, OnDestr [`ant-col-offset-${this.nzOffset}`]: isNotNil(this.nzOffset), [`ant-col-pull-${this.nzPull}`]: isNotNil(this.nzPull), [`ant-col-push-${this.nzPush}`]: isNotNil(this.nzPush), + ['ant-col-rtl']: this.isRtlLayout, ...this.generateClass() }; for (const i in this.classMap) { @@ -116,7 +127,18 @@ export class NzColDirective implements OnInit, OnChanges, AfterViewInit, OnDestr return listClassMap; } - constructor(private elementRef: ElementRef, @Optional() @Host() public nzRowDirective: NzRowDirective, public renderer: Renderer2) {} + constructor( + private elementRef: ElementRef, + @Optional() @Host() public nzRowDirective: NzRowDirective, + public renderer: Renderer2, + @Optional() directionality: Directionality + ) { + this.dir = directionality.value; + directionality.change.subscribe(() => { + this.dir = directionality.value; + this.setHostClassMap(); + }); + } ngOnInit(): void { this.setHostClassMap(); @@ -154,4 +176,7 @@ export class NzColDirective implements OnInit, OnChanges, AfterViewInit, OnDestr this.destroy$.next(); this.destroy$.complete(); } + get isRtlLayout(): boolean { + return this.dir === 'rtl'; + } } diff --git a/components/grid/row.directive.ts b/components/grid/row.directive.ts index a90105444f4..5d397ecb295 100644 --- a/components/grid/row.directive.ts +++ b/components/grid/row.directive.ts @@ -6,9 +6,18 @@ * found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE */ +/** + * @license + * Copyright Alibaba.com All Rights Reserved. + * + * 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 { Direction, Directionality } from '@angular/cdk/bidi'; import { MediaMatcher } from '@angular/cdk/layout'; import { Platform } from '@angular/cdk/platform'; -import { AfterViewInit, Directive, ElementRef, Input, NgZone, OnChanges, OnDestroy, OnInit, Renderer2, SimpleChanges } from '@angular/core'; +import { AfterViewInit, Directive, ElementRef, Input, NgZone, OnChanges, OnDestroy, OnInit, Optional, Renderer2, SimpleChanges } from '@angular/core'; import { gridResponsiveMap, NzBreakpointKey, NzBreakpointService } from 'ng-zorro-antd/core/services'; import { IndexableObject } from 'ng-zorro-antd/core/types'; import { ReplaySubject, Subject } from 'rxjs'; @@ -29,7 +38,8 @@ export type NzAlign = 'top' | 'middle' | 'bottom'; '[class.ant-row-end]': `nzJustify === 'end'`, '[class.ant-row-center]': `nzJustify === 'center'`, '[class.ant-row-space-around]': `nzJustify === 'space-around'`, - '[class.ant-row-space-between]': `nzJustify === 'space-between'` + '[class.ant-row-space-between]': `nzJustify === 'space-between'`, + '[class.ant-row-rtl]': `dir === "rtl"` } }) export class NzRowDirective implements OnInit, OnChanges, AfterViewInit, OnDestroy { @@ -43,6 +53,7 @@ export class NzRowDirective implements OnInit, OnChanges, AfterViewInit, OnDestr readonly actualGutter$ = new ReplaySubject<[number, number]>(1); + dir: Direction; private readonly destroy$ = new Subject(); getGutter(): [number, number] { @@ -81,15 +92,20 @@ export class NzRowDirective implements OnInit, OnChanges, AfterViewInit, OnDestr renderGutter('margin-bottom', verticalGutter); } } - constructor( public elementRef: ElementRef, public renderer: Renderer2, public mediaMatcher: MediaMatcher, public ngZone: NgZone, public platform: Platform, - private breakpointService: NzBreakpointService - ) {} + private breakpointService: NzBreakpointService, + @Optional() directionality: Directionality + ) { + this.dir = directionality.value; + directionality.change.pipe(takeUntil(this.destroy$)).subscribe(() => { + this.dir = directionality.value; + }); + } ngOnInit(): void { this.setGutterStyle(); diff --git a/components/layout/layout.component.ts b/components/layout/layout.component.ts index ee2d0783204..5bca3149718 100644 --- a/components/layout/layout.component.ts +++ b/components/layout/layout.component.ts @@ -6,7 +6,18 @@ * found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE */ -import { ChangeDetectionStrategy, Component, ContentChildren, QueryList, ViewEncapsulation } from '@angular/core'; +/** + * @license + * Copyright Alibaba.com All Rights Reserved. + * + * 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 { Direction, Directionality } from '@angular/cdk/bidi'; +import { ChangeDetectionStrategy, Component, ContentChildren, OnDestroy, QueryList, ViewEncapsulation } from '@angular/core'; +import { Subject } from 'rxjs'; +import { takeUntil } from 'rxjs/operators'; import { NzSiderComponent } from './sider.component'; @Component({ @@ -15,14 +26,28 @@ import { NzSiderComponent } from './sider.component'; encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, preserveWhitespaces: false, - template: ` - - `, + template: ` `, host: { '[class.ant-layout-has-sider]': 'listOfNzSiderComponent.length > 0', - '[class.ant-layout]': 'true' + '[class.ant-layout]': 'true', + '[class.ant-layout-rtl]': `dir === 'rtl'` } }) -export class NzLayoutComponent { +export class NzLayoutComponent implements OnDestroy { @ContentChildren(NzSiderComponent) listOfNzSiderComponent!: QueryList; + + dir: Direction; + private destroy$ = new Subject(); + + constructor(directionality: Directionality) { + this.dir = directionality.value; + directionality.change.pipe(takeUntil(this.destroy$)).subscribe(() => { + this.dir = directionality.value; + }); + } + + ngOnDestroy(): void { + this.destroy$.next(); + this.destroy$.complete(); + } } diff --git a/components/menu/menu-item.directive.ts b/components/menu/menu-item.directive.ts index 9601481c4c5..bd185f55593 100644 --- a/components/menu/menu-item.directive.ts +++ b/components/menu/menu-item.directive.ts @@ -6,6 +6,15 @@ * found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE */ +/** + * @license + * Copyright Alibaba.com All Rights Reserved. + * + * 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 { Direction, Directionality } from '@angular/cdk/bidi'; import { AfterContentInit, ChangeDetectorRef, @@ -39,7 +48,8 @@ import { NzSubmenuService } from './submenu.service'; '[class.ant-menu-item]': `!isMenuInsideDropDown`, '[class.ant-menu-item-selected]': `!isMenuInsideDropDown && nzSelected`, '[class.ant-menu-item-disabled]': `!isMenuInsideDropDown && nzDisabled`, - '[style.paddingLeft.px]': 'nzPaddingLeft || inlinePaddingLeft', + '[style.paddingLeft.px]': `dir === 'rtl' ? 0 : nzPaddingLeft || inlinePaddingLeft`, + '[style.paddingRight.px]': `dir === 'rtl' ? nzPaddingLeft || inlinePaddingLeft : 0`, '(click)': 'clickMenuItem($event)' } }) @@ -53,6 +63,8 @@ export class NzMenuItemDirective implements OnInit, OnChanges, OnDestroy, AfterC level = this.nzSubmenuService ? this.nzSubmenuService.level + 1 : 1; selected$ = new Subject(); inlinePaddingLeft: number | null = null; + dir: Direction; + @Input() nzPaddingLeft?: number; @Input() @InputBoolean() nzDisabled = false; @Input() @InputBoolean() nzSelected = false; @@ -116,6 +128,7 @@ export class NzMenuItemDirective implements OnInit, OnChanges, OnDestroy, AfterC private cdr: ChangeDetectorRef, @Optional() private nzSubmenuService: NzSubmenuService, @Inject(NzIsMenuInsideDropDownToken) public isMenuInsideDropDown: boolean, + @Optional() directionality: Directionality, @Optional() private routerLink?: RouterLink, @Optional() private routerLinkWithHref?: RouterLinkWithHref, @Optional() private router?: Router @@ -128,6 +141,11 @@ export class NzMenuItemDirective implements OnInit, OnChanges, OnDestroy, AfterC this.updateRouterActive(); }); } + + this.dir = directionality.value; + directionality.change.pipe(takeUntil(this.destroy$)).subscribe(() => { + this.dir = directionality.value; + }); } ngOnInit(): void { diff --git a/components/menu/menu.directive.ts b/components/menu/menu.directive.ts index add5cd9f5a7..060583b2955 100644 --- a/components/menu/menu.directive.ts +++ b/components/menu/menu.directive.ts @@ -6,6 +6,7 @@ * found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE */ +import { Direction, Directionality } from '@angular/cdk/bidi'; import { AfterContentInit, ChangeDetectorRef, @@ -77,7 +78,8 @@ export function MenuDropDownTokenFactory(isMenuInsideDropDownToken: boolean): bo '[class.ant-menu-vertical]': `!isMenuInsideDropDown && actualMode === 'vertical'`, '[class.ant-menu-horizontal]': `!isMenuInsideDropDown && actualMode === 'horizontal'`, '[class.ant-menu-inline]': `!isMenuInsideDropDown && actualMode === 'inline'`, - '[class.ant-menu-inline-collapsed]': `!isMenuInsideDropDown && nzInlineCollapsed` + '[class.ant-menu-inline-collapsed]': `!isMenuInsideDropDown && nzInlineCollapsed`, + '[class.ant-menu-rtl]': `dir === 'rtl'` } }) export class NzMenuDirective implements AfterContentInit, OnInit, OnChanges, OnDestroy { @@ -93,6 +95,7 @@ export class NzMenuDirective implements AfterContentInit, OnInit, OnChanges, OnD @Input() @InputBoolean() nzSelectable = !this.isMenuInsideDropDown; @Output() readonly nzClick = new EventEmitter(); actualMode: NzMenuModeType = 'vertical'; + dir: Direction; private inlineCollapsed$ = new BehaviorSubject(this.nzInlineCollapsed); private mode$ = new BehaviorSubject(this.nzMode); private destroy$ = new Subject(); @@ -118,8 +121,16 @@ export class NzMenuDirective implements AfterContentInit, OnInit, OnChanges, OnD constructor( private nzMenuService: MenuService, @Inject(NzIsMenuInsideDropDownToken) public isMenuInsideDropDown: boolean, - private cdr: ChangeDetectorRef - ) {} + private cdr: ChangeDetectorRef, + directionality: Directionality + ) { + this.dir = directionality.value; + directionality.change.pipe(takeUntil(this.destroy$)).subscribe(() => { + this.dir = directionality.value; + this.nzMenuService.setMode(this.actualMode); + this.cdr.markForCheck(); + }); + } ngOnInit(): void { combineLatest([this.inlineCollapsed$, this.mode$]) diff --git a/components/menu/submenu-inline-child.component.ts b/components/menu/submenu-inline-child.component.ts index 3a87e632ecd..ffeddbb92a0 100644 --- a/components/menu/submenu-inline-child.component.ts +++ b/components/menu/submenu-inline-child.component.ts @@ -14,12 +14,17 @@ import { OnChanges, OnInit, Renderer2, + OnDestroy, SimpleChanges, TemplateRef, ViewEncapsulation } from '@angular/core'; +import { Direction, Directionality } from '@angular/cdk/bidi'; + import { collapseMotion } from 'ng-zorro-antd/core/animation'; import { NzSafeAny } from 'ng-zorro-antd/core/types'; +import { Subject } from 'rxjs'; +import { takeUntil } from 'rxjs/operators'; import { NzMenuModeType } from './menu.types'; @Component({ @@ -33,16 +38,28 @@ import { NzMenuModeType } from './menu.types'; '[class.ant-menu]': 'true', '[class.ant-menu-inline]': 'true', '[class.ant-menu-sub]': 'true', + '[class.ant-menu-rtl]': `dir === 'rtl'`, + '[class]': 'menuClass', '[@collapseMotion]': 'expandState' } }) -export class NzSubmenuInlineChildComponent implements OnInit, OnChanges { +export class NzSubmenuInlineChildComponent implements OnDestroy, OnInit, OnChanges { @Input() templateOutlet: TemplateRef | null = null; @Input() menuClass: string = ''; @Input() mode: NzMenuModeType = 'vertical'; @Input() nzOpen = false; listOfCacheClassName: string[] = []; expandState = 'collapsed'; + dir: Direction; + private destroy$ = new Subject(); + + constructor(directionality: Directionality) { + this.dir = directionality.value; + directionality.change.pipe(takeUntil(this.destroy$)).subscribe(() => { + this.dir = directionality.value; + }); + } + calcMotionState(): void { if (this.nzOpen) { this.expandState = 'expanded'; @@ -77,4 +94,9 @@ export class NzSubmenuInlineChildComponent implements OnInit, OnChanges { } } } + + ngOnDestroy(): void { + this.destroy$.next(); + this.destroy$.complete(); + } } diff --git a/components/menu/submenu-non-inline-child.component.ts b/components/menu/submenu-non-inline-child.component.ts index 6c27d848c11..4579648a3d2 100644 --- a/components/menu/submenu-non-inline-child.component.ts +++ b/components/menu/submenu-non-inline-child.component.ts @@ -6,12 +6,14 @@ * found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE */ +import { Direction, Directionality } from '@angular/cdk/bidi'; import { ChangeDetectionStrategy, Component, EventEmitter, Input, OnChanges, + OnDestroy, OnInit, Output, SimpleChanges, @@ -20,6 +22,8 @@ import { } from '@angular/core'; import { slideMotion, zoomBigMotion } from 'ng-zorro-antd/core/animation'; import { NzSafeAny } from 'ng-zorro-antd/core/types'; +import { Subject } from 'rxjs'; +import { takeUntil } from 'rxjs/operators'; import { NzMenuModeType, NzMenuThemeType } from './menu.types'; @Component({ @@ -37,6 +41,7 @@ import { NzMenuModeType, NzMenuThemeType } from './menu.types'; [class.ant-dropdown-menu-sub]="isMenuInsideDropDown" [class.ant-menu-sub]="!isMenuInsideDropDown" [ngClass]="menuClass" + [class.ant-menu-rtl]="dir === 'rtl'" >
@@ -55,7 +60,7 @@ import { NzMenuModeType, NzMenuThemeType } from './menu.types'; '(mouseleave)': 'setMouseState(false)' } }) -export class NzSubmenuNoneInlineChildComponent implements OnInit, OnChanges { +export class NzSubmenuNoneInlineChildComponent implements OnDestroy, OnInit, OnChanges { @Input() menuClass: string = ''; @Input() theme: NzMenuThemeType = 'light'; @Input() templateOutlet: TemplateRef | null = null; @@ -71,6 +76,20 @@ export class NzSubmenuNoneInlineChildComponent implements OnInit, OnChanges { } } expandState = 'collapsed'; + dir: Direction; + private destroy$ = new Subject(); + + constructor(directionality: Directionality) { + this.dir = directionality.value; + directionality.change.pipe(takeUntil(this.destroy$)).subscribe(() => { + this.dir = directionality.value; + }); + } + + ngOnDestroy(): void { + this.destroy$.next(); + this.destroy$.complete(); + } calcMotionState(): void { if (this.nzOpen) { if (this.mode === 'horizontal') { diff --git a/components/menu/submenu-title.component.ts b/components/menu/submenu-title.component.ts index 631d1fcda06..4be83255178 100644 --- a/components/menu/submenu-title.component.ts +++ b/components/menu/submenu-title.component.ts @@ -6,7 +6,20 @@ * found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE */ -import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output, TemplateRef, ViewEncapsulation } from '@angular/core'; +import { Direction, Directionality } from '@angular/cdk/bidi'; +import { + ChangeDetectionStrategy, + ChangeDetectorRef, + Component, + EventEmitter, + Input, + OnDestroy, + Output, + TemplateRef, + ViewEncapsulation +} from '@angular/core'; +import { Subject } from 'rxjs'; +import { takeUntil } from 'rxjs/operators'; import { NzMenuModeType } from './menu.types'; @Component({ @@ -20,8 +33,9 @@ import { NzMenuModeType } from './menu.types'; {{ nzTitle }} - - + + + @@ -30,13 +44,14 @@ import { NzMenuModeType } from './menu.types'; host: { '[class.ant-dropdown-menu-submenu-title]': 'isMenuInsideDropDown', '[class.ant-menu-submenu-title]': '!isMenuInsideDropDown', - '[style.paddingLeft.px]': 'paddingLeft', + '[style.paddingLeft.px]': `dir === 'rtl' ? 0 : paddingLeft `, + '[style.paddingRight.px]': `dir === 'rtl' ? paddingLeft : 0`, '(click)': 'clickTitle()', '(mouseenter)': 'setMouseState(true)', '(mouseleave)': 'setMouseState(false)' } }) -export class NzSubMenuTitleComponent { +export class NzSubMenuTitleComponent implements OnDestroy { @Input() nzIcon: string | null = null; @Input() nzTitle: string | TemplateRef | null = null; @Input() isMenuInsideDropDown = false; @@ -45,6 +60,23 @@ export class NzSubMenuTitleComponent { @Input() mode: NzMenuModeType = 'vertical'; @Output() readonly toggleSubMenu = new EventEmitter(); @Output() readonly subMenuMouseState = new EventEmitter(); + + dir: Direction; + private destroy$ = new Subject(); + + constructor(cdr: ChangeDetectorRef, directionality: Directionality) { + this.dir = directionality.value; + directionality.change.pipe(takeUntil(this.destroy$)).subscribe(() => { + this.dir = directionality.value; + cdr.detectChanges(); + }); + } + + ngOnDestroy(): void { + this.destroy$.next(); + this.destroy$.complete(); + } + setMouseState(state: boolean): void { if (!this.nzDisabled) { this.subMenuMouseState.next(state); diff --git a/components/page-header/page-header.component.ts b/components/page-header/page-header.component.ts index 85a2d343138..eea1a52773c 100644 --- a/components/page-header/page-header.component.ts +++ b/components/page-header/page-header.component.ts @@ -8,20 +8,26 @@ import { ChangeDetectionStrategy, + ChangeDetectorRef, Component, ContentChild, ElementRef, EventEmitter, Input, + OnChanges, + OnDestroy, Optional, Output, TemplateRef, ViewEncapsulation } from '@angular/core'; +import { Direction, Directionality } from '@angular/cdk/bidi'; import { Location } from '@angular/common'; import { NzConfigService, WithConfig } from 'ng-zorro-antd/core/config'; import { PREFIX } from 'ng-zorro-antd/core/logger'; +import { Subject } from 'rxjs'; +import { takeUntil } from 'rxjs/operators'; import { NzPageHeaderBreadcrumbDirective, NzPageHeaderFooterDirective } from './page-header-cells'; const NZ_CONFIG_COMPONENT_NAME = 'pageHeader'; @@ -69,10 +75,11 @@ const NZ_CONFIG_COMPONENT_NAME = 'pageHeader'; class: 'ant-page-header', '[class.has-footer]': 'nzPageHeaderFooter', '[class.ant-page-header-ghost]': 'nzGhost', - '[class.has-breadcrumb]': 'nzPageHeaderBreadcrumb' + '[class.has-breadcrumb]': 'nzPageHeaderBreadcrumb', + '[class.ant-page-header-rtl]': `dir === 'rtl'` } }) -export class NzPageHeaderComponent { +export class NzPageHeaderComponent implements OnChanges, OnDestroy { @Input() nzBackIcon: string | TemplateRef | null = null; @Input() nzTitle?: string | TemplateRef; @Input() nzSubtitle?: string | TemplateRef; @@ -82,7 +89,28 @@ export class NzPageHeaderComponent { @ContentChild(NzPageHeaderFooterDirective, { static: false }) nzPageHeaderFooter?: ElementRef; @ContentChild(NzPageHeaderBreadcrumbDirective, { static: false }) nzPageHeaderBreadcrumb?: ElementRef; - constructor(@Optional() private location: Location, public nzConfigService: NzConfigService) {} + dir: Direction; + + private destroy$ = new Subject(); + + constructor( + cdr: ChangeDetectorRef, + private location: Location, + public nzConfigService: NzConfigService, + @Optional() directionality: Directionality + ) { + directionality.change.pipe(takeUntil(this.destroy$)).subscribe(() => { + this.dir = directionality.value; + cdr.detectChanges(); + }); + + this.dir = directionality.value; + } + + ngOnDestroy(): void { + this.destroy$.next(); + this.destroy$.complete(); + } onBack(): void { if (this.nzBack.observers.length) { @@ -94,4 +122,13 @@ export class NzPageHeaderComponent { this.location.back(); } } + getBackIcon(): string | TemplateRef { + if (this.nzBackIcon) { + return this.nzBackIcon; + } + if (this.dir === 'rtl') { + return 'arrow-right'; + } + return 'arrow-left'; + } } diff --git a/components/pagination/pagination-default.component.ts b/components/pagination/pagination-default.component.ts index 6b88133d260..39d3d875c8e 100644 --- a/components/pagination/pagination-default.component.ts +++ b/components/pagination/pagination-default.component.ts @@ -6,13 +6,17 @@ * found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE */ +import { Direction, Directionality } from '@angular/cdk/bidi'; import { ChangeDetectionStrategy, + ChangeDetectorRef, Component, ElementRef, EventEmitter, Input, OnChanges, + OnDestroy, + Optional, Output, Renderer2, SimpleChanges, @@ -21,6 +25,8 @@ import { ViewEncapsulation } from '@angular/core'; import { NzSafeAny } from 'ng-zorro-antd/core/types'; +import { Subject } from 'rxjs'; +import { takeUntil } from 'rxjs/operators'; import { NzPaginationItemComponent } from './pagination-item.component'; import { PaginationItemRenderContext } from './pagination.types'; @@ -45,6 +51,7 @@ import { PaginationItemRenderContext } from './pagination.types'; [active]="pageIndex === page.index" (gotoIndex)="jumpPage($event)" (diffIndex)="jumpDiff($event)" + [direction]="dir" >
` }) -export class NzPaginationDefaultComponent implements OnChanges { +export class NzPaginationDefaultComponent implements OnChanges, OnDestroy { @ViewChild('containerTemplate', { static: true }) template!: TemplateRef; @Input() nzSize: 'default' | 'small' = 'default'; @Input() itemRender: TemplateRef | null = null; @@ -82,10 +89,39 @@ export class NzPaginationDefaultComponent implements OnChanges { ranges = [0, 0]; listOfPageItem: Array> = []; - constructor(renderer: Renderer2, elementRef: ElementRef) { + dir: Direction; + private destroy$ = new Subject(); + + constructor( + cdr: ChangeDetectorRef, + private renderer: Renderer2, + private elementRef: ElementRef, + @Optional() directionality: Directionality + ) { + directionality.change.pipe(takeUntil(this.destroy$)).subscribe(() => { + this.dir = directionality.value; + this.updateRtlStyle(); + cdr.detectChanges(); + }); + this.dir = directionality.value; + this.updateRtlStyle(); + renderer.removeChild(renderer.parentNode(elementRef.nativeElement), elementRef.nativeElement); } + private updateRtlStyle(): void { + if (this.dir === 'rtl') { + this.renderer.addClass(this.elementRef.nativeElement, 'ant-pagination-rtl'); + } else { + this.renderer.removeClass(this.elementRef.nativeElement, 'ant-pagination-rtl'); + } + } + + ngOnDestroy(): void { + this.destroy$.next(); + this.destroy$.complete(); + } + jumpPage(index: number): void { this.onPageIndexChange(index); } diff --git a/components/pagination/pagination-item.component.ts b/components/pagination/pagination-item.component.ts index badce3cfee1..67be45f4d60 100644 --- a/components/pagination/pagination-item.component.ts +++ b/components/pagination/pagination-item.component.ts @@ -31,12 +31,24 @@ import { PaginationItemRenderContext, PaginationItemType } from './pagination.ty {{ page }} - - + + + + + + + +
- - + + + + + + + + •••
@@ -71,6 +83,7 @@ export class NzPaginationItemComponent implements OnChanges { @Input() locale: NzSafeAny = {}; @Input() index: number | null = null; @Input() disabled = false; + @Input() direction = 'ltr'; @Input() type: PaginationItemType | string | null = null; @Input() itemRender: TemplateRef | null = null; @Output() readonly diffIndex = new EventEmitter(); diff --git a/components/pagination/pagination-simple.component.ts b/components/pagination/pagination-simple.component.ts index 4f6c156cc2a..7583e35f829 100644 --- a/components/pagination/pagination-simple.component.ts +++ b/components/pagination/pagination-simple.component.ts @@ -6,13 +6,17 @@ * found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE */ +import { Direction, Directionality } from '@angular/cdk/bidi'; import { ChangeDetectionStrategy, + ChangeDetectorRef, Component, ElementRef, EventEmitter, Input, OnChanges, + OnDestroy, + Optional, Output, Renderer2, SimpleChanges, @@ -22,6 +26,8 @@ import { } from '@angular/core'; import { NzSafeAny } from 'ng-zorro-antd/core/types'; import { toNumber } from 'ng-zorro-antd/core/util'; +import { Subject } from 'rxjs'; +import { takeUntil } from 'rxjs/operators'; import { PaginationItemRenderContext } from './pagination.types'; @Component({ @@ -35,6 +41,7 @@ import { PaginationItemRenderContext } from './pagination.types'; nz-pagination-item [attr.title]="locale.prev_page" [disabled]="isFirstIndex" + [direction]="dir" (click)="prePage()" type="prev" [itemRender]="itemRender" @@ -48,6 +55,7 @@ import { PaginationItemRenderContext } from './pagination.types'; nz-pagination-item [attr.title]="locale.next_page" [disabled]="isLastIndex" + [direction]="dir" (click)="nextPage()" type="next" [itemRender]="itemRender" @@ -55,7 +63,7 @@ import { PaginationItemRenderContext } from './pagination.types'; ` }) -export class NzPaginationSimpleComponent implements OnChanges { +export class NzPaginationSimpleComponent implements OnChanges, OnDestroy { @ViewChild('containerTemplate', { static: true }) template!: TemplateRef; @Input() itemRender: TemplateRef | null = null; @Input() disabled = false; @@ -68,10 +76,39 @@ export class NzPaginationSimpleComponent implements OnChanges { isFirstIndex = false; isLastIndex = false; - constructor(renderer: Renderer2, elementRef: ElementRef) { + dir: Direction; + private destroy$ = new Subject(); + + constructor( + cdr: ChangeDetectorRef, + private renderer: Renderer2, + private elementRef: ElementRef, + @Optional() directionality: Directionality + ) { + directionality.change.pipe(takeUntil(this.destroy$)).subscribe(() => { + this.dir = directionality.value; + this.updateRtlStyle(); + cdr.detectChanges(); + }); + this.dir = directionality.value; + this.updateRtlStyle(); + renderer.removeChild(renderer.parentNode(elementRef.nativeElement), elementRef.nativeElement); } + private updateRtlStyle(): void { + if (this.dir === 'rtl') { + this.renderer.addClass(this.elementRef.nativeElement, 'ant-pagination-rtl'); + } else { + this.renderer.removeClass(this.elementRef.nativeElement, 'ant-pagination-rtl'); + } + } + + ngOnDestroy(): void { + this.destroy$.next(); + this.destroy$.complete(); + } + jumpToPageViaInput($event: Event): void { const target = $event.target as HTMLInputElement; const index = toNumber(target.value, this.pageIndex); diff --git a/components/popconfirm/popconfirm.ts b/components/popconfirm/popconfirm.ts index f94b935dcba..e1ef920a178 100644 --- a/components/popconfirm/popconfirm.ts +++ b/components/popconfirm/popconfirm.ts @@ -6,6 +6,7 @@ * found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE */ +import { Directionality } from '@angular/cdk/bidi'; import { ChangeDetectionStrategy, ChangeDetectorRef, @@ -130,6 +131,7 @@ export class NzPopconfirmDirective extends NzTooltipBaseDirective {
`${p}%`; [class.ant-progress-show-info]="nzShowInfo" [class.ant-progress-circle]="isCircleStyle" [class.ant-progress-steps]="isSteps" + [class.ant-progress-rtl]="dir === 'rtl'" >
@@ -186,6 +199,8 @@ export class NzProgressComponent implements OnChanges, OnInit, OnDestroy { pathString?: string; icon!: string; + dir: Direction; + trackByFn = (index: number) => `${index}`; get formatter(): NzProgressFormatter { @@ -208,7 +223,14 @@ export class NzProgressComponent implements OnChanges, OnInit, OnDestroy { private inferredStatus: NzProgressStatusType = 'normal'; private destroy$ = new Subject(); - constructor(public nzConfigService: NzConfigService) {} + constructor(cdr: ChangeDetectorRef, public nzConfigService: NzConfigService, @Optional() directionality: Directionality) { + directionality.change.pipe(takeUntil(this.destroy$)).subscribe(() => { + this.dir = directionality.value; + cdr.detectChanges(); + }); + + this.dir = directionality.value; + } ngOnChanges(changes: SimpleChanges): void { const { nzSteps, nzGapPosition, nzStrokeLinecap, nzStrokeColor, nzGapDegree, nzType, nzStatus, nzPercent, nzSuccessPercent } = changes; diff --git a/components/radio/radio-group.component.ts b/components/radio/radio-group.component.ts index d62df48aabe..dde29f5c5d6 100644 --- a/components/radio/radio-group.component.ts +++ b/components/radio/radio-group.component.ts @@ -6,6 +6,7 @@ * found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE */ +import { Direction, Directionality } from '@angular/cdk/bidi'; import { ChangeDetectionStrategy, ChangeDetectorRef, @@ -15,6 +16,7 @@ import { OnChanges, OnDestroy, OnInit, + Optional, SimpleChanges, ViewEncapsulation } from '@angular/core'; @@ -22,6 +24,7 @@ import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; import { BooleanInput, NzSafeAny, NzSizeLDSType, OnChangeType, OnTouchedType } from 'ng-zorro-antd/core/types'; import { InputBoolean } from 'ng-zorro-antd/core/util'; import { Subject } from 'rxjs'; +import { takeUntil } from 'rxjs/operators'; import { NzRadioService } from './radio.service'; export type NzRadioButtonStyle = 'outline' | 'solid'; @@ -45,7 +48,8 @@ export type NzRadioButtonStyle = 'outline' | 'solid'; '[class.ant-radio-group]': `true`, '[class.ant-radio-group-large]': `nzSize === 'large'`, '[class.ant-radio-group-small]': `nzSize === 'small'`, - '[class.ant-radio-group-solid]': `nzButtonStyle === 'solid'` + '[class.ant-radio-group-solid]': `nzButtonStyle === 'solid'`, + '[class.ant-radio-group-rtl]': `dir === 'rtl'` } }) export class NzRadioGroupComponent implements OnInit, ControlValueAccessor, OnDestroy, OnChanges { @@ -53,14 +57,27 @@ export class NzRadioGroupComponent implements OnInit, ControlValueAccessor, OnDe private value: NzSafeAny | null = null; private destroy$ = new Subject(); - onChange: OnChangeType = () => {}; - onTouched: OnTouchedType = () => {}; + onChange: OnChangeType = () => { }; + onTouched: OnTouchedType = () => { }; @Input() @InputBoolean() nzDisabled = false; @Input() nzButtonStyle: NzRadioButtonStyle = 'outline'; @Input() nzSize: NzSizeLDSType = 'default'; @Input() nzName: string | null = null; - constructor(private cdr: ChangeDetectorRef, private nzRadioService: NzRadioService) {} + dir: Direction; + + constructor( + private cdr: ChangeDetectorRef, + private nzRadioService: NzRadioService, + @Optional() directionality: Directionality + ) { + directionality.change.pipe(takeUntil(this.destroy$)).subscribe(() => { + this.dir = directionality.value; + cdr.detectChanges(); + }); + + this.dir = directionality.value; + } ngOnInit(): void { this.nzRadioService.selected$.subscribe(value => { diff --git a/components/radio/radio.component.ts b/components/radio/radio.component.ts index 3b03fee05f3..60eeff2dd74 100644 --- a/components/radio/radio.component.ts +++ b/components/radio/radio.component.ts @@ -24,6 +24,7 @@ import { import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; import { BooleanInput, NzSafeAny, OnChangeType, OnTouchedType } from 'ng-zorro-antd/core/types'; +import { Direction, Directionality } from '@angular/cdk/bidi'; import { InputBoolean } from 'ng-zorro-antd/core/util'; import { Subject } from 'rxjs'; import { takeUntil } from 'rxjs/operators'; @@ -73,6 +74,7 @@ import { NzRadioService } from './radio.service'; '[class.ant-radio-button-wrapper-checked]': 'isChecked && isRadioButton', '[class.ant-radio-wrapper-disabled]': 'nzDisabled && !isRadioButton', '[class.ant-radio-button-wrapper-disabled]': 'nzDisabled && isRadioButton', + '[class.ant-radio-button-wrapper-rtl]': `dir === 'rtl'`, '(click)': 'onHostClick($event)' } }) @@ -92,6 +94,8 @@ export class NzRadioComponent implements ControlValueAccessor, AfterViewInit, On @Input() @InputBoolean() nzDisabled = false; @Input() @InputBoolean() nzAutoFocus = false; + dir: Direction; + onHostClick(event: MouseEvent): void { /** prevent label click triggered twice. **/ event.stopPropagation(); @@ -120,9 +124,17 @@ export class NzRadioComponent implements ControlValueAccessor, AfterViewInit, On private elementRef: ElementRef, private cdr: ChangeDetectorRef, private focusMonitor: FocusMonitor, + @Optional() directionality: Directionality, @Optional() private nzRadioService: NzRadioService, @Optional() private nzRadioButtonDirective: NzRadioButtonDirective - ) {} + ) { + directionality.change.pipe(takeUntil(this.destroy$)).subscribe(() => { + this.dir = directionality.value; + cdr.detectChanges(); + }); + + this.dir = directionality.value; + } setDisabledState(disabled: boolean): void { this.nzDisabled = disabled; diff --git a/components/rate/rate.component.ts b/components/rate/rate.component.ts index c99e96dca78..2a3e4233e39 100644 --- a/components/rate/rate.component.ts +++ b/components/rate/rate.component.ts @@ -6,6 +6,7 @@ * found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE */ +import { Direction, Directionality } from '@angular/cdk/bidi'; import { LEFT_ARROW, RIGHT_ARROW } from '@angular/cdk/keycodes'; import { ChangeDetectionStrategy, @@ -18,6 +19,7 @@ import { OnChanges, OnDestroy, OnInit, + Optional, Output, Renderer2, SimpleChanges, @@ -46,6 +48,7 @@ const NZ_CONFIG_COMPONENT_NAME = 'rate'; #ulElement class="ant-rate" [class.ant-rate-disabled]="nzDisabled" + [class.ant-rate-rtl]="dir === 'rtl'" [ngClass]="classMap" (blur)="onBlur($event)" (focus)="onFocus($event)" @@ -102,6 +105,7 @@ export class NzRateComponent implements OnInit, OnDestroy, ControlValueAccessor, classMap: NgClassType = {}; starArray: number[] = []; starStyleArray: NgClassType[] = []; + dir: Direction; private readonly destroy$ = new Subject(); private hasHalf = false; @@ -123,7 +127,19 @@ export class NzRateComponent implements OnInit, OnDestroy, ControlValueAccessor, this.hoverValue = Math.ceil(input); } - constructor(public nzConfigService: NzConfigService, private renderer: Renderer2, private cdr: ChangeDetectorRef) {} + constructor( + public nzConfigService: NzConfigService, + private renderer: Renderer2, + private cdr: ChangeDetectorRef, + @Optional() directionality: Directionality + ) { + directionality.change.pipe(takeUntil(this.destroy$)).subscribe(() => { + this.dir = directionality.value; + cdr.detectChanges(); + }); + + this.dir = directionality.value; + } ngOnChanges(changes: SimpleChanges): void { const { nzAutoFocus, nzCount, nzValue } = changes; diff --git a/components/result/result.component.ts b/components/result/result.component.ts index 888e246733b..8e4abcf6b7d 100644 --- a/components/result/result.component.ts +++ b/components/result/result.component.ts @@ -6,7 +6,10 @@ * found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE */ -import { ChangeDetectionStrategy, Component, Input, OnChanges, TemplateRef, ViewEncapsulation } from '@angular/core'; +import { Direction, Directionality } from '@angular/cdk/bidi'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnChanges, OnDestroy, Optional, TemplateRef, ViewEncapsulation } from '@angular/core'; +import { Subject } from 'rxjs'; +import { takeUntil } from 'rxjs/operators'; export type NzResultIconType = 'success' | 'error' | 'info' | 'warning'; export type NzExceptionStatusType = '404' | '500' | '403'; @@ -69,10 +72,11 @@ const ExceptionStatus = ['404', '500', '403']; '[class.ant-result-success]': `nzStatus === 'success'`, '[class.ant-result-error]': `nzStatus === 'error'`, '[class.ant-result-info]': `nzStatus === 'info'`, - '[class.ant-result-warning]': `nzStatus === 'warning'` + '[class.ant-result-warning]': `nzStatus === 'warning'`, + '[class.ant-result-rtl]': `dir === 'rtl'` } }) -export class NzResultComponent implements OnChanges { +export class NzResultComponent implements OnChanges, OnDestroy { @Input() nzIcon?: string | TemplateRef; @Input() nzTitle?: string | TemplateRef; @Input() nzStatus: NzResultStatusType = 'info'; @@ -81,13 +85,31 @@ export class NzResultComponent implements OnChanges { icon?: string | TemplateRef; isException = false; + dir: Direction; - constructor() {} + private destroy$ = new Subject(); + + constructor( + cdr: ChangeDetectorRef, + @Optional() directionality: Directionality + ) { + directionality.change.pipe(takeUntil(this.destroy$)).subscribe(() => { + this.dir = directionality.value; + cdr.detectChanges(); + }); + + this.dir = directionality.value; + } ngOnChanges(): void { this.setStatusIcon(); } + ngOnDestroy(): void { + this.destroy$.next(); + this.destroy$.complete(); + } + private setStatusIcon(): void { const icon = this.nzIcon; @@ -97,7 +119,7 @@ export class NzResultComponent implements OnChanges { ? IconMap[icon as NzResultIconType] || icon : icon : this.isException - ? undefined - : IconMap[this.nzStatus as NzResultIconType]; + ? undefined + : IconMap[this.nzStatus as NzResultIconType]; } } diff --git a/components/select/select.component.ts b/components/select/select.component.ts index 004d9a98aaa..a72a5a95ec3 100644 --- a/components/select/select.component.ts +++ b/components/select/select.component.ts @@ -7,6 +7,7 @@ */ import { FocusMonitor } from '@angular/cdk/a11y'; +import { Direction, Directionality } from '@angular/cdk/bidi'; import { DOWN_ARROW, ENTER, SPACE, TAB, UP_ARROW } from '@angular/cdk/keycodes'; import { CdkConnectedOverlay, CdkOverlayOrigin, ConnectedOverlayPositionChange } from '@angular/cdk/overlay'; import { Platform } from '@angular/cdk/platform'; @@ -157,7 +158,8 @@ export type NzSelectSizeType = 'large' | 'default' | 'small'; '[class.ant-select-open]': 'nzOpen', '[class.ant-select-focused]': 'nzOpen', '[class.ant-select-single]': `nzMode === 'default'`, - '[class.ant-select-multiple]': `nzMode !== 'default'` + '[class.ant-select-multiple]': `nzMode !== 'default'`, + '[class.ant-select-rtl]': `dir === 'rtl'` } }) export class NzSelectComponent implements ControlValueAccessor, OnInit, AfterViewInit, OnDestroy, AfterContentInit, OnChanges { @@ -224,14 +226,15 @@ export class NzSelectComponent implements ControlValueAccessor, OnInit, AfterVie private isReactiveDriven = false; private value: NzSafeAny | NzSafeAny[]; private destroy$ = new Subject(); - onChange: OnChangeType = () => {}; - onTouched: OnTouchedType = () => {}; + onChange: OnChangeType = () => { }; + onTouched: OnTouchedType = () => { }; dropDownPosition: 'top' | 'center' | 'bottom' = 'bottom'; triggerWidth: number | null = null; listOfContainerItem: NzSelectItemInterface[] = []; listOfTopItem: NzSelectItemInterface[] = []; activatedValue: NzSafeAny | null = null; listOfValue: NzSafeAny[] = []; + dir: Direction; generateTagItem(value: string): NzSelectItemInterface { return { @@ -455,8 +458,16 @@ export class NzSelectComponent implements ControlValueAccessor, OnInit, AfterVie private elementRef: ElementRef, private platform: Platform, private focusMonitor: FocusMonitor, + @Optional() directionality: Directionality, @Host() @Optional() public noAnimation?: NzNoAnimationDirective - ) {} + ) { + directionality.change.pipe(takeUntil(this.destroy$)).subscribe(() => { + this.dir = directionality.value; + cdr.detectChanges(); + }); + + this.dir = directionality.value; + } writeValue(modelValue: NzSafeAny | NzSafeAny[]): void { /** https://github.com/angular/angular/issues/14988 **/ diff --git a/components/statistic/countdown.component.ts b/components/statistic/countdown.component.ts index ed931b3748d..c63f1f69f7d 100644 --- a/components/statistic/countdown.component.ts +++ b/components/statistic/countdown.component.ts @@ -17,12 +17,14 @@ import { OnChanges, OnDestroy, OnInit, + Optional, Output, SimpleChanges, ViewEncapsulation } from '@angular/core'; import { interval, Subscription } from 'rxjs'; +import { Directionality } from '@angular/cdk/bidi'; import { NzStatisticComponent } from './statistic.component'; const REFRESH_INTERVAL = 1000 / 30; @@ -55,8 +57,13 @@ export class NzCountdownComponent extends NzStatisticComponent implements OnInit private target: number = 0; private updater_?: Subscription | null; - constructor(private cdr: ChangeDetectorRef, private ngZone: NgZone, private platform: Platform) { - super(); + constructor( + private cdr: ChangeDetectorRef, + private ngZone: NgZone, + private platform: Platform, + @Optional() directionality: Directionality + ) { + super(cdr, directionality); } ngOnChanges(changes: SimpleChanges): void { diff --git a/components/statistic/statistic.component.ts b/components/statistic/statistic.component.ts index fd1c7d67b40..cf954057d92 100644 --- a/components/statistic/statistic.component.ts +++ b/components/statistic/statistic.component.ts @@ -6,8 +6,20 @@ * found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE */ -import { ChangeDetectionStrategy, Component, Input, TemplateRef, ViewEncapsulation } from '@angular/core'; +import { Direction, Directionality } from '@angular/cdk/bidi'; +import { + ChangeDetectionStrategy, + ChangeDetectorRef, + Component, + Input, + OnDestroy, + Optional, + TemplateRef, + ViewEncapsulation +} from '@angular/core'; import { NgStyleInterface } from 'ng-zorro-antd/core/types'; +import { Subject } from 'rxjs'; +import { takeUntil } from 'rxjs/operators'; import { NzStatisticValueType } from './typings'; @Component({ @@ -16,7 +28,7 @@ import { NzStatisticValueType } from './typings'; selector: 'nz-statistic', exportAs: 'nzStatistic', template: ` -
+
{{ nzTitle }}
@@ -32,11 +44,28 @@ import { NzStatisticValueType } from './typings';
` }) -export class NzStatisticComponent { +export class NzStatisticComponent implements OnDestroy { @Input() nzPrefix?: string | TemplateRef; @Input() nzSuffix?: string | TemplateRef; @Input() nzTitle?: string | TemplateRef; @Input() nzValue?: NzStatisticValueType; @Input() nzValueStyle: NgStyleInterface = {}; @Input() nzValueTemplate?: TemplateRef<{ $implicit: NzStatisticValueType }>; + dir: Direction; + + private destroy$ = new Subject(); + + constructor(cdr: ChangeDetectorRef, @Optional() directionality: Directionality) { + directionality.change.pipe(takeUntil(this.destroy$)).subscribe(() => { + this.dir = directionality.value; + cdr.detectChanges(); + }); + + this.dir = directionality.value; + } + + ngOnDestroy(): void { + this.destroy$.next(); + this.destroy$.complete(); + } } diff --git a/components/steps/steps.component.ts b/components/steps/steps.component.ts index 0b095e289de..f38f1761f4f 100644 --- a/components/steps/steps.component.ts +++ b/components/steps/steps.component.ts @@ -9,6 +9,7 @@ import { AfterContentInit, ChangeDetectionStrategy, + ChangeDetectorRef, Component, ContentChildren, EventEmitter, @@ -16,6 +17,7 @@ import { OnChanges, OnDestroy, OnInit, + Optional, Output, QueryList, SimpleChanges, @@ -28,6 +30,7 @@ import { startWith, takeUntil } from 'rxjs/operators'; import { BooleanInput, NgClassType, NzSizeDSType } from 'ng-zorro-antd/core/types'; +import { Direction, Directionality } from '@angular/cdk/bidi'; import { NzStepComponent } from './step.component'; export type NzDirectionType = 'horizontal' | 'vertical'; @@ -77,6 +80,18 @@ export class NzStepsComponent implements OnChanges, OnInit, OnDestroy, AfterCont showProcessDot = false; customProcessDotTemplate?: TemplateRef<{ $implicit: TemplateRef; status: string; index: number }>; classMap: NgClassType = {}; + dir: Direction; + + constructor(cdr: ChangeDetectorRef, @Optional() directionality: Directionality) { + directionality.change.pipe(takeUntil(this.destroy$)).subscribe(() => { + this.dir = directionality.value; + this.setClassMap(); + cdr.detectChanges(); + }); + + this.dir = directionality.value; + this.setClassMap(); + } ngOnChanges(changes: SimpleChanges): void { if (changes.nzStartIndex || changes.nzDirection || changes.nzStatus || changes.nzCurrent) { @@ -140,7 +155,12 @@ export class NzStepsComponent implements OnChanges, OnInit, OnDestroy, AfterCont [`ant-steps-label-vertical`]: (this.showProcessDot || this.nzLabelPlacement === 'vertical') && this.nzDirection === 'horizontal', [`ant-steps-dot`]: this.showProcessDot, ['ant-steps-small']: this.nzSize === 'small', - ['ant-steps-navigation']: this.nzType === 'navigation' + ['ant-steps-navigation']: this.nzType === 'navigation', + ['ant-steps-rtl']: this.dir === 'rtl' }; } + + get isRtlLayout(): boolean { + return this.dir === 'rtl'; + } } diff --git a/components/switch/switch.component.ts b/components/switch/switch.component.ts index 5b4e1be1f1b..5565e47444e 100644 --- a/components/switch/switch.component.ts +++ b/components/switch/switch.component.ts @@ -17,15 +17,19 @@ import { forwardRef, Input, OnDestroy, + Optional, TemplateRef, ViewChild, ViewEncapsulation } from '@angular/core'; import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; +import { Direction, Directionality } from '@angular/cdk/bidi'; import { NzConfigService, WithConfig } from 'ng-zorro-antd/core/config'; import { BooleanInput, NzSizeDSType, OnChangeType, OnTouchedType } from 'ng-zorro-antd/core/types'; import { InputBoolean } from 'ng-zorro-antd/core/util'; +import { Subject } from 'rxjs'; +import { takeUntil } from 'rxjs/operators'; const NZ_CONFIG_COMPONENT_NAME = 'switch'; @@ -53,6 +57,7 @@ const NZ_CONFIG_COMPONENT_NAME = 'switch'; [class.ant-switch-loading]="nzLoading" [class.ant-switch-disabled]="nzDisabled" [class.ant-switch-small]="nzSize === 'small'" + [class.ant-switch-rtl]="dir === 'rtl'" [nzWaveExtraNode]="true" (keydown)="onKeyDown($event)" > @@ -88,6 +93,10 @@ export class NzSwitchComponent implements ControlValueAccessor, AfterViewInit, O @Input() nzUnCheckedChildren: string | TemplateRef | null = null; @Input() @WithConfig(NZ_CONFIG_COMPONENT_NAME) nzSize: NzSizeDSType = 'default'; + dir: Direction; + + private destroy$ = new Subject(); + onHostClick(e: MouseEvent): void { e.preventDefault(); if (!this.nzDisabled && !this.nzLoading && !this.nzControl) { @@ -125,7 +134,19 @@ export class NzSwitchComponent implements ControlValueAccessor, AfterViewInit, O this.switchElement?.nativeElement.blur(); } - constructor(public nzConfigService: NzConfigService, private cdr: ChangeDetectorRef, private focusMonitor: FocusMonitor) {} + constructor( + public nzConfigService: NzConfigService, + private cdr: ChangeDetectorRef, + private focusMonitor: FocusMonitor, + @Optional() directionality: Directionality + ) { + directionality.change.pipe(takeUntil(this.destroy$)).subscribe(() => { + this.dir = directionality.value; + cdr.detectChanges(); + }); + + this.dir = directionality.value; + } ngAfterViewInit(): void { this.focusMonitor.monitor(this.switchElement!.nativeElement, true).subscribe(focusOrigin => { @@ -138,6 +159,8 @@ export class NzSwitchComponent implements ControlValueAccessor, AfterViewInit, O ngOnDestroy(): void { this.focusMonitor.stopMonitoring(this.switchElement!.nativeElement); + this.destroy$.next(); + this.destroy$.complete(); } writeValue(value: boolean): void { diff --git a/components/table/src/table/table.component.ts b/components/table/src/table/table.component.ts index 3beecc6fa2e..2d16d32c5d4 100644 --- a/components/table/src/table/table.component.ts +++ b/components/table/src/table/table.component.ts @@ -6,6 +6,7 @@ * found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE */ +import { Direction, Directionality } from '@angular/cdk/bidi'; import { CdkVirtualScrollViewport } from '@angular/cdk/scrolling'; import { AfterViewInit, @@ -19,6 +20,7 @@ import { OnChanges, OnDestroy, OnInit, + Optional, Output, SimpleChanges, TemplateRef, @@ -56,6 +58,7 @@ const NZ_CONFIG_COMPONENT_NAME = 'table';
`, host: { - '[class.ant-table-wrapper]': 'true' + '[class.ant-table-wrapper]': 'true', + '[class.ant-table-wrapper-rtl]': 'dir === "rtl"' + } }) export class NzTableComponent implements OnInit, OnDestroy, OnChanges, AfterViewInit { @@ -179,6 +184,7 @@ export class NzTableComponent implements OnInit, OnDestroy, OnChanges, AfterView private destroy$ = new Subject(); private loading$ = new BehaviorSubject(false); private templateMode$ = new BehaviorSubject(false); + dir: Direction; @ContentChild(NzTableVirtualScrollDirective, { static: false }) nzVirtualScrollDirective!: NzTableVirtualScrollDirective; @ViewChild(NzTableInnerScrollComponent) nzTableInnerScrollComponent!: NzTableInnerScrollComponent; @@ -197,7 +203,8 @@ export class NzTableComponent implements OnInit, OnDestroy, OnChanges, AfterView private nzConfigService: NzConfigService, private cdr: ChangeDetectorRef, private nzTableStyleService: NzTableStyleService, - private nzTableDataService: NzTableDataService + private nzTableDataService: NzTableDataService, + @Optional() directionality: Directionality ) { this.nzConfigService .getConfigChangeEventForComponent(NZ_CONFIG_COMPONENT_NAME) @@ -205,6 +212,11 @@ export class NzTableComponent implements OnInit, OnDestroy, OnChanges, AfterView .subscribe(() => { this.cdr.markForCheck(); }); + this.dir = directionality.value; + directionality.change.pipe(takeUntil(this.destroy$)).subscribe(() => { + this.dir = directionality.value; + cdr.detectChanges(); + }); } ngOnInit(): void { diff --git a/components/table/src/testing/table.spec.ts b/components/table/src/testing/table.spec.ts index a82716bbc65..6e418a24552 100644 --- a/components/table/src/testing/table.spec.ts +++ b/components/table/src/testing/table.spec.ts @@ -213,6 +213,13 @@ describe('nz-table', () => { fixture.detectChanges(); expect(table.nativeElement.querySelector('.ant-table-placeholder').innerText.trim()).toBe('No Data'); }); + + it('#RTL', () => { + document.body.setAttribute('dir', 'rtl'); + fixture.detectChanges(); + expect(table.nativeElement.classList).toContain('ant-table-wrapper-rtl'); + expect(table.nativeElement.querySelector('.ant-table-rtl').classList).toContain('ant-table-rtl'); + }); }); describe('scroll nz-table', () => { let fixture: ComponentFixture; diff --git a/components/table/style/patch.less b/components/table/style/patch.less index 890eb0e4fd5..254761a8aa5 100644 --- a/components/table/style/patch.less +++ b/components/table/style/patch.less @@ -25,3 +25,7 @@ cdk-virtual-scroll-viewport.ant-table-body { } } } + +.ant-table-wrapper-rtl .ant-table thead > tr > th.ant-table-selection-column { + text-align: center; +} diff --git a/components/tag/tag.component.ts b/components/tag/tag.component.ts index 7863d25fa41..3e36fac4f21 100644 --- a/components/tag/tag.component.ts +++ b/components/tag/tag.component.ts @@ -7,14 +7,18 @@ */ import { AnimationEvent } from '@angular/animations'; +import { Direction, Directionality } from '@angular/cdk/bidi'; import { ChangeDetectionStrategy, + ChangeDetectorRef, Component, ElementRef, EventEmitter, Input, OnChanges, + OnDestroy, OnInit, + Optional, Output, Renderer2, ViewEncapsulation @@ -23,6 +27,8 @@ import { fadeMotion } from 'ng-zorro-antd/core/animation'; import { warnDeprecation } from 'ng-zorro-antd/core/logger'; import { BooleanInput } from 'ng-zorro-antd/core/types'; import { InputBoolean } from 'ng-zorro-antd/core/util'; +import { Subject } from 'rxjs'; +import { takeUntil } from 'rxjs/operators'; @Component({ selector: 'nz-tag', @@ -47,10 +53,9 @@ import { InputBoolean } from 'ng-zorro-antd/core/util'; '(@fadeMotion.done)': 'afterAnimation($event)' } }) -export class NzTagComponent implements OnInit, OnChanges { +export class NzTagComponent implements OnInit, OnChanges, OnDestroy { static ngAcceptInputType_nzChecked: BooleanInput; static ngAcceptInputType_nzNoAnimation: BooleanInput; - presetColor = false; cacheClassName: string | null = null; @Input() nzMode: 'default' | 'closeable' | 'checkable' = 'default'; @@ -61,6 +66,10 @@ export class NzTagComponent implements OnInit, OnChanges { @Output() readonly nzOnClose = new EventEmitter(); @Output() readonly nzCheckedChange = new EventEmitter(); + dir: Direction; + + private destroy$ = new Subject(); + private isPresetColor(color?: string): boolean { if (!color) { return false; @@ -107,7 +116,21 @@ export class NzTagComponent implements OnInit, OnChanges { } } - constructor(private renderer: Renderer2, private elementRef: ElementRef) {} + constructor( + cdr: ChangeDetectorRef, + private renderer: Renderer2, + private elementRef: ElementRef, + @Optional() directionality: Directionality + ) { + directionality.change.pipe(takeUntil(this.destroy$)).subscribe(() => { + this.dir = directionality.value; + this.prepareComponentForRtl(); + cdr.detectChanges(); + }); + + this.dir = directionality.value; + this.prepareComponentForRtl(); + } ngOnInit(): void { this.updateClassMap(); @@ -116,4 +139,20 @@ export class NzTagComponent implements OnInit, OnChanges { ngOnChanges(): void { this.updateClassMap(); } + + ngOnDestroy(): void { + this.destroy$.next(); + this.destroy$.complete(); + } + + private prepareComponentForRtl(): void { + if (this.isRtlLayout) { + this.renderer.addClass(this.elementRef.nativeElement, 'ant-tag-rtl'); + } else { + this.renderer.removeClass(this.elementRef.nativeElement, 'ant-tag-rtl'); + } + } + get isRtlLayout(): boolean { + return this.dir === 'rtl'; + } } diff --git a/components/timeline/timeline.component.ts b/components/timeline/timeline.component.ts index e58645b68e5..1a9e07c669d 100644 --- a/components/timeline/timeline.component.ts +++ b/components/timeline/timeline.component.ts @@ -16,6 +16,7 @@ import { OnChanges, OnDestroy, OnInit, + Optional, QueryList, SimpleChange, SimpleChanges, @@ -25,6 +26,7 @@ import { import { Subject } from 'rxjs'; import { takeUntil } from 'rxjs/operators'; +import { Direction, Directionality } from '@angular/cdk/bidi'; import { NzTimelineItemComponent } from './timeline-item.component'; import { TimelineService } from './timeline.service'; @@ -45,6 +47,7 @@ export type NzTimelineMode = typeof TimelineModes[number]; [class.ant-timeline-alternate]="nzMode === 'alternate'" [class.ant-timeline-pending]="!!nzPending" [class.ant-timeline-reverse]="nzReverse" + [class.ant-timeline-rtl]="dir === 'rtl'" > @@ -83,10 +86,18 @@ export class NzTimelineComponent implements AfterContentInit, OnChanges, OnDestr isPendingBoolean: boolean = false; timelineItems: NzTimelineItemComponent[] = []; + dir: Direction; private destroy$ = new Subject(); - constructor(private cdr: ChangeDetectorRef, private timelineService: TimelineService) {} + constructor(private cdr: ChangeDetectorRef, private timelineService: TimelineService, @Optional() directionality: Directionality) { + directionality.change.pipe(takeUntil(this.destroy$)).subscribe(() => { + this.dir = directionality.value; + cdr.detectChanges(); + }); + + this.dir = directionality.value; + } ngOnChanges(changes: SimpleChanges): void { const { nzMode, nzReverse, nzPending } = changes; diff --git a/components/tooltip/base.ts b/components/tooltip/base.ts index fbe3aed2ff6..6968b996ac1 100644 --- a/components/tooltip/base.ts +++ b/components/tooltip/base.ts @@ -6,6 +6,7 @@ * found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE */ +import { Direction, Directionality } from '@angular/cdk/bidi'; import { CdkConnectedOverlay, CdkOverlayOrigin, ConnectedOverlayPositionChange, ConnectionPositionPair } from '@angular/cdk/overlay'; import { AfterViewInit, @@ -17,6 +18,8 @@ import { Input, OnChanges, OnDestroy, + OnInit, + Optional, Output, Renderer2, SimpleChanges, @@ -386,16 +389,36 @@ export abstract class NzTooltipBaseComponent implements OnDestroy { origin?: CdkOverlayOrigin; preferredPlacement = 'top'; + public dir: Direction; _classMap: NgClassInterface = {}; _hasBackdrop = false; _prefix = 'ant-tooltip-placement'; _positions: ConnectionPositionPair[] = [...DEFAULT_TOOLTIP_POSITIONS]; - constructor(public cdr: ChangeDetectorRef, public noAnimation?: NzNoAnimationDirective) {} + private destroy$ = new Subject(); + + get content(): string | TemplateRef | null { + return this.nzContent !== undefined ? this.nzContent : this.nzContentTemplate; + } + + get title(): string | TemplateRef | null { + return this.nzTitle !== undefined ? this.nzTitle : this.nzTitleTemplate; + } + + constructor(public cdr: ChangeDetectorRef, @Optional() directionality: Directionality, public noAnimation?: NzNoAnimationDirective) { + directionality.change.pipe(takeUntil(this.destroy$)).subscribe(() => { + this.dir = directionality.value; + cdr.detectChanges(); + }); + + this.dir = directionality.value; + } ngOnDestroy(): void { this.nzVisibleChange.complete(); + this.destroy$.next(); + this.destroy$.complete(); } show(): void { @@ -408,6 +431,11 @@ export abstract class NzTooltipBaseComponent implements OnDestroy { this.nzVisibleChange.next(true); this.cdr.detectChanges(); } + + // for ltr for overlay to display tooltip in correct placement in rtl direction. + if (this.origin && this.overlay && this.overlay.overlayRef && this.overlay.overlayRef.getDirection() === 'rtl') { + this.overlay.overlayRef.setDirection('ltr'); + } } hide(): void { diff --git a/components/tooltip/demo/template.ts b/components/tooltip/demo/template.ts index adb365b0947..d9b889b7afb 100644 --- a/components/tooltip/demo/template.ts +++ b/components/tooltip/demo/template.ts @@ -5,6 +5,14 @@ import { Component } from '@angular/core'; template: ` This Tooltip has an Icon Tooltip With Icon - ` + `, + styles: [ + ` + .anticon { + margin-right: 8px; + margin-left: 8px; + } + ` + ] }) export class NzDemoTooltipTemplateComponent {} diff --git a/components/tooltip/tooltip.ts b/components/tooltip/tooltip.ts index 964c035afe1..242f96b6fea 100644 --- a/components/tooltip/tooltip.ts +++ b/components/tooltip/tooltip.ts @@ -25,6 +25,7 @@ import { zoomBigMotion } from 'ng-zorro-antd/core/animation'; import { NzNoAnimationDirective } from 'ng-zorro-antd/core/no-animation'; import { NzTSType } from 'ng-zorro-antd/core/types'; +import { Directionality } from '@angular/cdk/bidi'; import { isTooltipEmpty, NzTooltipBaseComponent, NzTooltipBaseDirective, NzTooltipTrigger } from './base'; @Directive({ @@ -75,6 +76,7 @@ export class NzTooltipDirective extends NzTooltipBaseDirective { >
(); onChange: OnChangeType = _value => {}; onTouched: OnTouchedType = () => {}; @@ -300,11 +306,20 @@ export class NzTreeSelectComponent extends NzTreeBase implements ControlValueAcc private renderer: Renderer2, private cdr: ChangeDetectorRef, private elementRef: ElementRef, + @Optional() directionality: Directionality, @Host() @Optional() public noAnimation?: NzNoAnimationDirective ) { super(nzTreeService); + + directionality.change.pipe(takeUntil(this.destroy$)).subscribe(() => { + this.dir = directionality.value; + cdr.detectChanges(); + }); + this.renderer.addClass(this.elementRef.nativeElement, 'ant-select'); this.renderer.addClass(this.elementRef.nativeElement, 'ant-tree-select'); + + this.dir = directionality.value; } ngOnInit(): void { @@ -315,7 +330,8 @@ export class NzTreeSelectComponent extends NzTreeBase implements ControlValueAcc ngOnDestroy(): void { this.isDestroy = true; this.closeDropDown(); - this.selectionChangeSubscription.unsubscribe(); + this.destroy$.next(); + this.destroy$.complete(); } setDisabledState(isDisabled: boolean): void { @@ -432,7 +448,7 @@ export class NzTreeSelectComponent extends NzTreeBase implements ControlValueAcc subscribeSelectionChange(): Subscription { return merge( - this.nzTreeClick.pipe( + this.nzTreeClick.pipe(takeUntil(this.destroy$)).pipe( tap((event: NzFormatEmitEvent) => { const node = event.node!; if (this.nzCheckable && !node.isDisabled && !node.isDisableCheckbox) { diff --git a/components/typography/typography.component.ts b/components/typography/typography.component.ts index 52951e7aa02..b1de7e242ce 100644 --- a/components/typography/typography.component.ts +++ b/components/typography/typography.component.ts @@ -21,6 +21,7 @@ import { OnChanges, OnDestroy, OnInit, + Optional, Output, Renderer2, SimpleChanges, @@ -40,6 +41,7 @@ import { takeUntil } from 'rxjs/operators'; import { NzI18nService } from 'ng-zorro-antd/i18n'; +import { Direction, Directionality } from '@angular/cdk/bidi'; import { NzTextCopyComponent } from './text-copy.component'; import { NzTextEditComponent } from './text-edit.component'; @@ -83,6 +85,7 @@ const EXPAND_ELEMENT_CLASSNAME = 'ant-typography-expand'; preserveWhitespaces: false, host: { '[class.ant-typography]': '!editing', + '[class.ant-typography-rtl]': 'dir === "rtl"', '[class.ant-typography-edit-content]': 'editing', '[class.ant-typography-secondary]': 'nzType === "secondary"', '[class.ant-typography-warning]': 'nzType === "warning"', @@ -131,6 +134,7 @@ export class NzTypographyComponent implements OnInit, AfterViewInit, OnDestroy, isEllipsis: boolean = true; expanded: boolean = false; ellipsisStr = '...'; + dir: Direction; get canCssEllipsis(): boolean { return this.nzEllipsis && this.cssEllipsis && !this.expanded; @@ -153,8 +157,15 @@ export class NzTypographyComponent implements OnInit, AfterViewInit, OnDestroy, private platform: Platform, private i18n: NzI18nService, @Inject(DOCUMENT) document: NzSafeAny, - private resizeService: NzResizeService + private resizeService: NzResizeService, + @Optional() directionality: Directionality ) { + directionality.change.pipe(takeUntil(this.destroy$)).subscribe(() => { + this.dir = directionality.value; + cdr.detectChanges(); + }); + + this.dir = directionality.value; this.document = document; } diff --git a/scripts/site/_site/doc/app/app.component.html b/scripts/site/_site/doc/app/app.component.html index 47c6ee7a7ef..b07236a9be7 100644 --- a/scripts/site/_site/doc/app/app.component.html +++ b/scripts/site/_site/doc/app/app.component.html @@ -1,52 +1,66 @@ -
-
- +
+
+
-