UNPKG

@whitesev/pops

Version:

弹窗库

793 lines (779 loc) 23.5 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 type { PopsLayerCommonConfig } from "../types/layer"; import type { PopsLayerMode } from "../types/main"; import { popsDOMUtils } from "./PopsDOMUtils"; import { popsUtils } from "./PopsUtils"; import { PopsCore } from "../Core"; import { pops } from "../Pops"; export const PopsInstanceUtils = { /** * 获取页面中最大的z-index的元素信息 * @param deviation 获取最大的z-index值的偏移,默认是+1 * @param node 进行判断的元素,默认是document * @param ignoreCallBack 执行元素处理时调用的函数,返回false可忽略不想要处理的元素 * @example * Utils.getMaxZIndexNodeInfo(); * > { * node: ..., * zIndex: 1001 * } **/ getMaxZIndexNodeInfo( deviation = 1, target: Element | ShadowRoot | Document = PopsCore.document, ignoreCallBack?: ( $ele: Element | HTMLElement | ShadowRoot ) => boolean | void ): { node: Element; zIndex: number; } { deviation = Number.isNaN(deviation) ? 1 : deviation; // 最大值 2147483647 // const maxZIndex = Math.pow(2, 31) - 1; // 比较值 2000000000 const maxZIndexCompare = 2 * Math.pow(10, 9); // 当前页面最大的z-index let zIndex = 0; // 当前的最大z-index的元素,调试使用 // @ts-ignore let maxZIndexNode: Element = null; /** * 元素是否可见 * @param $css */ function isVisibleNode($css: CSSStyleDeclaration): boolean { return $css.position !== "static" && $css.display !== "none"; } /** * 查询元素的z-index * 并比较值是否是已获取的最大值 * @param $ele */ function queryMaxZIndex($ele: Element) { if (typeof ignoreCallBack === "function") { let ignoreResult = ignoreCallBack($ele); if (typeof ignoreResult === "boolean" && !ignoreResult) { return; } } /** 元素的样式 */ const nodeStyle = PopsCore.window.getComputedStyle($ele); /* 不对position为static和display为none的元素进行获取它们的z-index */ if (isVisibleNode(nodeStyle)) { let nodeZIndex = parseInt(nodeStyle.zIndex); if (!isNaN(nodeZIndex)) { if (nodeZIndex > zIndex) { // 赋值到全局 zIndex = nodeZIndex; maxZIndexNode = $ele; } } // 判断shadowRoot if ($ele.shadowRoot != null && $ele instanceof ShadowRoot) { $ele.shadowRoot.querySelectorAll("*").forEach(($shadowEle) => { queryMaxZIndex($shadowEle); }); } } } target.querySelectorAll("*").forEach(($ele, index) => { queryMaxZIndex($ele); }); zIndex += deviation; if (zIndex >= maxZIndexCompare) { // 最好不要超过最大值 zIndex = maxZIndexCompare; } return { node: maxZIndexNode, zIndex: zIndex, }; }, /** * 获取pops所有弹窗中的最大的z-index * @param deviation */ getPopsMaxZIndex(deviation: number = 1) { deviation = Number.isNaN(deviation) ? 1 : deviation; // 最大值 2147483647 // const browserMaxZIndex = Math.pow(2, 31) - 1; // 比较值 2000000000 const maxZIndex = 2 * Math.pow(10, 9); // 当前页面最大的z-index let zIndex = 0; // 当前的最大z-index的元素,调试使用 let maxZIndexNode = null as HTMLDivElement | null; /** * 元素是否可见 * @param $css */ function isVisibleNode($css: CSSStyleDeclaration): boolean { return $css.position !== "static" && $css.display !== "none"; } Object.keys(pops.config.layer).forEach((layerName) => { let layerList = pops.config.layer[layerName as PopsLayerMode]; for (let index = 0; index < layerList.length; index++) { const layer = layerList[index]; let nodeStyle = window.getComputedStyle(layer.animElement); /* 不对position为static和display为none的元素进行获取它们的z-index */ if (isVisibleNode(nodeStyle)) { let nodeZIndex = parseInt(nodeStyle.zIndex); if (!isNaN(nodeZIndex)) { if (nodeZIndex > zIndex) { zIndex = nodeZIndex; maxZIndexNode = layer.animElement; } } } } }); zIndex += deviation; let isOverMaxZIndex = zIndex >= maxZIndex; if (isOverMaxZIndex) { // 超出z-index最大值 zIndex = maxZIndex; } return { zIndex: zIndex, animElement: maxZIndexNode, isOverMaxZIndex }; }, /** * 获取页面中最大的z-index * @param deviation 获取最大的z-index值的偏移,默认是+1 * @example * getMaxZIndex(); * > 1001 **/ getMaxZIndex(deviation = 1): number { return this.getMaxZIndexNodeInfo(deviation).zIndex; }, /** * 获取CSS Rule * @param sheet * @returns */ getKeyFrames(sheet: CSSStyleSheet) { let result = {}; Object.keys(sheet.cssRules).forEach((key) => { if ( (sheet.cssRules as any)[key].type === 7 && (sheet.cssRules as any)[key].name.startsWith("pops-anim-") ) { (result as any)[(sheet.cssRules as any)[key].name] = ( sheet.cssRules as any )[key]; } }); return result; }, /** * 删除配置中对应的对象 * @param moreLayerConfigList 配置实例列表 * @param guid 唯一标识 * @param isAll 是否全部删除 */ removeInstance( moreLayerConfigList: PopsLayerCommonConfig[][], guid: string, isAll = false ) { /** * 移除元素实例 * @param layerCommonConfig */ function removeItem(layerCommonConfig: PopsLayerCommonConfig) { if (typeof layerCommonConfig.beforeRemoveCallBack === "function") { // 调用移除签的回调 layerCommonConfig.beforeRemoveCallBack(layerCommonConfig); } layerCommonConfig?.animElement?.remove(); layerCommonConfig?.popsElement?.remove(); layerCommonConfig?.maskElement?.remove(); layerCommonConfig?.$shadowContainer?.remove(); } // [ layer[], layer[],...] moreLayerConfigList.forEach((layerConfigList) => { // layer[] layerConfigList.forEach((layerConfigItem, index) => { // 移除全部或者guid相同 if (isAll || layerConfigItem["guid"] === guid) { // 判断是否有动画 if ( pops.config.animation.hasOwnProperty( layerConfigItem.animElement.getAttribute("anim") as string ) ) { layerConfigItem.animElement.style.width = "100%"; layerConfigItem.animElement.style.height = "100%"; (layerConfigItem.animElement.style as any)["animation-name"] = layerConfigItem.animElement.getAttribute("anim") + "-reverse"; if ( pops.config.animation.hasOwnProperty( (layerConfigItem.animElement.style as any)["animation-name"] ) ) { popsDOMUtils.on( layerConfigItem.animElement, popsDOMUtils.getAnimationEndNameList(), function () { removeItem(layerConfigItem); }, { capture: true, } ); } else { removeItem(layerConfigItem); } } else { removeItem(layerConfigItem); } layerConfigList.splice(index, 1); } }); }); return moreLayerConfigList; }, /** * 隐藏 * @param popsType * @param layerConfigList * @param guid * @param config * @param animElement * @param maskElement */ hide( popsType: PopsLayerMode, layerConfigList: PopsLayerCommonConfig[], guid: string, config: | PopsAlertDetails | PopsDrawerDetails | PopsPromptDetails | PopsConfirmDetails | PopsIframeDetails | PopsLoadingDetails | PopsPanelDetails | PopsFolderDetails, animElement: HTMLElement, maskElement: HTMLElement ) { let popsElement = animElement.querySelector<HTMLDivElement>(".pops[type-value]")!; if (popsType === "drawer") { let drawerConfig = config as Required<PopsDrawerDetails>; setTimeout(() => { maskElement.style.setProperty("display", "none"); if (["top", "bottom"].includes(drawerConfig.direction)) { popsElement.style.setProperty("height", "0"); } else if (["left", "right"].includes(drawerConfig.direction)) { popsElement.style.setProperty("width", "0"); } else { console.error("未知direction:", drawerConfig.direction); } }, drawerConfig.closeDelay); } else { layerConfigList.forEach((layerConfigItem) => { if (layerConfigItem.guid === guid) { /* 存在动画 */ layerConfigItem.animElement.style.width = "100%"; layerConfigItem.animElement.style.height = "100%"; (layerConfigItem.animElement.style as any)["animation-name"] = layerConfigItem.animElement.getAttribute("anim") + "-reverse"; if ( pops.config.animation.hasOwnProperty( (layerConfigItem.animElement.style as any)["animation-name"] ) ) { function animationendCallBack() { layerConfigItem.animElement.style.display = "none"; if (layerConfigItem.maskElement) { layerConfigItem.maskElement.style.display = "none"; } popsDOMUtils.off( layerConfigItem.animElement, popsDOMUtils.getAnimationEndNameList(), animationendCallBack, { capture: true, } ); } popsDOMUtils.on( layerConfigItem.animElement, popsDOMUtils.getAnimationEndNameList(), animationendCallBack, { capture: true, } ); } else { layerConfigItem.animElement.style.display = "none"; if (layerConfigItem.maskElement) { layerConfigItem.maskElement.style.display = "none"; } } return; } }); } }, /** * 显示 * @param popsType * @param layerConfigList * @param guid * @param config * @param animElement * @param maskElement */ show( popsType: PopsLayerMode, layerConfigList: PopsLayerCommonConfig[], guid: string, config: | PopsAlertDetails | PopsDrawerDetails | PopsPromptDetails | PopsConfirmDetails | PopsIframeDetails | PopsLoadingDetails | PopsPanelDetails | PopsFolderDetails, animElement: HTMLElement, maskElement: HTMLElement ) { let popsElement = animElement.querySelector<HTMLDivElement>(".pops[type-value]")!; if (popsType === "drawer") { let drawerConfig = config as PopsDrawerDetails; setTimeout(() => { maskElement.style.setProperty("display", ""); let direction = drawerConfig.direction!; let size = drawerConfig.size!.toString(); if (["top", "bottom"].includes(direction)) { popsElement.style.setProperty("height", size); } else if (["left", "right"].includes(direction)) { popsElement.style.setProperty("width", size); } else { console.error("未知direction:", direction); } }, drawerConfig.openDelay); } else { layerConfigList.forEach((layerConfigItem) => { if (layerConfigItem.guid === guid) { layerConfigItem.animElement.style.width = ""; layerConfigItem.animElement.style.height = ""; (layerConfigItem.animElement.style as any)["animation-name"] = layerConfigItem .animElement!.getAttribute("anim")! .replace("-reverse", ""); if ( pops.config.animation.hasOwnProperty( (layerConfigItem.animElement.style as any)["animation-name"] ) ) { layerConfigItem.animElement.style.display = ""; if (layerConfigItem.maskElement) { layerConfigItem.maskElement.style.display = ""; } function animationendCallBack() { popsDOMUtils.off( layerConfigItem.animElement, popsDOMUtils.getAnimationEndNameList(), animationendCallBack, { capture: true, } ); } popsDOMUtils.on( layerConfigItem.animElement, popsDOMUtils.getAnimationEndNameList(), animationendCallBack, { capture: true, } ); } else { layerConfigItem.animElement.style.display = ""; if (layerConfigItem.maskElement) { layerConfigItem.maskElement.style.display = ""; } } } return; }); } }, /** * 关闭 * @param popsType * @param layerConfigList * @param guid * @param config * @param animElement */ close( popsType: string, layerConfigList: PopsLayerCommonConfig[], guid: string, config: | PopsAlertDetails | PopsDrawerDetails | PopsPromptDetails | PopsConfirmDetails | PopsIframeDetails | PopsLoadingDetails | PopsPanelDetails | PopsFolderDetails, animElement: HTMLElement ) { let popsElement = animElement.querySelector<HTMLDivElement>(".pops[type-value]")!; let drawerConfig = config as Required<PopsDrawerDetails>; /** * 动画结束事件 */ function transitionendEvent() { function closeCallBack(event: Event) { if ((event as TransitionEvent).propertyName !== "transform") { return; } popsDOMUtils.off( popsElement, popsDOMUtils.getTransitionEndNameList(), void 0, closeCallBack ); PopsInstanceUtils.removeInstance([layerConfigList], guid); } /* 监听过渡结束 */ popsDOMUtils.on( popsElement, popsDOMUtils.getTransitionEndNameList(), closeCallBack ); let popsTransForm = getComputedStyle(popsElement).transform; if (popsTransForm !== "none") { popsDOMUtils.trigger( popsElement, popsDOMUtils.getTransitionEndNameList(), void 0, true ); return; } if (["top"].includes(drawerConfig.direction)) { popsElement.style.setProperty("transform", "translateY(-100%)"); } else if (["bottom"].includes(drawerConfig.direction)) { popsElement.style.setProperty("transform", "translateY(100%)"); } else if (["left"].includes(drawerConfig.direction)) { popsElement.style.setProperty("transform", "translateX(-100%)"); } else if (["right"].includes(drawerConfig.direction)) { popsElement.style.setProperty("transform", "translateX(100%)"); } else { console.error("未知direction:", drawerConfig.direction); } } if (popsType === "drawer") { setTimeout(() => { transitionendEvent(); }, drawerConfig.closeDelay); } else { PopsInstanceUtils.removeInstance([layerConfigList], guid); } }, /** * 拖拽元素 * 说明: * + 元素的position为absolute或者fixed * + 会为元素的 * @param moveElement 需要拖拽的元素 * @param options 配置 */ drag( moveElement: HTMLElement, options: { dragElement: HTMLElement; limit: boolean; triggerClick?: boolean; extraDistance: number; container?: Window | typeof globalThis | HTMLElement; moveCallBack?: ( moveElement: HTMLElement, left: number, top: number ) => void; endCallBack?: ( moveElement: HTMLElement, left: number, top: number ) => void; preventEvent?: (event: TouchEvent | PointerEvent) => boolean; } ) { options = Object.assign( { limit: true, extraDistance: 3, container: PopsCore.globalThis, triggerClick: true, }, options ); let isMove = false; /* 点击元素,left偏移 */ let clickElementLeftOffset = 0; /* 点击元素,top偏移 */ let clickElementTopOffset = 0; let AnyTouch = popsUtils.AnyTouch(); let anyTouchElement = new AnyTouch(options.dragElement, { preventDefault(event: Event) { if (typeof options.preventEvent === "function") { return options.preventEvent(event as any); } else { // 返回true阻止滑动 return true; } }, }); popsDOMUtils.css(options.dragElement, { cursor: "move", }); /** * 获取移动元素的transform偏移 */ function getTransform(element: HTMLElement) { let transform_left = 0; let transform_top = 0; let elementTransform = PopsCore.globalThis.getComputedStyle(element).transform; if ( elementTransform !== "none" && elementTransform != null && elementTransform !== "" ) { let elementTransformSplit = elementTransform .match(/\((.+)\)/)?.[1] .split(",")!; transform_left = Math.abs(parseInt(elementTransformSplit[4])); transform_top = Math.abs(parseInt(elementTransformSplit[5])); } return { transformLeft: transform_left, transformTop: transform_top, }; } /** * 修改移动的元素的style */ function changeMoveElementStyle(element: HTMLElement) { let old_transitionDuration = element.style.transitionDuration; if (globalThis.getComputedStyle(element).transitionDuration !== "0s") { element.style.transitionDuration = "0s"; } return () => { element.style.transitionDuration = old_transitionDuration; }; } /** * 获取容器的高度、宽度,一般是window为容器 */ function getContainerWidthOrHeight( container: HTMLElement | Window | typeof globalThis ) { container = container ?? globalThis; return { width: popsDOMUtils.width(container), height: popsDOMUtils.height(container), }; } /** * 获取容器的最小left、top偏移 */ function getContainerTopOrLeft( container: HTMLElement | Window | typeof globalThis ) { container = container ?? globalThis; if (popsUtils.isWin(container)) { return { left: 0, top: 0, }; } else { let rect = (container as HTMLElement).getBoundingClientRect(); return { left: rect.left, top: rect.top, }; } } let transformInfo = getTransform(moveElement); let transformLeft = transformInfo.transformLeft; let transformTop = transformInfo.transformTop; let resumeMoveElementStyle: Function | null = null; anyTouchElement.on("pan", function (event) { if (!isMove) { isMove = true; let rect = options.dragElement.getBoundingClientRect(); clickElementLeftOffset = event.x - rect.left; clickElementTopOffset = event.y - rect.top; transformInfo = getTransform(moveElement); transformLeft = transformInfo.transformLeft; transformTop = transformInfo.transformTop; //if (event.nativeEvent.offsetX) { // clickElementLeftOffset = parseInt(event.nativeEvent.offsetX); //} else { // clickElementLeftOffset = parseInt(event.getOffset().x); //} //if (event.nativeEvent.offsetY) { // clickElementTopOffset = parseInt(event.nativeEvent.offsetY); //} else { // clickElementTopOffset = parseInt(event.getOffset().y); //} resumeMoveElementStyle = changeMoveElementStyle(moveElement); } /** 当前移动的left偏移 */ let currentMoveLeftOffset = event.x - clickElementLeftOffset + transformLeft; /** 当前移动的top偏移 */ let currentMoveTopOffset = event.y - clickElementTopOffset + transformTop; /* 拖拽移动 */ if (event.phase === "move") { if (options.limit) { /* 限制在容器内移动 */ /* left偏移最大值 */ let maxLeftOffset = getContainerWidthOrHeight(options.container!).width - popsDOMUtils.width(moveElement) + transformLeft; let { left: minLeftOffset, top: minTopOffset } = getContainerTopOrLeft(options.container!); /* top偏移的最大值 */ let maxTopOffset = getContainerWidthOrHeight(options.container!).height - popsDOMUtils.height(moveElement) + transformTop; if (currentMoveLeftOffset > maxLeftOffset) { /* 不允许超过容器的最大宽度 */ currentMoveLeftOffset = maxLeftOffset; } if (currentMoveTopOffset > maxTopOffset) { /* 不允许超过容器的最大高度 */ currentMoveTopOffset = maxTopOffset; } if ( currentMoveLeftOffset - options.extraDistance * 2 < minLeftOffset + transformLeft ) { /* 不允许left偏移小于容器最小值 */ currentMoveLeftOffset = minLeftOffset + transformLeft; /* 最左边 +额外距离 */ currentMoveLeftOffset += options.extraDistance; } else { /* 最右边 -额外距离 */ currentMoveLeftOffset -= options.extraDistance; } if ( currentMoveTopOffset - options.extraDistance * 2 < minTopOffset + transformTop ) { /* 不允许top偏移小于容器最小值 */ currentMoveTopOffset = minTopOffset + transformTop; /* 最上面 +额外距离 */ currentMoveTopOffset += options.extraDistance; } else { /* 最下面 -额外距离 */ currentMoveTopOffset -= options.extraDistance; } } if (typeof options.moveCallBack === "function") { options.moveCallBack( moveElement, currentMoveLeftOffset, currentMoveTopOffset ); } popsDOMUtils.css(moveElement, { left: currentMoveLeftOffset + "px", top: currentMoveTopOffset + "px", }); } /* 停止拖拽 */ if (event.phase === "end") { isMove = false; if (typeof resumeMoveElementStyle === "function") { resumeMoveElementStyle(); resumeMoveElementStyle = null; } if (typeof options.endCallBack === "function") { options.endCallBack( moveElement, currentMoveLeftOffset, currentMoveTopOffset ); } } }); if (options.triggerClick) { /* 因为会覆盖上面的点击事件,主动触发一下 */ anyTouchElement.on(["tap"], function (event) { event.changedPoints.forEach((item) => { popsDOMUtils.trigger( item.target! as HTMLElement, "click", void 0, true ); }); }); } }, /** * 排序数组 * @param getBeforeValueFun * @param getAfterValueFun * @param sortByDesc 排序是否降序,默认降序 */ sortElementListByProperty<T extends any, R>( getBeforeValueFun: (value: T) => R, getAfterValueFun: (value: T) => R, sortByDesc = true ) { if (typeof sortByDesc !== "boolean") { throw "参数 sortByDesc 必须为boolean类型"; } if (getBeforeValueFun == null || getAfterValueFun == null) { throw "获取前面的值或后面的值的方法不能为空"; } return function (after_obj: T, before_obj: T) { var beforeValue = getBeforeValueFun(before_obj); /* 前 */ var afterValue = getAfterValueFun(after_obj); /* 后 */ if (sortByDesc) { if (afterValue > beforeValue) { return -1; } else if (afterValue < beforeValue) { return 1; } else { return 0; } } else { if (afterValue < beforeValue) { return -1; } else if (afterValue > beforeValue) { return 1; } else { return 0; } } }; }, };