Skip to content

Commit

Permalink
chore(module:message,notification): refactor (NG-ZORRO#4723)
Browse files Browse the repository at this point in the history
* feat(module:notification): support opening at different places

close NG-ZORRO#4338

chore(module:message,notification): refactor

* chore: test with cdr

* chore: remove console
  • Loading branch information
Wendell authored Mar 15, 2020
1 parent efb5c4a commit d2b93f1
Show file tree
Hide file tree
Showing 29 changed files with 428 additions and 442 deletions.
5 changes: 4 additions & 1 deletion angular.json
Original file line number Diff line number Diff line change
Expand Up @@ -240,5 +240,8 @@
"@schematics/angular:directive": {
"prefix": "app"
}
},
"cli": {
"analytics": false
}
}
}
2 changes: 1 addition & 1 deletion components/core/config/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ export interface ModalConfig {
export interface NotificationConfig extends MessageConfig {
nzTop?: string | number;
nzBottom?: string | number;
nzPlacement?: 'topLeft' | 'topRight' | 'bottomLeft' | 'bottomRight' | string;
nzPlacement?: 'topLeft' | 'topRight' | 'bottomLeft' | 'bottomRight';
}

export interface PageHeaderConfig {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@ import { Overlay } from '@angular/cdk/overlay';
import { ApplicationRef, ComponentFactoryResolver, EmbeddedViewRef, Injector, Type } from '@angular/core';
import { NzSingletonService } from 'ng-zorro-antd/core';

import { NzMessageContainerComponent } from './nz-message-container.component';
import { NzMessageData, NzMessageDataFilled, NzMessageDataOptions } from './nz-message.definitions';
import { NzMessageContainerComponent } from './message-container.component';
import { NzMessageData, NzMessageDataFilled, NzMessageDataOptions } from './typings';

let globalCounter = 0;

export class NzMessageBaseService<ContainerClass extends NzMessageContainerComponent, MessageData> {
protected _container: ContainerClass;
protected container: ContainerClass;

constructor(
private nzSingletonService: NzSingletonService,
Expand All @@ -27,15 +27,15 @@ export class NzMessageBaseService<ContainerClass extends NzMessageContainerCompo
private appRef: ApplicationRef,
private name: string = ''
) {
this._container = this.withContainer();
this.nzSingletonService.registerSingletonWithKey(this.name, this._container);
this.container = this.withContainer();
this.nzSingletonService.registerSingletonWithKey(this.name, this.container);
}

remove(messageId?: string): void {
if (messageId) {
this._container.removeMessage(messageId);
this.container.removeMessage(messageId);
} else {
this._container.removeMessageAll();
this.container.removeMessageAll();
}
}

Expand All @@ -44,16 +44,16 @@ export class NzMessageBaseService<ContainerClass extends NzMessageContainerCompo
...(message as NzMessageData),
...{
createdAt: new Date(),
messageId: this._generateMessageId(),
messageId: this.generateMessageId(),
options
}
};
this._container.createMessage(resultMessage);
this.container.createMessage(resultMessage);

return resultMessage;
}

protected _generateMessageId(): string {
protected generateMessageId(): string {
return `${this.name}-${globalCounter++}`;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@
* found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE
*/

import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit, ViewEncapsulation } from '@angular/core';
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core';
import { Subject } from 'rxjs';

import { MessageConfig, NzConfigService, toCssPixel } from 'ng-zorro-antd/core';
import { NzMessageDataFilled, NzMessageDataOptions } from './nz-message.definitions';
import { takeUntil } from 'rxjs/operators';
import { NzMessageDataFilled, NzMessageDataOptions } from './typings';

const NZ_CONFIG_COMPONENT_NAME = 'message';
const NZ_MESSAGE_DEFAULT_CONFIG: Required<MessageConfig> = {
Expand All @@ -27,9 +28,14 @@ const NZ_MESSAGE_DEFAULT_CONFIG: Required<MessageConfig> = {
selector: 'nz-message-container',
exportAs: 'nzMessageContainer',
preserveWhitespaces: false,
templateUrl: './nz-message-container.component.html'
template: `
<div class="ant-message" [style.top]="top">
<nz-message *ngFor="let message of messages" [nzMessage]="message"></nz-message>
</div>
`
})
export class NzMessageContainerComponent implements OnInit {
export class NzMessageContainerComponent implements OnInit, OnDestroy {
destroy$ = new Subject<void>();
messages: NzMessageDataFilled[] = [];
config: Required<MessageConfig>;
top: string | null;
Expand All @@ -42,17 +48,22 @@ export class NzMessageContainerComponent implements OnInit {
this.subscribeConfigChange();
}

ngOnDestroy(): void {
this.destroy$.next();
this.destroy$.complete();
}

/**
* Create a new message.
* @param message Parsed message configuration.
*/
createMessage(message: NzMessageDataFilled): void {
if (this.messages.length >= this.config.nzMaxStack) {
this.messages.splice(0, 1);
this.messages = this.messages.slice(1);
}
message.options = this._mergeMessageOptions(message.options);
message.options = this.mergeMessageOptions(message.options);
message.onClose = new Subject<boolean>();
this.messages.push(message);
this.messages = [...this.messages, message];
this.cdr.detectChanges();
}

Expand All @@ -65,6 +76,7 @@ export class NzMessageContainerComponent implements OnInit {
this.messages.some((message, index) => {
if (message.messageId === messageId) {
this.messages.splice(index, 1);
this.messages = [...this.messages];
this.cdr.detectChanges();
message.onClose!.next(userAction);
message.onClose!.complete();
Expand All @@ -89,7 +101,10 @@ export class NzMessageContainerComponent implements OnInit {
}

protected subscribeConfigChange(): void {
this.nzConfigService.getConfigChangeEventForComponent(NZ_CONFIG_COMPONENT_NAME).subscribe(() => this.updateConfig());
this.nzConfigService
.getConfigChangeEventForComponent(NZ_CONFIG_COMPONENT_NAME)
.pipe(takeUntil(this.destroy$))
.subscribe(() => this.updateConfig());
}

protected updateConfigFromConfigService(): Required<MessageConfig> {
Expand All @@ -104,7 +119,7 @@ export class NzMessageContainerComponent implements OnInit {
* Merge default options and custom message options
* @param options
*/
protected _mergeMessageOptions(options?: NzMessageDataOptions): NzMessageDataOptions {
protected mergeMessageOptions(options?: NzMessageDataOptions): NzMessageDataOptions {
const defaultOptions: NzMessageDataOptions = {
nzDuration: this.config.nzDuration,
nzAnimate: this.config.nzAnimate,
Expand Down
130 changes: 130 additions & 0 deletions components/message/message.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
/**
* @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 { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core';

import { moveUpMotion } from 'ng-zorro-antd/core';

import { NzMessageContainerComponent } from './message-container.component';
import { NzMessageDataFilled, NzMessageDataOptions } from './typings';

@Component({
changeDetection: ChangeDetectionStrategy.OnPush,
encapsulation: ViewEncapsulation.None,
selector: 'nz-message',
exportAs: 'nzMessage',
preserveWhitespaces: false,
animations: [moveUpMotion],
template: `
<div class="ant-message-notice" [@moveUpMotion]="nzMessage.state" (mouseenter)="onEnter()" (mouseleave)="onLeave()">
<div class="ant-message-notice-content">
<div class="ant-message-custom-content" [ngClass]="'ant-message-' + nzMessage.type">
<ng-container [ngSwitch]="nzMessage.type">
<i *ngSwitchCase="'success'" nz-icon nzType="check-circle"></i>
<i *ngSwitchCase="'info'" nz-icon nzType="info-circle"></i>
<i *ngSwitchCase="'warning'" nz-icon nzType="exclamation-circle"></i>
<i *ngSwitchCase="'error'" nz-icon nzType="close-circle"></i>
<i *ngSwitchCase="'loading'" nz-icon nzType="loading"></i>
</ng-container>
<ng-container *nzStringTemplateOutlet="nzMessage.content">
<span [innerHTML]="nzMessage.content"></span>
</ng-container>
</div>
</div>
</div>
`
})
export class NzMessageComponent implements OnInit, OnDestroy {
@Input() nzMessage: NzMessageDataFilled;
@Input() nzIndex: number;

protected options: Required<NzMessageDataOptions>;

// Whether to set a timeout to destroy itself.
private autoClose: boolean;

private eraseTimer: number | null = null;
private eraseTimingStart: number;
private eraseTTL: number; // Time to live.

constructor(private messageContainerComponent: NzMessageContainerComponent, protected cdr: ChangeDetectorRef) {}

ngOnInit(): void {
// `NzMessageContainer` does its job so all properties cannot be undefined.
this.options = this.nzMessage.options as Required<NzMessageDataOptions>;

if (this.options.nzAnimate) {
this.nzMessage.state = 'enter';
}

this.autoClose = this.options.nzDuration > 0;

if (this.autoClose) {
this.initErase();
this.startEraseTimeout();
}
}

ngOnDestroy(): void {
if (this.autoClose) {
this.clearEraseTimeout();
}
}

onEnter(): void {
if (this.autoClose && this.options.nzPauseOnHover) {
this.clearEraseTimeout();
this.updateTTL();
}
}

onLeave(): void {
if (this.autoClose && this.options.nzPauseOnHover) {
this.startEraseTimeout();
}
}

// Remove self
protected destroy(userAction: boolean = false): void {
if (this.options.nzAnimate) {
this.nzMessage.state = 'leave';
this.cdr.detectChanges();
setTimeout(() => this.messageContainerComponent.removeMessage(this.nzMessage.messageId, userAction), 200);
} else {
this.messageContainerComponent.removeMessage(this.nzMessage.messageId, userAction);
}
}

private initErase(): void {
this.eraseTTL = this.options.nzDuration;
this.eraseTimingStart = Date.now();
}

private updateTTL(): void {
if (this.autoClose) {
this.eraseTTL -= Date.now() - this.eraseTimingStart;
}
}

private startEraseTimeout(): void {
if (this.eraseTTL > 0) {
this.clearEraseTimeout();
this.eraseTimer = setTimeout(() => this.destroy(), this.eraseTTL);
this.eraseTimingStart = Date.now();
} else {
this.destroy();
}
}

private clearEraseTimeout(): void {
if (this.eraseTimer !== null) {
clearTimeout(this.eraseTimer);
this.eraseTimer = null;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,13 @@ import { NgModule } from '@angular/core';
import { NzOutletModule } from 'ng-zorro-antd/core';
import { NzIconModule } from 'ng-zorro-antd/icon';

import { NzMessageContainerComponent } from './nz-message-container.component';
import { NzMessageComponent } from './nz-message.component';
import { NzMessageServiceModule } from './nz-message.service.module';
import { NzMessageContainerComponent } from './message-container.component';
import { NzMessageComponent } from './message.component';
import { NzMessageServiceModule } from './message.service.module';

@NgModule({
imports: [CommonModule, OverlayModule, NzIconModule, NzOutletModule, NzMessageServiceModule],
declarations: [NzMessageContainerComponent, NzMessageComponent],
entryComponents: [NzMessageContainerComponent]
entryComponents: [NzMessageContainerComponent, NzMessageComponent]
})
export class NzMessageModule {}
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ import { Overlay } from '@angular/cdk/overlay';
import { ApplicationRef, ComponentFactoryResolver, Injectable, Injector, TemplateRef } from '@angular/core';
import { NzSingletonService } from 'ng-zorro-antd/core';

import { NzMessageBaseService } from './nz-message-base.service';
import { NzMessageContainerComponent } from './nz-message-container.component';
import { NzMessageData, NzMessageDataFilled, NzMessageDataOptions } from './nz-message.definitions';
import { NzMessageServiceModule } from './nz-message.service.module';
import { NzMessageBaseService } from './base.service';
import { NzMessageContainerComponent } from './message-container.component';
import { NzMessageServiceModule } from './message.service.module';
import { NzMessageData, NzMessageDataFilled, NzMessageDataOptions } from './typings';

@Injectable({
providedIn: NzMessageServiceModule
Expand Down
Loading

0 comments on commit d2b93f1

Please sign in to comment.