UNPKG

@whitesev/pops

Version:

弹窗库

682 lines (680 loc) 17.7 kB
import type { PopsAlertDetails } from "../components/alert/indexType"; import type { PopsConfirmDetails } from "../components/confirm/indexType"; import type { PopsDrawerDetails } from "../components/drawer/indexType"; import type { PopsFolderDetails } from "../components/folder/indexType"; import type { PopsIframeDetails } from "../components/iframe/indexType"; import type { PopsLoadingDetails } from "../components/loading/indexType"; import type { PopsPanelDetails } from "../components/panel/indexType"; import type { PopsPromptDetails } from "../components/prompt/indexType"; import { PopsCore } from "../Core"; import { pops } from "../Pops"; import type { PopsCommonConfig } from "../types/components"; import { PopsEventDetails, PopsHandlerEventDetails } from "../types/event"; import { PopsLayerCommonConfig } from "../types/layer"; import type { PopsAllDetails, PopsLayerMode, PopsMode, PopsType, } from "../types/main"; import { popsDOMUtils } from "../utils/PopsDOMUtils"; import { PopsInstanceUtils } from "../utils/PopsInstanceUtils"; import { popsUtils } from "../utils/PopsUtils"; export const PopsHandler = { /** * 创建shadow */ handlerShadow(config: Pick<PopsCommonConfig, "useShadowRoot">) { let $shadowContainer = document.createElement("div"); $shadowContainer.className = "pops-shadow-container"; if (config.useShadowRoot) { let $shadowRoot = $shadowContainer.attachShadow({ mode: "open" }); return { $shadowContainer, $shadowRoot, }; } else { return { $shadowContainer, $shadowRoot: $shadowContainer, }; } }, /** * 处理初始化 * @param $shadowRoot 所在的shadowRoot * @param cssText 添加进ShadowRoot的CSS */ handleInit( $shadowRoot?: ShadowRoot | HTMLElement, cssText?: string | string[] ) { pops.init(); if (!arguments.length) { return; } if (Array.isArray(cssText)) { for (let index = 0; index < cssText.length; index++) { this.handleInit($shadowRoot, cssText[index]); } } else { let $css = popsDOMUtils.createElement( "style", { innerHTML: cssText, }, { "data-type": "PopsHandler.handleInit", } ); $shadowRoot!.appendChild($css); } }, /** * 处理遮罩层 * * + 设置遮罩层的点击事件 * @param details 传递的配置 */ handleMask( details = {} as { type: | "alert" | "confirm" | "prompt" | "loading" | "iframe" | "drawer" | "folder" | "panel"; guid: string; config: | Required<PopsAlertDetails> | Required<PopsLoadingDetails> | Required<PopsIframeDetails> | Required<PopsDrawerDetails> | Required<PopsPanelDetails> | Required<PopsFolderDetails>; animElement: HTMLElement; maskHTML: string; } ): { maskElement: HTMLDivElement; } { let result = { maskElement: popsUtils.parseTextToDOM<HTMLDivElement>(details.maskHTML), }; let isMaskClick = false; /** * 点击其它区域的事件 * @param event */ function clickEvent(event: MouseEvent | PointerEvent) { popsDOMUtils.preventEvent(event); // 获取该类型实例存储列表 let targetLayer = pops.config.layer[details.type]; function originalRun() { if (details.config.mask!.clickEvent!.toClose) { /* 关闭 */ PopsInstanceUtils.close( details.type, targetLayer, details.guid, details.config, details.animElement ); } else if (details.config.mask!.clickEvent!.toHide) { /* 隐藏 */ PopsInstanceUtils.hide( details.type, targetLayer, details.guid, details.config, details.animElement, result.maskElement ); } } if (typeof details.config.mask.clickCallBack === "function") { details.config.mask.clickCallBack(originalRun, details.config); } else { originalRun(); } return false; } // 判断是否启用了遮罩层点击动作 if ( details.config.mask.clickEvent!.toClose || details.config.mask.clickEvent!.toHide ) { /** * 判断点击的元素是否是动画层的元素 * @param element * @returns */ function isAnimElement(element: HTMLElement) { return Boolean( element?.localName?.toLowerCase() === "div" && element.className && element.className === "pops-anim" && element.hasAttribute("anim") ); } /* 判断按下的元素是否是pops-anim */ popsDOMUtils.on( details.animElement, ["touchstart", "mousedown"], void 0, (event) => { let $click = event.composedPath()[0] as HTMLElement; isMaskClick = isAnimElement($click); } ); /* 如果有动画层,在动画层上监听点击事件 */ popsDOMUtils.on<MouseEvent | PointerEvent>( details.animElement, "click", void 0, (event) => { let $click = event.composedPath()[0] as HTMLElement; if (isAnimElement($click) && isMaskClick) { return clickEvent(event); } } ); /* 在遮罩层监听点击事件 */ /* 如果有动画层,那么该点击事件触发不了 */ popsDOMUtils.on<MouseEvent | PointerEvent>( result.maskElement, "click", void 0, (event) => { isMaskClick = true; clickEvent(event); } ); } return result; }, /** * 处理获取元素 * @param animElement * @param type */ handleQueryElement(animElement: HTMLDivElement, type: PopsType) { return { /** * 主元素 */ popsElement: animElement.querySelector<HTMLDivElement>(".pops[type-value")!, /** * 确认按钮 */ btnOkElement: animElement.querySelector<HTMLDivElement>( `.pops-${type}-btn-ok` )!, /** * 取消按钮 */ btnCancelElement: animElement.querySelector<HTMLDivElement>( `.pops-${type}-btn-cancel` )!, /** * 其它按钮 */ btnOtherElement: animElement.querySelector<HTMLDivElement>( `.pops-${type}-btn-other` )!, /** * 标题元素 */ titleElement: animElement.querySelector<HTMLDivElement>( `.pops-${type}-title` )!, /** * 输入框元素 */ inputElement: animElement.querySelector<HTMLTextAreaElement>( `.pops-${type}-content textarea[pops]` ) ? animElement.querySelector<HTMLTextAreaElement>( `.pops-${type}-content textarea[pops]` )! : animElement.querySelector<HTMLInputElement>( `.pops-${type}-content input[pops]` )!, /** * 顶部按钮控制层元素 */ headerControlsElement: animElement.querySelector<HTMLDivElement>( ".pops-header-controls" )!, /** * iframe元素 */ iframeElement: animElement.querySelector<HTMLIFrameElement>("iframe[pops]")!, /** * 加载中元素 */ loadingElement: animElement.querySelector<HTMLDivElement>(".pops-loading")!, /** * 内容元素 */ contentElement: animElement.querySelector<HTMLDivElement>( `.pops-${type}-content` )!, /** * 内容侧边栏容器元素 */ contentAsideElement: animElement.querySelector<HTMLDivElement>( `.pops-${type}-content aside.pops-${type}-aside` )!, /** * 内容主要区域容器元素 */ contentSectionContainerElement: animElement.querySelector<HTMLDivElement>( `.pops-${type}-content section.pops-${type}-container` )!, /** * 内容加载中元素 */ contentLoadingElement: animElement.querySelector<HTMLDivElement>( `.pops-${type}-content-global-loading` )!, /** * 顶部缩小按钮 */ headerMinBtnElement: animElement.querySelector<HTMLDivElement>( ".pops-header-control[type='min']" )!, /** * 顶部放大按钮 */ headerMaxBtnElement: animElement.querySelector<HTMLDivElement>( ".pops-header-control[type='max']" )!, /** * 顶部恢复原样按钮 */ headerMiseBtnElement: animElement.querySelector<HTMLDivElement>( ".pops-header-control[type='mise']" )!, /** * 顶部关闭按钮 */ headerCloseBtnElement: animElement.querySelector<HTMLDivElement>( ".pops-header-control[type='close']" )!, /** * 文件夹列表元素 */ folderListElement: animElement.querySelector<HTMLDivElement>(".pops-folder-list")!, /** * 文件夹列表顶部元素 */ folderListHeaderElement: animElement.querySelector<HTMLDivElement>( ".pops-folder-list .pops-folder-list-table__header-div" )!, /** * 文件夹列表行元素 */ folderListHeaderRowElement: animElement.querySelector<HTMLTableRowElement>( ".pops-folder-list .pops-folder-list-table__header-div .pops-folder-list-table__header-row" )!, /** * 文件夹列表tbody元素 */ folderListBodyElement: animElement.querySelector<HTMLTableElement>( ".pops-folder-list .pops-folder-list-table__body-div tbody" )!, /** * 文件夹列表primary元素 */ folderFileListBreadcrumbPrimaryElement: animElement.querySelector<HTMLDivElement>( ".pops-folder-list .pops-folder-file-list-breadcrumb-primary" )!, /** * 文件夹排序按钮-文件名 */ folderListSortFileNameElement: animElement.querySelector<HTMLDivElement>( '.pops-folder-list-table__sort[data-sort="fileName"]' )!, /** * 文件夹排序按钮-修改时间 */ folderListSortLatestTimeElement: animElement.querySelector<HTMLDivElement>( '.pops-folder-list-table__sort[data-sort="latestTime"]' )!, /** * 文件夹排序按钮-文件大小 */ folderListSortFileSizeElement: animElement.querySelector<HTMLDivElement>( '.pops-folder-list-table__sort[data-sort="fileSize"]' )!, }; }, /** * 获取事件配置 * @param guid * @param $shadowContainer * @param $shadowRoot * @param mode 当前弹窗类型 * @param animElement 动画层 * @param popsElement 主元素 * @param maskElement 遮罩层 * @param config 当前配置 */ handleEventDetails( guid: string, $shadowContainer: HTMLDivElement, $shadowRoot: ShadowRoot | HTMLElement, mode: PopsLayerMode, animElement: HTMLDivElement, popsElement: HTMLDivElement, maskElement: HTMLDivElement, config: | PopsAlertDetails | PopsDrawerDetails | PopsPromptDetails | PopsConfirmDetails | PopsIframeDetails | PopsLoadingDetails | PopsPanelDetails | PopsFolderDetails ): PopsEventDetails { return { $shadowContainer: $shadowContainer, $shadowRoot: $shadowRoot, element: animElement, animElement: animElement, popsElement: popsElement, maskElement: maskElement, mode: mode, guid: guid, close() { PopsInstanceUtils.close( mode, pops.config.layer[mode], guid, config, animElement ); }, hide() { PopsInstanceUtils.hide( mode, pops.config.layer[mode], guid, config, animElement, maskElement ); }, show() { PopsInstanceUtils.show( mode, pops.config.layer[mode], guid, config, animElement, maskElement ); }, }; }, /** * 获取loading的事件配置 * @param guid * @param mode 当前弹窗类型 * @param animElement 动画层 * @param popsElement 主元素 * @param maskElement 遮罩层 * @param config 当前配置 */ handleLoadingEventDetails( guid: string, mode: "loading", animElement: HTMLDivElement, popsElement: HTMLDivElement, maskElement: HTMLDivElement, config: | PopsAlertDetails | PopsDrawerDetails | PopsPromptDetails | PopsConfirmDetails | PopsIframeDetails | PopsLoadingDetails | PopsPanelDetails | PopsFolderDetails ): Omit<PopsEventDetails, "$shadowContainer" | "$shadowRoot"> { return { element: animElement, animElement: animElement, popsElement: popsElement, maskElement: maskElement, mode: mode, guid: guid, close() { PopsInstanceUtils.close( mode, pops.config.layer[mode], guid, config, animElement ); }, hide() { PopsInstanceUtils.hide( mode, pops.config.layer[mode], guid, config, animElement, maskElement ); }, show() { PopsInstanceUtils.show( mode, pops.config.layer[mode], guid, config, animElement, maskElement ); }, }; }, /** * 处理返回的配置,针对popsHandler.handleEventDetails */ handleResultDetails<T extends any>(details: T): Omit<T, "type" | "function"> { let resultDetails = Object.assign({}, details); popsUtils.delete(resultDetails, "type"); popsUtils.delete(resultDetails, "function"); return resultDetails; }, /** * 处理点击事件 * @param type 当前按钮类型 * @param $btn 按钮元素 * @param eventDetails 事件配置,由popsHandler.handleEventDetails创建的 * @param callback 点击回调 */ handleClickEvent( type: "cancel" | "close" | "ok" | "other", $btn: HTMLElement, eventDetails: PopsEventDetails, callback: ( details: PopsHandlerEventDetails, event: PointerEvent | MouseEvent ) => void ) { popsDOMUtils.on<PointerEvent | MouseEvent>( $btn, "click", (event) => { let extraParam = { type: type, }; callback(Object.assign(eventDetails, 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 ) { let keyboardEvent = function (event: KeyboardEvent) { let _keyName = event.code || event.key; let _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); } }; popsDOMUtils.on(PopsCore.globalThis, "keydown", keyboardEvent, { capture: true, }); return { removeKeyboardEvent() { popsDOMUtils.off(globalThis, "keydown", keyboardEvent, { capture: true, }); }, }; }, /** * 处理prompt的点击事件 * @param type 触发事件类型 * @param inputElement 输入框 * @param $btn 按钮元素 * @param eventDetails 事件配置,由popsHandler.handleEventDetails创建的 * @param callback 点击回调 */ handlePromptClickEvent( type: "ok" | "close" | "cancel" | "other", inputElement: HTMLInputElement | HTMLTextAreaElement, $btn: HTMLElement, eventDetails: PopsEventDetails, callback: ( details: PopsEventDetails & { type: any; text: string; }, event: MouseEvent | PointerEvent ) => void ) { popsDOMUtils.on<MouseEvent | PointerEvent>( $btn, "click", (event) => { // 额外参数 let extraParam = { type: type, text: inputElement.value, }; callback(Object.assign(eventDetails, extraParam), event); }, { capture: true, } ); }, /** * 把配置的z-index配置转为数字 * @param zIndex */ handleZIndex(zIndex: number | (() => number)): number { if (typeof zIndex === "function") { return zIndex(); } else { return zIndex; } }, /** * 处理config.only * @param type 当前弹窗类型 * @param config 配置 */ handleOnly<T extends Required<PopsAllDetails[keyof PopsAllDetails]>>( type: PopsMode, config: T ): T { if (config.only) { if ( type === "loading" || type === "tooltip" || type === "rightClickMenu" ) { let layer = pops.config.layer[type as keyof typeof pops.config.layer]; if (layer) { PopsInstanceUtils.removeInstance([layer], "", true); } } else { PopsInstanceUtils.removeInstance( [ pops.config.layer.alert, pops.config.layer.confirm, pops.config.layer.prompt, pops.config.layer.iframe, pops.config.layer.drawer, pops.config.layer.folder, pops.config.layer.panel, ], "", true ); } } else { // 对配置进行处理 // 选择配置的z-index和已有的pops实例的最大z-index值 let originZIndex = config.zIndex; config.zIndex = () => { const { zIndex: maxZIndex } = PopsInstanceUtils.getPopsMaxZIndex( PopsHandler.handleZIndex(originZIndex) + 100 ); return maxZIndex; }; } return config; }, /** * 处理把已创建的元素保存到内部环境中 * @param type 当前弹窗类型 * @param value */ handlePush(type: PopsLayerMode, value: PopsLayerCommonConfig) { pops.config.layer[type].push(value); }, };