import { ComponentFactoryResolver, ComponentRef, EventEmitter, Injectable, Injector, LOCALE_ID } from '@angular/core'; import { BsModalService, BsModalRef } from '@farris/ui-modal'; import { LocaleService } from '@farris/ui-locale'; import { IFeature, IFeatureEditorOptions } from './type'; import { FeatureEditorComponent } from './feature-editor.component'; import { I18nService } from './i18n-service'; import { isObservable, Observable, of } from 'rxjs'; import { EventManager } from '@angular/platform-browser'; @Injectable() export class FeatureEditorService { public dialog: BsModalRef = null; private componentRef: ComponentRef = null; private keyDownHandler = null; onUserConfirmed: EventEmitter = new EventEmitter(); private eventManager: EventManager; constructor( public injector: Injector, private modalService: BsModalService, private componentFactoryResolver: ComponentFactoryResolver, private localeService: LocaleService, private languageService: I18nService ) { if (!this.languageService) { const localeId = this.injector.get(LOCALE_ID, 'zh-CHS'); this.languageService = new I18nService(localeId); } this.eventManager = this.injector.get(EventManager, null); } /** * 弹出特性编辑器 * @param features 特征数组 * @param options 弹窗配置 * @returns */ public show(features: Array, options?: IFeatureEditorOptions): void { if (!features || features.length < 1) { return; } this.canOpen(options).subscribe((canOpen: boolean) => { if (!canOpen) { return; } const componentFactory = this.componentFactoryResolver.resolveComponentFactory(FeatureEditorComponent); this.componentRef = componentFactory.create(this.injector); this.componentRef.instance.features = features; const titleText = this.languageService.title; const okText = this.localeService.getValue('batchEditDialog.okText'); const cancelText = this.localeService.getValue('batchEditDialog.cancelText'); const { width = 470, height = 500, title = titleText } = options || {}; const modalOptions: any = { title, width, height, buttons: [ { text: cancelText, cls: 'btn btn-secondary', handle: () => { this.dialog.close(); } }, { text: okText, cls: 'btn btn-primary', handle: () => { this.onConfirmClick(options); } } ], showButtons: true, areaResponse: true, opened: (e) => { setTimeout(() => { this.registerShortcutKey(options, e.instance); }); }, closed: () => { if (this.keyDownHandler) { this.keyDownHandler(); this.keyDownHandler = null; } } }; this.dialog = this.modalService.show(this.componentRef, modalOptions); }); } private registerShortcutKey(options, dialogRef) { // 回车,与确定按钮处理逻辑一至。 const dlgContainerDom = dialogRef.dialog.location.nativeElement; if (dlgContainerDom && !this.keyDownHandler) { this.keyDownHandler = this.eventManager.addEventListener(dlgContainerDom, 'keydown', (e: KeyboardEvent) => { e.stopPropagation(); if (e.key === 'Enter') { // if (e.target['nodeName'] === 'INPUT' && !e.ctrlKey) { // return; // } this.onConfirmClick(options); } else if (e.key === 'Escape') { this.dialog.close(); } else if (e.key === 'Tab') { this.onTabKeydownHandler(e.target); } }); } if (this.componentRef) { this.setFirstInputFocus(); } } private setFirstInputFocus() { const firstInputEl = this.componentRef.location.nativeElement.querySelector('input,textarea'); if (firstInputEl) { firstInputEl.focus(); } } private onTabKeydownHandler(currentTarget) { let allInputs = this.componentRef.location.nativeElement.querySelectorAll('input,textarea'); allInputs = Array.from(allInputs); const currentInputIndex = allInputs.findIndex(n => n === currentTarget); let nextInputIndex = currentInputIndex + 1; if (nextInputIndex >= allInputs.length) { nextInputIndex = 0; } const nextInputTarget = allInputs[nextInputIndex]; if (nextInputTarget) { setTimeout(() => { nextInputTarget.focus(); }); } } /** * 用户点击确认按钮 */ private onConfirmClick(options?: IFeatureEditorOptions) { this.canClose(options).subscribe((result: boolean) => { if (result === true) { this.onConfirm(options); } }); } /** * 确认 * @param options options */ private onConfirm(options?: IFeatureEditorOptions) { const features = this.componentRef && this.componentRef.instance && this.componentRef.instance.features || []; this.dialog.close(); if (options.onConfirm && typeof options.onConfirm) { options.onConfirm(features); } this.onUserConfirmed.next(features); } /** * 是否可以关闭 * @param options options * @returns */ private canClose(options?: IFeatureEditorOptions): Observable { const features = this.componentRef && this.componentRef.instance && this.componentRef.instance.features || []; const onConfirmingRef = options && options.onConfirming || null; if (!onConfirmingRef) { return of(true); } // 有确认前事件 const onConfirming = onConfirmingRef(features); if (isObservable(onConfirming)) { return onConfirming; } else { return of(onConfirming); } } /** * 是否可以弹出特性编辑器 * @param options options * @returns */ private canOpen(options?: IFeatureEditorOptions): Observable { const onOpeningHandlerRef = options && options.onOpening || null; if (!onOpeningHandlerRef) { return of(true); } const onOpeningHandler = onOpeningHandlerRef(options); if (isObservable(onOpeningHandler)) { return onOpeningHandler; } else { return of(onOpeningHandler); } } }