diff --git a/components/affix/nz-affix.component.ts b/components/affix/nz-affix.component.ts index fb61a41ba0e..d3fd60c567b 100644 --- a/components/affix/nz-affix.component.ts +++ b/components/affix/nz-affix.component.ts @@ -1,3 +1,4 @@ +import { Platform } from '@angular/cdk/platform'; import { DOCUMENT } from '@angular/common'; import { ChangeDetectionStrategy, @@ -35,10 +36,12 @@ import { throttleByAnimationFrameDecorator } from '../core/util/throttleByAnimat export class NzAffixComponent implements OnInit, OnDestroy { @Input() set nzTarget(value: string | Element | Window) { - this.clearEventListeners(); - this._target = typeof value === 'string' ? this.doc.querySelector(value) : value || window; - this.setTargetEventListeners(); - this.updatePosition({} as Event); + if (this.platform.isBrowser) { + this.clearEventListeners(); + this._target = typeof value === 'string' ? this.doc.querySelector(value) : value || window; + this.setTargetEventListeners(); + this.updatePosition({} as Event); + } } @Input() @@ -73,13 +76,21 @@ export class NzAffixComponent implements OnInit, OnDestroy { private affixStyle: NGStyleInterface | undefined; private placeholderStyle: NGStyleInterface | undefined; - private _target: Element | Window = window; + private _target: Element | Window | null = null; private _offsetTop: number | null; private _offsetBottom: number | null; // tslint:disable-next-line:no-any - constructor(_el: ElementRef, private scrollSrv: NzScrollService, @Inject(DOCUMENT) private doc: any) { + constructor( + _el: ElementRef, + private scrollSrv: NzScrollService, + @Inject(DOCUMENT) private doc: any, + private platform: Platform + ) { this.placeholderNode = _el.nativeElement; + if (this.platform.isBrowser) { + this._target = window; + } } ngOnInit(): void { @@ -125,15 +136,19 @@ export class NzAffixComponent implements OnInit, OnDestroy { private setTargetEventListeners(): void { this.clearEventListeners(); - this.events.forEach((eventName: string) => { - this._target.addEventListener(eventName, this.updatePosition, false); - }); + if (this.platform.isBrowser) { + this.events.forEach((eventName: string) => { + this._target!.addEventListener(eventName, this.updatePosition, false); + }); + } } private clearEventListeners(): void { - this.events.forEach(eventName => { - this._target.removeEventListener(eventName, this.updatePosition, false); - }); + if (this.platform.isBrowser) { + this.events.forEach(eventName => { + this._target!.removeEventListener(eventName, this.updatePosition, false); + }); + } } private getTargetRect(target: Element | Window | undefined): ClientRect { @@ -205,11 +220,14 @@ export class NzAffixComponent implements OnInit, OnDestroy { @throttleByAnimationFrameDecorator() updatePosition(e: Event): void { - const targetNode = this._target; + if (!this.platform.isBrowser) { + return; + } + const targetNode = this._target as (HTMLElement | Window); // Backwards support let offsetTop = this.nzOffsetTop; - const scrollTop = this.scrollSrv.getScroll(targetNode, true); - const elemOffset = this.getOffset(this.placeholderNode, targetNode); + const scrollTop = this.scrollSrv.getScroll(targetNode!, true); + const elemOffset = this.getOffset(this.placeholderNode, targetNode!); const fixedNode = this.fixedEl.nativeElement as HTMLElement; const elemSize = { width: fixedNode.offsetWidth, @@ -227,7 +245,7 @@ export class NzAffixComponent implements OnInit, OnDestroy { offsetMode.top = typeof offsetTop === 'number'; offsetMode.bottom = typeof this._offsetBottom === 'number'; } - const targetRect = this.getTargetRect(targetNode); + const targetRect = this.getTargetRect(targetNode as Window); const targetInnerHeight = (targetNode as Window).innerHeight || (targetNode as HTMLElement).clientHeight; if (scrollTop >= elemOffset.top - (offsetTop as number) && offsetMode.top) { const width = elemOffset.width; diff --git a/components/anchor/nz-anchor-link.component.ts b/components/anchor/nz-anchor-link.component.ts index a63581eae74..37b467520d4 100644 --- a/components/anchor/nz-anchor-link.component.ts +++ b/components/anchor/nz-anchor-link.component.ts @@ -1,3 +1,4 @@ +import { Platform } from '@angular/cdk/platform'; import { ChangeDetectionStrategy, ChangeDetectorRef, @@ -54,6 +55,7 @@ export class NzAnchorLinkComponent implements OnInit, OnDestroy { public elementRef: ElementRef, private anchorComp: NzAnchorComponent, private cdr: ChangeDetectorRef, + private platform: Platform, renderer: Renderer2 ) { renderer.addClass(elementRef.nativeElement, 'ant-anchor-link'); @@ -66,7 +68,9 @@ export class NzAnchorLinkComponent implements OnInit, OnDestroy { goToClick(e: Event): void { e.preventDefault(); e.stopPropagation(); - this.anchorComp.handleScrollTo(this); + if (this.platform.isBrowser) { + this.anchorComp.handleScrollTo(this); + } } markForCheck(): void { diff --git a/components/anchor/nz-anchor.component.ts b/components/anchor/nz-anchor.component.ts index dc3d6fe32e4..45af1fde62c 100644 --- a/components/anchor/nz-anchor.component.ts +++ b/components/anchor/nz-anchor.component.ts @@ -1,3 +1,4 @@ +import { Platform } from '@angular/cdk/platform'; import { DOCUMENT } from '@angular/common'; import { AfterViewInit, @@ -74,8 +75,13 @@ export class NzAnchorComponent implements OnDestroy, AfterViewInit { private scroll$: Subscription | null = null; private destroyed = false; - /* tslint:disable-next-line:no-any */ - constructor(private scrollSrv: NzScrollService, @Inject(DOCUMENT) private doc: any, private cdr: ChangeDetectorRef) {} + constructor( + private scrollSrv: NzScrollService, + /* tslint:disable-next-line:no-any */ + @Inject(DOCUMENT) private doc: any, + private cdr: ChangeDetectorRef, + private platform: Platform + ) {} registerLink(link: NzAnchorLinkComponent): void { this.links.push(link); @@ -99,6 +105,9 @@ export class NzAnchorComponent implements OnDestroy, AfterViewInit { } private registerScrollEvent(): void { + if (!this.platform.isBrowser) { + return; + } this.removeListen(); this.scroll$ = fromEvent(this.getTarget(), 'scroll') .pipe( diff --git a/components/avatar/nz-avatar.component.ts b/components/avatar/nz-avatar.component.ts index 632dbf9d8b9..ada76772b0f 100644 --- a/components/avatar/nz-avatar.component.ts +++ b/components/avatar/nz-avatar.component.ts @@ -1,3 +1,4 @@ +import { Platform } from '@angular/cdk/platform'; import { ChangeDetectionStrategy, ChangeDetectorRef, @@ -52,7 +53,8 @@ export class NzAvatarComponent implements OnChanges { private elementRef: ElementRef, private cd: ChangeDetectorRef, private updateHostClassService: NzUpdateHostClassService, - private renderer: Renderer2 + private renderer: Renderer2, + private platform: Platform ) {} setClass(): this { @@ -114,9 +116,11 @@ export class NzAvatarComponent implements OnChanges { private notifyCalc(): this { // If use ngAfterViewChecked, always demands more computations, so...... - setTimeout(() => { - this.calcStringSize(); - }); + if (this.platform.isBrowser) { + setTimeout(() => { + this.calcStringSize(); + }); + } return this; } diff --git a/components/back-top/nz-back-top.component.ts b/components/back-top/nz-back-top.component.ts index 8cc1438675b..96c0346a9ff 100644 --- a/components/back-top/nz-back-top.component.ts +++ b/components/back-top/nz-back-top.component.ts @@ -1,3 +1,4 @@ +import { Platform } from '@angular/cdk/platform'; import { DOCUMENT } from '@angular/common'; import { ChangeDetectionStrategy, @@ -56,7 +57,12 @@ export class NzBackTopComponent implements OnInit, OnDestroy { @Output() readonly nzClick: EventEmitter = new EventEmitter(); // tslint:disable-next-line:no-any - constructor(private scrollSrv: NzScrollService, @Inject(DOCUMENT) private doc: any, private cd: ChangeDetectorRef) {} + constructor( + private scrollSrv: NzScrollService, + @Inject(DOCUMENT) private doc: any, + private platform: Platform, + private cd: ChangeDetectorRef + ) {} ngOnInit(): void { if (!this.scroll$) { @@ -88,6 +94,9 @@ export class NzBackTopComponent implements OnInit, OnDestroy { } private registerScrollEvent(): void { + if (!this.platform.isBrowser) { + return; + } this.removeListen(); this.handleScroll(); this.scroll$ = fromEvent(this.getTarget(), 'scroll') diff --git a/components/carousel/nz-carousel.component.ts b/components/carousel/nz-carousel.component.ts index 0e358cfb1ef..aadca3527ac 100755 --- a/components/carousel/nz-carousel.component.ts +++ b/components/carousel/nz-carousel.component.ts @@ -1,4 +1,5 @@ import { LEFT_ARROW, RIGHT_ARROW } from '@angular/cdk/keycodes'; +import { Platform } from '@angular/cdk/platform'; import { AfterContentInit, AfterViewInit, @@ -99,7 +100,8 @@ export class NzCarouselComponent implements AfterViewInit, AfterContentInit, OnD public elementRef: ElementRef, private renderer: Renderer2, private cdr: ChangeDetectorRef, - private ngZone: NgZone + private ngZone: NgZone, + private platform: Platform ) { renderer.addClass(elementRef.nativeElement, 'ant-carousel'); } @@ -118,16 +120,18 @@ export class NzCarouselComponent implements AfterViewInit, AfterContentInit, OnD }) ); - this.ngZone.runOutsideAngular(() => { - this.subs_.add( - fromEvent(window, 'resize') - .pipe(debounceTime(50)) - .subscribe(() => { - this.renderContent(); - this.setTransition(); - }) - ); - }); + if (this.platform.isBrowser) { + this.ngZone.runOutsideAngular(() => { + this.subs_.add( + fromEvent(window, 'resize') + .pipe(debounceTime(50)) + .subscribe(() => { + this.renderContent(); + this.setTransition(); + }) + ); + }); + } // When used in modals (drawers maybe too), it should render itself asynchronously. // Refer to https://github.com/NG-ZORRO/ng-zorro-antd/issues/2387 @@ -158,8 +162,10 @@ export class NzCarouselComponent implements AfterViewInit, AfterContentInit, OnD this.slideContents.forEach((slide, i) => (slide.isActive = index === i)); this.setUpNextScroll(); this.cdr.markForCheck(); - // Should trigger the following when animation is done. The transition takes 0.5 seconds according to the CSS. - setTimeout(() => this.nzAfterChange.emit(index), this.nzTransitionSpeed); + if (this.platform.isBrowser) { + // Should trigger the following when animation is done. The transition takes 0.5 seconds according to the CSS. + setTimeout(() => this.nzAfterChange.emit(index), this.nzTransitionSpeed); + } } } @@ -250,11 +256,13 @@ export class NzCarouselComponent implements AfterViewInit, AfterContentInit, OnD * Make a carousel scroll to `this.nextIndex` after `this.nzAutoPlaySpeed` milliseconds. */ private setUpNextScroll(): void { - this.clearTimeout(); - if (this.nzAutoPlay && this.nzAutoPlaySpeed > 0) { - this.transitionAction = setTimeout(() => { - this.setContentActive(this.nextIndex); - }, this.nzAutoPlaySpeed); + if (this.platform.isBrowser) { + this.clearTimeout(); + if (this.nzAutoPlay && this.nzAutoPlaySpeed > 0) { + this.transitionAction = setTimeout(() => { + this.setContentActive(this.nextIndex); + }, this.nzAutoPlaySpeed); + } } } diff --git a/components/icon/nz-icon.directive.ts b/components/icon/nz-icon.directive.ts index a9400a07fc2..fcd0f36a77d 100644 --- a/components/icon/nz-icon.directive.ts +++ b/components/icon/nz-icon.directive.ts @@ -1,3 +1,4 @@ +import { Platform } from '@angular/cdk/platform'; import { AfterContentChecked, Directive, @@ -170,7 +171,12 @@ export class NzIconDirective extends IconDirective implements OnInit, OnChanges, } } - constructor(public iconService: NzIconService, public elementRef: ElementRef, public renderer: Renderer2) { + constructor( + public iconService: NzIconService, + public elementRef: ElementRef, + public renderer: Renderer2, + private platform: Platform + ) { super(iconService, elementRef, renderer); } @@ -192,13 +198,15 @@ export class NzIconDirective extends IconDirective implements OnInit, OnChanges, this.iconService.warnAPI('old'); // Get `type` from `className`. If not, initial rendering would be missed. this.classChangeHandler(this.el.className); - // Add `class` mutation observer. - this.classNameObserver = new MutationObserver((mutations: MutationRecord[]) => { - mutations - .filter((mutation: MutationRecord) => mutation.attributeName === 'class') - .forEach((mutation: MutationRecord) => this.classChangeHandler((mutation.target as HTMLElement).className)); - }); - this.classNameObserver.observe(this.el, { attributes: true }); + if (this.platform.isBrowser) { + // Add `class` mutation observer. + this.classNameObserver = new MutationObserver((mutations: MutationRecord[]) => { + mutations + .filter((mutation: MutationRecord) => mutation.attributeName === 'class') + .forEach((mutation: MutationRecord) => this.classChangeHandler((mutation.target as HTMLElement).className)); + }); + this.classNameObserver.observe(this.el, { attributes: true }); + } } // If `classList` does not contain `anticon`, add it before other class names. if (!this.el.classList.contains('anticon')) { diff --git a/components/menu/nz-submenu.component.ts b/components/menu/nz-submenu.component.ts index b37e2b24399..be8f186d99d 100644 --- a/components/menu/nz-submenu.component.ts +++ b/components/menu/nz-submenu.component.ts @@ -1,4 +1,5 @@ import { CdkConnectedOverlay, CdkOverlayOrigin, ConnectedOverlayPositionChange } from '@angular/cdk/overlay'; +import { Platform } from '@angular/cdk/platform'; import { AfterContentInit, ChangeDetectionStrategy, @@ -100,7 +101,7 @@ export class NzSubMenuComponent implements OnInit, OnDestroy, AfterContentInit, } setTriggerWidth(): void { - if (this.nzSubmenuService.mode === 'horizontal') { + if (this.nzSubmenuService.mode === 'horizontal' && this.platform.isBrowser) { this.triggerWidth = this.cdkOverlayOrigin.nativeElement.getBoundingClientRect().width; } } @@ -128,6 +129,7 @@ export class NzSubMenuComponent implements OnInit, OnDestroy, AfterContentInit, private cdr: ChangeDetectorRef, public nzSubmenuService: NzSubmenuService, private nzUpdateHostClassService: NzUpdateHostClassService, + private platform: Platform, @Host() @Optional() public noAnimation?: NzNoAnimationDirective ) {} diff --git a/components/modal/modal-util.ts b/components/modal/modal-util.ts deleted file mode 100644 index 25204954e1a..00000000000 --- a/components/modal/modal-util.ts +++ /dev/null @@ -1,24 +0,0 @@ -export interface ClickPosition { - x: number; - y: number; -} - -export class ModalUtil { - private lastPosition: ClickPosition | null = null; - - constructor(private document: Document) { - this.listenDocumentClick(); - } - - getLastClickPosition(): ClickPosition | null { - return this.lastPosition; - } - - listenDocumentClick(): void { - this.document.addEventListener('click', (event: MouseEvent) => { - this.lastPosition = { x: event.clientX, y: event.clientY }; - }); - } -} - -export default new ModalUtil(document); diff --git a/components/modal/nz-modal.component.ts b/components/modal/nz-modal.component.ts index b8f63e40a66..985845024a2 100644 --- a/components/modal/nz-modal.component.ts +++ b/components/modal/nz-modal.component.ts @@ -31,9 +31,9 @@ import { fromEvent, Observable, Subject } from 'rxjs'; import { takeUntil } from 'rxjs/operators'; import { InputBoolean } from '../core/util/convert'; +import { getElementOffset } from '../core/util/dom'; import { isPromise } from '../core/util/is-promise'; import { NzI18nService } from '../i18n/nz-i18n.service'; -import ModalUtil from './modal-util'; import { NzModalConfig, NZ_MODAL_CONFIG } from './nz-modal-config'; import { NzModalControlService } from './nz-modal-control.service'; import { NzModalRef } from './nz-modal-ref.class'; @@ -471,10 +471,12 @@ export class NzModalComponent extends NzModalRef // Update transform-origin to the last click position on document private updateTransformOrigin(): void { const modalElement = this.modalContainer.nativeElement as HTMLElement; - const lastPosition = ModalUtil.getLastClickPosition(); - if (lastPosition) { - this.transformOrigin = `${lastPosition.x - modalElement.offsetLeft}px ${lastPosition.y - - modalElement.offsetTop}px 0px`; + if (this.previouslyFocusedElement) { + const previouslyDOMRect = this.previouslyFocusedElement.getBoundingClientRect(); + const lastPosition = getElementOffset(this.previouslyFocusedElement); + const x = lastPosition.left + previouslyDOMRect.width / 2; + const y = lastPosition.top + previouslyDOMRect.height / 2; + this.transformOrigin = `${x - modalElement.offsetLeft}px ${y - modalElement.offsetTop}px 0px`; } } diff --git a/components/select/nz-select.component.ts b/components/select/nz-select.component.ts index fa134c83924..af727308ddf 100644 --- a/components/select/nz-select.component.ts +++ b/components/select/nz-select.component.ts @@ -1,5 +1,6 @@ import { FocusMonitor } from '@angular/cdk/a11y'; import { CdkConnectedOverlay, CdkOverlayOrigin, ConnectedOverlayPositionChange } from '@angular/cdk/overlay'; +import { Platform } from '@angular/cdk/platform'; import { forwardRef, AfterContentInit, @@ -226,7 +227,9 @@ export class NzSelectComponent implements ControlValueAccessor, OnInit, AfterVie } updateCdkConnectedOverlayStatus(): void { - this.triggerWidth = this.cdkOverlayOrigin.elementRef.nativeElement.getBoundingClientRect().width; + if (this.platform.isBrowser) { + this.triggerWidth = this.cdkOverlayOrigin.elementRef.nativeElement.getBoundingClientRect().width; + } } updateCdkConnectedOverlayPositions(): void { @@ -242,6 +245,7 @@ export class NzSelectComponent implements ControlValueAccessor, OnInit, AfterVie public nzSelectService: NzSelectService, private cdr: ChangeDetectorRef, private focusMonitor: FocusMonitor, + private platform: Platform, elementRef: ElementRef, @Host() @Optional() public noAnimation?: NzNoAnimationDirective ) { diff --git a/components/slider/nz-slider.component.ts b/components/slider/nz-slider.component.ts index 69e567b8382..9c96ce90c60 100644 --- a/components/slider/nz-slider.component.ts +++ b/components/slider/nz-slider.component.ts @@ -1,3 +1,4 @@ +import { Platform } from '@angular/cdk/platform'; import { forwardRef, ChangeDetectionStrategy, @@ -84,13 +85,15 @@ export class NzSliderComponent implements ControlValueAccessor, OnInit, OnChange private dragMove_: Subscription | null; private dragEnd_: Subscription | null; - constructor(private cdr: ChangeDetectorRef) {} + constructor(private cdr: ChangeDetectorRef, private platform: Platform) {} ngOnInit(): void { this.handles = this.generateHandles(this.nzRange ? 2 : 1); this.sliderDOM = this.slider.nativeElement; this.marksArray = this.nzMarks ? this.generateMarkItems(this.nzMarks) : null; - this.createDraggingObservables(); + if (this.platform.isBrowser) { + this.createDraggingObservables(); + } this.toggleDragDisabled(this.nzDisabled); if (this.getValue() === null) { diff --git a/components/statistic/nz-countdown.component.ts b/components/statistic/nz-countdown.component.ts index a485a94e3e8..873572b766a 100644 --- a/components/statistic/nz-countdown.component.ts +++ b/components/statistic/nz-countdown.component.ts @@ -1,3 +1,4 @@ +import { Platform } from '@angular/cdk/platform'; import { ChangeDetectionStrategy, ChangeDetectorRef, @@ -30,7 +31,7 @@ export class NzCountdownComponent extends NzStatisticComponent implements OnInit private target: number; private updater_: Subscription | null; - constructor(private cdr: ChangeDetectorRef, private ngZone: NgZone) { + constructor(private cdr: ChangeDetectorRef, private ngZone: NgZone, private platform: Platform) { super(); } @@ -61,13 +62,15 @@ export class NzCountdownComponent extends NzStatisticComponent implements OnInit } startTimer(): void { - this.ngZone.runOutsideAngular(() => { - this.stopTimer(); - this.updater_ = interval(REFRESH_INTERVAL).subscribe(() => { - this.updateValue(); - this.cdr.detectChanges(); + if (this.platform.isBrowser) { + this.ngZone.runOutsideAngular(() => { + this.stopTimer(); + this.updater_ = interval(REFRESH_INTERVAL).subscribe(() => { + this.updateValue(); + this.cdr.detectChanges(); + }); }); - }); + } } stopTimer(): void { diff --git a/components/table/nz-table.component.ts b/components/table/nz-table.component.ts index 83e11beb8b6..6b007d2481d 100644 --- a/components/table/nz-table.component.ts +++ b/components/table/nz-table.component.ts @@ -1,3 +1,4 @@ +import { Platform } from '@angular/cdk/platform'; import { CdkVirtualScrollViewport } from '@angular/cdk/scrolling'; import { AfterContentInit, @@ -213,6 +214,7 @@ export class NzTableComponent implements OnInit, AfterViewInit, OnDestr private cdr: ChangeDetectorRef, private nzMeasureScrollbarService: NzMeasureScrollbarService, private i18n: NzI18nService, + private platform: Platform, elementRef: ElementRef ) { renderer.addClass(elementRef.nativeElement, 'ant-table-wrapper'); @@ -240,6 +242,9 @@ export class NzTableComponent implements OnInit, AfterViewInit, OnDestr } ngAfterViewInit(): void { + if (!this.platform.isBrowser) { + return; + } setTimeout(() => this.setScrollPositionClassName()); this.ngZone.runOutsideAngular(() => { merge( diff --git a/components/timeline/nz-timeline.component.ts b/components/timeline/nz-timeline.component.ts index db361d65deb..9a2cd961443 100644 --- a/components/timeline/nz-timeline.component.ts +++ b/components/timeline/nz-timeline.component.ts @@ -1,3 +1,4 @@ +import { Platform } from '@angular/cdk/platform'; import { AfterContentInit, ChangeDetectionStrategy, @@ -44,7 +45,7 @@ export class NzTimelineComponent implements AfterContentInit, OnChanges, OnDestr private destroy$ = new Subject(); - constructor(private cdr: ChangeDetectorRef) {} + constructor(private cdr: ChangeDetectorRef, private platform: Platform) {} ngOnChanges(changes: SimpleChanges): void { const modeChanges = changes.nzMode; @@ -100,7 +101,9 @@ export class NzTimelineComponent implements AfterContentInit, OnChanges, OnDestr } private reverseChildTimelineDots(): void { - reverseChildNodes(this.timeline.nativeElement as HTMLElement); - this.updateChildren(); + if (this.platform.isBrowser) { + reverseChildNodes(this.timeline.nativeElement as HTMLElement); + this.updateChildren(); + } } } diff --git a/components/upload/nz-upload-list.component.ts b/components/upload/nz-upload-list.component.ts index 73aedee5276..b28e6ba9ab6 100644 --- a/components/upload/nz-upload-list.component.ts +++ b/components/upload/nz-upload-list.component.ts @@ -1,4 +1,5 @@ import { animate, style, transition, trigger } from '@angular/animations'; +import { Platform } from '@angular/cdk/platform'; import { ChangeDetectionStrategy, ChangeDetectorRef, @@ -114,6 +115,9 @@ export class NzUploadListComponent implements OnChanges { } private genThumb(): void { + if (!this.platform.isBrowser) { + return; + } // tslint:disable-next-line:no-any const win = window as any; if ( @@ -166,7 +170,8 @@ export class NzUploadListComponent implements OnChanges { constructor( private el: ElementRef, private cdr: ChangeDetectorRef, - private updateHostClassService: NzUpdateHostClassService + private updateHostClassService: NzUpdateHostClassService, + private platform: Platform ) {} detectChanges(): void {