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 } }); } }