import {
Component, ElementRef, HostListener, OnDestroy,
OnInit, Renderer2, ViewChild, Output,
EventEmitter, TemplateRef, AfterViewInit, Input,
NgZone,
ViewChildren,
QueryList,
HostBinding
} from '@angular/core';
import { AngularDraggableDirective } from '@farris/ui-draggable';
import { DomSanitizer, SafeResourceUrl } from "@angular/platform-browser";
import {
CLASS_NAME, DISMISS_REASONS,
ModalOptions, TRANSITION_DURATIONS
} from './modal-options.class';
import { BsModalService } from './bs-modal.service';
import { isBs3 } from '@farris/ui-modal/utils';
import { ModalButtonDirective } from './modal-button.directive';
@Component({
selector: 'modal-container',
template: `
`,
host: {
class: 'modal farris-modal',
role: 'dialog',
tabindex: '-1',
'[attr.aria-modal]': 'true'
}
})
export class ModalContainerComponent implements OnInit, OnDestroy, AfterViewInit {
config: ModalOptions;
isShown = false;
level: number;
isAnimated: boolean;
bsModalService: BsModalService;
dialogType: string;
iframeSrc: SafeResourceUrl;
@Input()
public set iframe(value: string) {
if (this.sanitizer) {
this.iframeSrc = this.sanitizer.bypassSecurityTrustResourceUrl(value);
}
}
private isModalHiding = false;
private originalWidth: number;
private originalHeight: number;
isMax = false;
@ViewChild(AngularDraggableDirective) draggbar: AngularDraggableDirective;
@ViewChild('header') modalHeader: ElementRef;
boundsElement: HTMLElement;
@Output() closed = new EventEmitter();
@Output() opened = new EventEmitter();
@Output() resized = new EventEmitter();
@HostBinding('id') modalID = `MODALID_${Date.now().toPrecision()}`;
@ViewChildren(ModalButtonDirective) buttons: QueryList;
dlgHeaderLineHeight: any;
constructor(
options: ModalOptions,
protected el: ElementRef,
private _renderer: Renderer2,
private sanitizer: DomSanitizer
) {
this.config = Object.assign({}, options);
if (!this.config.buttons) {
this.config.buttons = [
{
text: '取消',
cls: 'btn btn-light',
iconCls: 'k-icon k-i-close',
handle: () => {
this.close();
}
},
{
text: '确定',
cls: 'btn btn-primary',
iconCls: 'k-icon k-i-check',
handle: () => { alert('好(。・∀・)ノ゙嗨哟!!'); }
}
];
}
}
ngOnInit(): void {
this.boundsElement = this.el.nativeElement;
if (this.config.fitContent) {
this._renderer.addClass(this.el.nativeElement, 'f-modal-fitContent');
}
if (this.isAnimated) {
this._renderer.addClass(this.el.nativeElement, CLASS_NAME.FADE);
}
// this.ngZone.runOutsideAngular(() => {
setTimeout(() => {
this.isShown = true;
this._renderer.addClass(
this.el.nativeElement,
isBs3() ? CLASS_NAME.IN : CLASS_NAME.SHOW
);
this._renderer.setStyle(this.el.nativeElement, 'display', 'block');
if (this.config.fitContent && this.el.nativeElement.className.indexOf('f-modal-fitContent-scroll') > -1) {
this.el.nativeElement.scrollTop = 0;
}
if (document && document.body) {
const modalLen = this.bsModalService.getModalsCount();
if (modalLen === 1) {
this.bsModalService.checkScrollbar();
this.bsModalService.setScrollbar();
}
if (!document.body.classList.contains(CLASS_NAME.OPEN)) {
this._renderer.addClass(document.body, CLASS_NAME.OPEN);
}
}
this.opened.emit({ modal: this });
}, this.isAnimated ? TRANSITION_DURATIONS.BACKDROP : 0);
// });
if (this.el.nativeElement) {
this.el.nativeElement.focus();
}
this.dlgHeaderLineHeight = this.getDialogHeaderLineHeight() + 'px';
}
ngAfterViewInit(): void {
// Called after ngAfterContentInit when the component's view has been initialized. Applies to components only.
// Add 'implements AfterViewInit' to the class.
this._renderer.setStyle(this.el.nativeElement, 'display', 'block');
// 如果内容适应宽度
if (this.config.fitContent) {
let initElRect = this.el.nativeElement.getBoundingClientRect();
let initBodySize = this.getContainerSize();
if (initElRect.height < initBodySize.height + 30) {
this._renderer.addClass(this.el.nativeElement, 'f-modal-fitContent-scroll');
}
}
}
private getDialogHeaderLineHeight() {
if (this.config.showHeader) {
const modalHeaderStyles = window.getComputedStyle(this.modalHeader.nativeElement);
const paddingTop = parseInt(modalHeaderStyles.paddingTop.replace('px', ''), 10);
const paddingBottom = parseInt(modalHeaderStyles.paddingBottom.replace('px', ''), 10);
return this.config.dialogHeaderHeight - paddingTop - paddingBottom;
} else {
return 0;
}
}
// 获取内容区域的尺寸
getContainerSize() {
const bodyTarget = this.el.nativeElement.querySelector('.modal-body');
const height = bodyTarget.offsetHeight;
const width = bodyTarget.offsetWidth;
return { width, height };
}
buildFooterStyles() {
let styles = {};
if (this.config.dialogFooterStyles) {
styles = Object.assign({}, styles, this.config.dialogFooterStyles);
}
return styles;
}
useButtonsTemplate() {
return this.config.buttons instanceof TemplateRef;
}
@HostListener('click', ['$event'])
onClick(event: MouseEvent) {
const tag = event.target as any;
// if (tag.localName === 'label') {
// if (tag.className) {
// const clsList = tag.className.split(' ');
// if (
// clsList.indexOf('k-radio-label') > -1 ||
// clsList.indexOf('k-checkbox-label') > -1 ||
// clsList.indexOf('custom-control-label') > -1
// ) {
// return;
// }
// }
// }
// if (tag.localName === 'input' && (tag.type === 'checkbox' || tag.type === 'radio' || tag.type === 'file')) {
// return;
// }
if (this.config.ignoreBackdropClick || this.config.backdrop === 'static' || event.target !== this.el.nativeElement ) {
event.stopPropagation();
return;
}
this.bsModalService.setDismissReason(DISMISS_REASONS.BACKRDOP);
this.close();
}
@HostListener('window:keydown.esc', ['$event'])
onEsc(event: any): void {
if (!this.isShown) {
return;
}
if (event.keyCode === 27) {
event.preventDefault();
}
if (
this.config.keyboard &&
this.level === this.bsModalService.getModalsCount()
) {
this.bsModalService.setDismissReason(DISMISS_REASONS.ESC);
this.close(event);
}
}
ngOnDestroy(): void {
if (this.isShown) {
this.close();
}
}
close(e?: MouseEvent, isCloseButton = false): void {
if (e && e.stopPropagation) {
e.stopPropagation();
}
if (this.isModalHiding || !this.isShown) {
return;
}
this.config.beforeClose(this, { event: e }).subscribe(r => {
if (!r) {
return false;
}
this.isModalHiding = true;
this._renderer.removeClass(
this.el.nativeElement,
isBs3() ? CLASS_NAME.IN : CLASS_NAME.SHOW
);
setTimeout(() => {
this.isShown = false;
if (document &&
document.body
) {
if (this.bsModalService.getModalsCount() === 1) {
this._renderer.removeClass(document.body, CLASS_NAME.OPEN);
}
}
this.bsModalService.hide(this.level);
this.isModalHiding = false;
if (this.config.closed) {
this.config.closed(isCloseButton, { modalRef: this, event: e });
}
this.closed.emit(this.level);
}, this.isAnimated ? TRANSITION_DURATIONS.MODAL : 0);
// });
});
}
toCenter() {
const marginLeft = Number.parseInt('' + -this.config.width / 2) + 'px';
const marginTop = Number.parseInt('' + -this.config.height / 2) + 'px';
const dialogEl = this.el.nativeElement.querySelector('.modal-dialog');
const contentEl = this.el.nativeElement.querySelector('.modal-body');
this._renderer.setStyle(dialogEl, 'margin-left', marginLeft);
this._renderer.setStyle(dialogEl, 'margin-top', marginTop);
let contentHeight = this.config.height - 38;
if (this.config.showButtons) {
contentHeight = contentHeight - 50;
}
if (!this.config.fitContent) {
this._renderer.setStyle(
contentEl,
'height',
contentHeight + 'px'
);
}
this._renderer.setStyle(contentEl, 'overflow', 'hidden');
this._renderer.setStyle(dialogEl, 'transform', `translate3d(0px, 0px, 0px)`);
setTimeout(() => {
const container = this.getContainerSize();
const { width: containerWidth, height: containerHeight } = container;
this.resized.emit({ width: this.config.width, height: this.config.height, containerWidth, containerHeight });
});
}
moveTo(x: number, y: number) {
const dialogEl = this.el.nativeElement.querySelector('.modal-dialog');
this._renderer.setStyle(dialogEl, 'transform', `translate3d(${x}px, ${y}px, 0px)`);
this.draggbar.position = { x, y };
this.draggbar.resetPosition();
}
maxDialog() {
this.originalWidth = this.config.width;
this.originalHeight = this.config.height;
this.config.width = (document.scrollingElement?document.scrollingElement:document.documentElement)['clientWidth'] - 20;
this.config.height = (document.scrollingElement?document.scrollingElement:document.documentElement)['clientHeight'] - 20;
this.config.draggable = false;
this.config.resizable = false;
const dialogEl = this.el.nativeElement.querySelector('.modal-dialog');
this._renderer.addClass(dialogEl, 'model-dialog-maximize');
this._renderer.setStyle(dialogEl, 'top', '50%');
this._renderer.setStyle(dialogEl, 'left', '50%');
this.isMax = true;
this.toCenter();
}
resize(width: number, height: number) {
if (width) {
this.config.width = width;
}
if (height) {
this.config.height = height;
}
const dialogEl = this.el.nativeElement.querySelector('.modal-dialog');
this._renderer.removeClass(dialogEl, 'model-dialog-maximize');
this.toCenter();
this.draggbar.resetPosition();
}
dropdown(el: any) {
// const rect = el.getBoundingClientRect();
// this._renderer.removeStyle(el, 'margin-top');
// this._renderer.removeStyle(el, 'margin-left');
// this._renderer.setStyle(el, 'top', `${rect.top}px`);
// this._renderer.setStyle(el, 'left', `${rect.left}px`);
// this._renderer.removeStyle(el, 'transform');
// this._renderer.removeStyle(el, '-webkit-transform');
// this._renderer.removeStyle(el, '-ms-transform');
// this._renderer.removeStyle(el, '-moz-transform');
// this._renderer.removeStyle(el, '-o-transform');
}
revertDialog() {
this.isMax = false;
this.config.width = this.originalWidth;
this.config.height = this.originalHeight;
this.config.draggable = true;
this.config.resizable = true;
const dialogEl = this.el.nativeElement.querySelector('.modal-dialog');
this._renderer.removeClass(dialogEl, 'model-dialog-maximize');
this.toCenter();
this.draggbar.resetPosition();
}
changeDialogSize() {
if (this.isMax) {
this.revertDialog();
} else {
this.maxDialog();
}
}
resizeStop(opts: any) {
// console.log('resizeStop');
this.config.width = opts.size.width;
this.config.height = opts.size.height;
const container = this.getContainerSize();
const { width: containerWidth, height: containerHeight } = container;
this.resized.emit({ ...opts.size, ...{ containerWidth, containerHeight } });
}
}