import type { PopsAlertConfig } from "../components/alert/types"; import type { PopsConfirmConfig } from "../components/confirm/types"; import type { PopsDrawerConfig } from "../components/drawer/types"; import type { PopsFolderConfig } from "../components/folder/types"; import type { PopsIframeConfig } from "../components/iframe/types"; import type { PopsLoadingConfig } from "../components/loading/types"; import type { PopsPanelConfig } from "../components/panel/types"; import type { PopsPromptConfig } from "../components/prompt/types/index"; import type { EventEmiter } from "../event/EventEmiter"; import { PopsAnimation } from "../PopsAnimation"; import { PopsCore } from "../PopsCore"; import { PopsInstData } from "../PopsInst"; import type { PopsGeneralConfig } from "../types/components"; import type { PopsEventConfig, PopsHandlerEventConfig } from "../types/event"; import type { CustomEventMap } from "../types/EventEmitter"; import type { PopsInstGeneralConfig } from "../types/inst"; import type { PopsInstStoreType, PopsSupportAnimConfigType, PopsSupportOnlyConfig, PopsType } from "../types/main"; import { popsDOMUtils } from "../utils/PopsDOMUtils"; import { PopsInstanceUtils } from "../utils/PopsInstanceUtils"; import { popsUtils } from "../utils/PopsUtils"; import { PopsInstHandler } from "./PopsInstHandler"; export const PopsHandler = { /** * 创建shadow */ handlerShadow(config: Pick) { const $shadowContainer = popsDOMUtils.createElement("div", { className: "pops-shadow-container", }); let $shadowRoot: ShadowRoot | HTMLElement; if (config.useShadowRoot) { $shadowRoot = $shadowContainer.attachShadow({ mode: "open" }); } else { $shadowRoot = $shadowContainer; } // 添加键盘监听 // rightClickMenu // searchSuggestion // tooltip // 以上都不需要添加该事件监听 if (config.stopKeyDownEventPropagation) { popsDOMUtils.on( $shadowRoot, "keydown", [ 'input[type="text"]', 'input[type="password"]', 'input[type="number"]', 'input[type="email"]', 'input[type="url"]', 'input[type="search"]', "input:not([type])", "textarea", ], (evt) => { evt.stopImmediatePropagation(); evt.stopPropagation(); }, { capture: true, overrideTarget: false } ); } return { $shadowContainer, $shadowRoot, }; }, /** * 处理初始化 * @param $styleParent style元素的父元素 * @param css 添加进ShadowRoot的CSS */ handleInit( $styleParent?: ShadowRoot | HTMLElement, css?: | string | string[] | { name?: string; css: string; }[] ) { PopsAnimation.init(); if (!arguments.length) { return; } if ($styleParent == null) { return; } if (css == null) { return; } if (typeof css === "string") { if (css.trim() === "") { return; } css = [ { css: css, }, ]; } else { css = css.map((item) => { if (typeof item === "string") { return { css: item, }; } else { return item; } }); } for (const cssItem of css) { const $css = popsDOMUtils.createElement( "style", {}, { "data-type": "PopsHandler.handleInit", } ); $css.textContent = cssItem.css; if (typeof cssItem.name === "string") { $css.setAttribute("data-name", cssItem.name); } $styleParent.appendChild($css); } }, /** * 处理遮罩层 * * + 设置遮罩层的点击事件 * @param config 传递的配置 */ handleMask( config = {} as { type: "alert" | "confirm" | "prompt" | "loading" | "iframe" | "drawer" | "folder" | "panel"; guid: string; config: | Required | Required | Required | Required | Required | Required; animElement: HTMLElement; maskHTML: string; } ) { const result = { maskElement: popsDOMUtils.parseTextToDOM(config.maskHTML), }; let isMaskClick = false; /** * 点击其它区域的事件 * @param event */ const clickEvent = (event: MouseEvent | PointerEvent) => { popsDOMUtils.preventEvent(event); // 获取该类型实例存储列表 const targetInst = PopsInstData[config.type]; const continueExec = () => { if (config.config.mask!.clickEvent!.toClose) { // 关闭 return PopsInstHandler.close(config.config, config.type, targetInst, config.guid, config.animElement); } else if (config.config.mask!.clickEvent!.toHide) { // 隐藏 return PopsInstHandler.hide( config.config, config.type, targetInst, config.guid, config.animElement, result.maskElement ); } }; if (typeof config.config.mask.clickCallBack === "function") { config.config.mask.clickCallBack(continueExec, config.config); } else { continueExec(); } return false; }; // 判断是否启用了遮罩层点击动作 if (config.config.mask.clickEvent!.toClose || config.config.mask.clickEvent!.toHide) { // 判断按下的元素是否是pops-anim popsDOMUtils.on(config.animElement, "pointerup", (event) => { const $click = event.composedPath()[0] as HTMLElement; isMaskClick = PopsInstanceUtils.isAnimNode($click); }); // 如果有动画层,在动画层上监听点击事件 popsDOMUtils.on(config.animElement, "click", (event) => { const $click = event.composedPath()[0] as HTMLElement; if (isMaskClick && PopsInstanceUtils.isAnimNode($click)) { return clickEvent(event); } }); // 在遮罩层监听点击事件 // 如果有动画层,那么该点击事件触发不了 popsDOMUtils.on(result.maskElement, "click", (event) => { isMaskClick = true; clickEvent(event); }); } return result; }, /** * 处理获取元素 * @param animElement * @param type */ handleQueryElement(animElement: HTMLDivElement, type: PopsSupportAnimConfigType) { return { /** * 主元素 */ $pops: animElement.querySelector(".pops[type-value")!, /** * 确认按钮 */ $btnOk: animElement.querySelector(`.pops-${type}-btn-ok`)!, /** * 取消按钮 */ $btnCancel: animElement.querySelector(`.pops-${type}-btn-cancel`)!, /** * 其它按钮 */ $btnOther: animElement.querySelector(`.pops-${type}-btn-other`)!, /** * 标题元素 */ $title: animElement.querySelector(`.pops-${type}-title`)!, /** * 输入框元素 */ $input: animElement.querySelector(`.pops-${type}-content textarea[pops]`) ? animElement.querySelector(`.pops-${type}-content textarea[pops]`)! : animElement.querySelector(`.pops-${type}-content input[pops]`)!, /** * 顶部按钮控制层元素 */ $headerControls: animElement.querySelector(".pops-header-controls")!, /** * iframe元素 */ $iframe: animElement.querySelector("iframe[pops]")!, /** * 加载中元素 */ $loading: animElement.querySelector(".pops-loading")!, /** * 内容元素 */ $content: animElement.querySelector(`.pops-${type}-content`)!, /** * panel的右侧容器元素 */ $panelRightSectionWrapper: animElement.querySelector(`.pops-${type}-section-wrapper`)!, /** * panel侧边栏容器元素 */ $panelLeftAside: animElement.querySelector(`.pops-${type}-content aside.pops-${type}-aside`)!, /** * panel主要区域容器元素 */ $panelContentSectionContainer: animElement.querySelector( `.pops-${type}-content section.pops-${type}-container` )!, /** * panel底部区域 */ $panelBottomWrapper: animElement.querySelector(`.pops-${type}-bottom-wrapper`)!, /** * panel底部区域容器 */ $panelBottomContainer: animElement.querySelector(`.pops-${type}-bottom-container`)!, /** * panel底部区域左侧容器 */ $panelBottomLeftContainer: animElement.querySelector(`.pops-${type}-bottom-left-container`)!, /** * panel底部区域右侧容器 */ $panelBottomRightContainer: animElement.querySelector(`.pops-${type}-bottom-right-container`)!, /** * 内容加载中元素 */ $contentLoading: animElement.querySelector(`.pops-${type}-content-global-loading`)!, /** * 顶部缩小按钮 */ $headerBtnMin: animElement.querySelector(".pops-header-control[data-type='min']")!, /** * 顶部放大按钮 */ $headerBtnMax: animElement.querySelector(".pops-header-control[data-type='max']")!, /** * 顶部恢复原样按钮 */ $headerBtnMise: animElement.querySelector(".pops-header-control[data-type='mise']")!, /** * 顶部关闭按钮 */ $headerBtnClose: animElement.querySelector(".pops-header-control[data-type='close']")!, /** * 文件夹列表元素 */ $folderList: animElement.querySelector(".pops-folder-list")!, /** * 文件夹列表顶部元素 */ $folderHeaderNav: animElement.querySelector( ".pops-folder-list .pops-folder-list-table__header-div" )!, /** * 文件夹列表行元素 */ $folderHeaderRow: animElement.querySelector( ".pops-folder-list .pops-folder-list-table__header-div .pops-folder-list-table__header-row" )!, /** * 文件夹列表tbody元素 */ $folderTbody: animElement.querySelector( ".pops-folder-list .pops-folder-list-table__body-div tbody" )!, /** * 文件夹列表primary元素 */ $folderHeaderBreadcrumbPrimary: animElement.querySelector( ".pops-folder-list .pops-folder-file-list-breadcrumb-primary" )!, /** * 文件夹排序按钮-文件名 */ $folderSortFileName: animElement.querySelector( '.pops-folder-list-table__sort[data-sort="fileName"]' )!, /** * 文件夹排序按钮-修改时间 */ $folderSortLatestTime: animElement.querySelector( '.pops-folder-list-table__sort[data-sort="latestTime"]' )!, /** * 文件夹排序按钮-文件大小 */ $folderSortFileSize: animElement.querySelector( '.pops-folder-list-table__sort[data-sort="fileSize"]' )!, }; }, /** * 获取事件配置 * @param guid * @param $shadowContainer * @param $shadowRoot * @param type 当前弹窗类型 * @param $anim 动画层 * @param $pops 主元素 * @param $mask 遮罩层 * @param config 当前配置 */ handleEventConfig = EventEmiter>( config: | PopsAlertConfig | PopsDrawerConfig | PopsPromptConfig | PopsConfirmConfig | PopsIframeConfig | PopsLoadingConfig | PopsPanelConfig | PopsFolderConfig, guid: string, $shadowContainer: HTMLDivElement, $shadowRoot: ShadowRoot | HTMLElement, type: PopsInstStoreType, $anim: HTMLDivElement, $pops: HTMLDivElement, emitter: E, $mask?: HTMLDivElement ): PopsEventConfig { return { $shadowContainer: $shadowContainer, $shadowRoot: $shadowRoot, $el: $anim, $anim: $anim, $pops: $pops, $mask: $mask, mode: type, guid: guid, emitter: emitter, close() { return PopsInstHandler.close(config, type, PopsInstData[type], guid, $anim); }, hide() { return PopsInstHandler.hide(config, type, PopsInstData[type], guid, $anim, $mask); }, show($parent?: HTMLElement | Document | ShadowRoot) { if ($parent) { $parent.appendChild(PopsInstData[type][0].$shadowRoot); } return PopsInstHandler.show(config, type, PopsInstData[type], guid, $anim, $mask); }, }; }, /** * 获取loading的事件配置 * @param guid * @param type 当前弹窗类型 * @param $anim 动画层 * @param $pops 主元素 * @param $mask 遮罩层 * @param config 当前配置 */ handleLoadingEventConfig = EventEmiter>( config: | PopsAlertConfig | PopsDrawerConfig | PopsPromptConfig | PopsConfirmConfig | PopsIframeConfig | PopsLoadingConfig | PopsPanelConfig | PopsFolderConfig, guid: string, type: "loading", $anim: HTMLDivElement, $pops: HTMLDivElement, emitter: E, $mask?: HTMLDivElement ): Omit, "$shadowContainer" | "$shadowRoot"> { return { $el: $anim, $anim: $anim, $pops: $pops, $mask: $mask, mode: type, guid: guid, emitter, close() { return PopsInstHandler.close(config, type, PopsInstData[type], guid, $anim); }, hide() { return PopsInstHandler.hide(config, type, PopsInstData[type], guid, $anim, $mask); }, show() { return PopsInstHandler.show(config, type, PopsInstData[type], guid, $anim, $mask); }, }; }, /** * 处理返回的配置,针对popsHandler.handleEventConfig * @param config 配置 */ handleResultConfig(config: T): Omit { const resultDetails = Object.assign({}, config); popsUtils.delete(resultDetails, "type"); popsUtils.delete(resultDetails, "function"); return resultDetails; }, /** * 处理点击事件 * @param type 当前按钮类型 * @param $btn 按钮元素 * @param eventConfig 事件配置,由popsHandler.handleEventConfig创建的 * @param callback 点击回调 */ handleClickEvent = EventEmiter>( type: PopsHandlerEventConfig["type"], $btn: HTMLElement, eventConfig: PopsEventConfig, callback?: (details: PopsHandlerEventConfig, event: PointerEvent | MouseEvent) => void ) { if (typeof callback !== "function") return; return popsDOMUtils.on( $btn, "click", (event) => { const extraParam = { type: type, }; callback(Object.assign(eventConfig, extraParam), event); }, { capture: true, } ); }, /** * 全局监听键盘事件 * @param keyName 键名|键值 * @param otherKeyList 组合按键,数组类型,包含ctrl、shift、alt和meta(win键或mac的cmd键) * @param callback 回调函数 */ handleKeyboardEvent(keyName: string | number, otherKeyList: string[] = [], callback: (event: KeyboardEvent) => void) { const keyboardEvent = function (event: KeyboardEvent) { const _keyName = event.code || event.key; const _keyValue = event.charCode || event.keyCode || event.which; if (otherKeyList.includes("ctrl") && !event.ctrlKey) { return; } if (otherKeyList.includes("alt") && !event.altKey) { return; } if (otherKeyList.includes("meta") && !event.metaKey) { return; } if (otherKeyList.includes("shift") && !event.shiftKey) { return; } if (typeof keyName === "string" && keyName === _keyName) { callback && callback(event); } else if (typeof keyName === "number" && keyName === _keyValue) { callback && callback(event); } }; const listener = popsDOMUtils.on(PopsCore.globalThis, "keydown", keyboardEvent, { capture: true, }); return listener; }, /** * 处理prompt的点击事件 * @param type 触发事件类型 * @param inputElement 输入框 * @param $btn 按钮元素 * @param eventConfig 事件配置,由popsHandler.handleEventConfig创建的 * @param callback 点击回调 */ handlePromptClickEvent = EventEmiter>( type: PopsHandlerEventConfig["type"], inputElement: HTMLInputElement | HTMLTextAreaElement, $btn: HTMLElement, eventConfig: PopsEventConfig, callback: ( details: PopsEventConfig & { type: any; text: string; }, event: MouseEvent | PointerEvent ) => void ) { popsDOMUtils.on( $btn, "click", (event) => { // 额外参数 const extraParam = { type: type, text: inputElement.value, }; callback(Object.assign(eventConfig, extraParam), event); }, { capture: true, } ); }, /** * 获取数值 * @param target */ getTargerOrFunctionValue(target: T | (() => T)): T { if (typeof target === "function") { const result = (target as () => T)(); return result; } else { return target; } }, /** * 处理config.only * @param type 当前弹窗类型 * @param config 配置 */ handleOnly>(type: PopsType, config: T): T { if (config.only) { // .loading // .rightClickMenu // .tooltip // 单独处理 if (type === "loading" || type === "tooltip" || type === "rightClickMenu") { const inst = PopsInstData[type as keyof typeof PopsInstData]; if (inst) { PopsInstHandler.removeInstance([inst], "", true); } } else { PopsInstHandler.removeInstance( [ PopsInstData.alert, PopsInstData.confirm, PopsInstData.drawer, PopsInstData.folder, PopsInstData.iframe, PopsInstData.panel, PopsInstData.prompt, ], "", true ); } } if (type !== "rightClickMenu") { // rightClickMenu在配置处理时就处理了一次,这里不需要重复处理 config = this.handleZIndex(config); } return config; }, /** * 处理z-index * @param config 配置 */ handleZIndex>(config: T): T { // 对配置进行处理 // 选择配置的z-index和已有的pops实例的最大z-index值 const originZIndex = config.zIndex; const handler = () => { let deviation = 100; deviation += PopsHandler.getTargerOrFunctionValue(originZIndex) ?? 0; let maxZIndex = deviation; const pointZIndex = popsUtils.getMaxZIndexNodeInfoFromPoint(deviation)[0]?.zIndex ?? 0; maxZIndex = Math.max(maxZIndex, pointZIndex); return maxZIndex === deviation ? maxZIndex : maxZIndex + deviation; }; config.zIndex = handler; return config; }, /** * 处理把已创建的元素保存到内部环境中 * @param type 当前弹窗类型 * @param value */ handlePush(type: PopsInstStoreType, value: PopsInstGeneralConfig) { PopsInstData[type].push(value); }, };