ng-ytl-zorro-antd
Version:
An enterprise-class UI components based on Ant Design and Angular
358 lines (319 loc) • 10.6 kB
text/typescript
import {
AfterViewInit,
Component,
ComponentFactory,
ComponentRef,
ElementRef,
EventEmitter,
HostListener,
Inject,
Input,
OnDestroy,
OnInit,
Output,
TemplateRef,
ViewChild,
ViewContainerRef,
ViewEncapsulation,
} from '@angular/core';
import { NzLocaleService } from '../locale/index';
import { toBoolean } from '../util/convert';
import nzGlobalMonitor from '../util/nz-global-monitor';
import { NzModalSubject } from './nz-modal-subject.service';
interface Position {
x: number;
y: number;
}
export class NzModalComponent implements OnInit, OnDestroy, AfterViewInit {
private _confirmLoading = false;
private _maskClosable = true;
_footerHide = false;
_closable = true;
_visible = false;
_prefixCls = 'ant-modal';
_maskClassMap;
_bodyClassMap;
_bodyStyleMap;
_width = '520px';
_zIndex = 1000;
_title = '';
_titleTpl: TemplateRef<void>;
_content = '';
_contentTpl: TemplateRef<void>;
_footerTpl: TemplateRef<void>;
_okText = this._locale.translate('Modal.okText');
_cancelText = this._locale.translate('Modal.cancelText');
_style: object = {};
_wrapClass = `${this._prefixCls}-wrap`;
_customClass = '';
_animationStatus = '';
_bodyComponent: ComponentFactory<void>;
_componentParams: object = {};
modalId = `nzModal${nzGlobalMonitor.getGlobalCount()}`;
contentEl: ElementRef;
bodyEl: ViewContainerRef;
set nzVisible(value: boolean) {
const visible = toBoolean(value);
if (this._visible === visible) {
return;
}
if (visible) {
this.anmiateFade('enter');
this.subject.next('onShow');
// 每次触发点击事件的时候,通过全局监听的类,记录下点击的位置,计算动画的origin
setTimeout(() => {
this.setStyles({
x: nzGlobalMonitor.lastClickPos.x || 0,
y: nzGlobalMonitor.lastClickPos.y || 0
});
});
} else {
this.anmiateFade('leave');
this.subject.next('onHide');
}
this._visible = visible;
this.nzVisibleChange.emit(this._visible);
// 设置全局的overflow样式
nzGlobalMonitor.setDocumentOverflowHidden(visible);
}
get nzVisible(): boolean {
return this._visible;
}
set nzConfirmLoading(value: boolean) {
this._confirmLoading = toBoolean(value);
}
set nzClosable(value: boolean) {
this._closable = toBoolean(value);
}
set nzClass(value: string) {
this._customClass = value;
}
set nzWidth(value: string | number) {
this._width = typeof value === 'number' ? value + 'px' : value;
}
set nzZIndex(value: number) {
this._zIndex = value;
}
set nzTitle(value: string | TemplateRef<void>) {
// TODO: should guard for string instead, all types are theoretically structural
if (value instanceof TemplateRef) {
this._titleTpl = value;
} else {
this._title = value;
}
}
set nzContent(value: string | TemplateRef<void> | ComponentFactory<void>) {
if (value instanceof ComponentFactory) {
// 如果容器对象已存在,则直接渲染,如果不存在,则设置到_bodyComponent,在ngAfterViewInit中执行
if (this.bodyEl) {
const compRef: ComponentRef<void> = this.bodyEl.createComponent(value, null, this._vcr.injector);
Object.assign(compRef.instance, this._componentParams);
} else {
this._bodyComponent = value;
}
} else if (value instanceof TemplateRef) {
this._contentTpl = value;
} else {
this._content = value;
}
}
set nzFooter(value: TemplateRef<void> | boolean) {
if (value instanceof TemplateRef) {
this._footerTpl = value;
} else {
this._footerHide = !toBoolean(value);
}
}
set nzOkText(value: string) {
this._okText = value;
}
set nzCancelText(value: string) {
this._cancelText = value;
}
set nzMaskClosable(value: boolean) {
this._maskClosable = toBoolean(value);
}
set nzStyle(value: object) {
this._style = value;
}
set nzWrapClassName(value: string) {
if (value) {
this._wrapClass = `${this._prefixCls}-wrap ${value}`;
}
}
set nzComponentParams(value: object) {
this._componentParams = value;
}
nzVisibleChange: EventEmitter<boolean> = new EventEmitter();
nzOnOk: EventEmitter<MouseEvent> = new EventEmitter();
// TODO: reconsider the payload type
nzOnCancel: EventEmitter<MouseEvent | KeyboardEvent> = new EventEmitter();
onEsc(e: KeyboardEvent): void {
if (this._maskClosable) {
this.clickCancel(e);
}
}
setStyles(origin?: { x: number, y: number }): void {
const el = this.contentEl.nativeElement;
const transformOrigin = origin ? `${origin.x - el.offsetLeft}px ${origin.y - el.offsetTop}px 0px` : '';
// TODO: spread on literal has been disallow on latest proposal
this._bodyStyleMap = {
...{
'width' : this._width,
'transform-origin': transformOrigin
},
...this._style
};
}
setClassMap(): void {
this._maskClassMap = {
[`${this._prefixCls}-mask`] : true,
[`${this._prefixCls}-mask-hidden`]: !this._visible && !this._animationStatus,
'fade-enter' : this._animationStatus === 'enter',
'fade-enter-active' : this._animationStatus === 'enter',
'fade-leave' : this._animationStatus === 'leave',
'fade-leave-active' : this._animationStatus === 'leave'
};
this._bodyClassMap = {
[this._prefixCls] : true,
'zoom-enter' : this._animationStatus === 'enter',
'zoom-enter-active': this._animationStatus === 'enter',
'zoom-leave' : this._animationStatus === 'leave',
'zoom-leave-active': this._animationStatus === 'leave'
};
}
anmiateFade(status: string): void {
this._animationStatus = status;
this.setClassMap();
setTimeout(_ => {
this._animationStatus = '';
this.setClassMap();
this.subject.next(status === 'enter' ? 'onShown' : 'onHidden');
// modal打开后,默认焦点设置到modal上
if (status === 'enter') {
this.contentEl.nativeElement.parentNode.focus();
}
}, 200);
}
closeFromMask(e: MouseEvent): void {
if (this._maskClosable && (e.target as HTMLElement).getAttribute('role') === 'dialog') {
this.clickCancel(e);
}
}
setConfirmLoading(loading: boolean): void {
this.nzConfirmLoading = loading;
}
open(): void {
this.nzVisible = true;
}
close(): void {
this.nzVisible = false;
}
clickOk(e: MouseEvent): void {
if (this.nzOnOk) {
this.nzOnOk.emit(e);
} else {
this.nzVisible = false;
}
this.subject.next('onOk');
}
clickCancel(e: MouseEvent | KeyboardEvent): void {
this.nzOnCancel.emit(e);
this.subject.next('onCancel');
}
constructor(public subject: NzModalSubject,
private _vcr: ViewContainerRef,
private _locale: NzLocaleService) {
this.subject.modalId = this.modalId;
}
ngOnInit(): void {
this.setClassMap();
this.setStyles();
}
ngAfterViewInit(): void {
if (this._bodyComponent) {
const compRef: ComponentRef<void> = this.bodyEl.createComponent(this._bodyComponent, null, this._vcr.injector);
Object.assign(compRef.instance, this._componentParams);
}
}
ngOnDestroy(): void {
if (this._visible) {
nzGlobalMonitor.setDocumentOverflowHidden(false);
}
this.subject.next('onDestroy');
this.subject.unsubscribe();
this.subject = null;
}
}