UNPKG

@opensig/opendesign

Version:

<p align="center"><img src="../docs/public/opendesign-logo-light.png"/></p> <h1 align="center">opendesign</h1> <p align="center">一个 Vue 3 组件库</p> <p align="center"><b>皮肤可定制,使用 TypeScript</b></p>

462 lines (461 loc) 12.5 kB
import { getElementSize, getOffsetElement, getElementBorder, getScroll } from "../_utils/dom.mjs"; import { useOutClick } from "../hooks/use-out-click.mjs"; function getWrapperContentRect(wrapperEl, wrapperRect) { const { left = 0, top = 0, right = 0, bottom = 0 } = getElementBorder(wrapperEl); const rect = wrapperRect || wrapperEl.getBoundingClientRect(); return { left: rect.left + left, top: rect.top + top, right: rect.right - right, bottom: rect.bottom - bottom }; } function getPopupViewOffset(position, t, p, { offset = 0 } = {}) { const formula = { top: { left: t.left + t.width / 2 - p.width / 2, top: t.top - p.height - offset }, tl: { left: t.left, top: t.top - p.height - offset }, tr: { left: t.right - p.width, top: t.top - p.height - offset }, bottom: { left: t.left + t.width / 2 - p.width / 2, top: t.bottom + offset }, bl: { left: t.left, top: t.bottom + offset }, br: { left: t.right - p.width, top: t.bottom + offset }, left: { left: t.left - p.width - offset, top: t.top + t.height / 2 - p.height / 2 }, lt: { left: t.left - p.width - offset, top: t.top }, lb: { left: t.left - p.width - offset, top: t.bottom - p.height }, right: { left: t.right + offset, top: t.top + t.height / 2 - p.height / 2 }, rt: { left: t.right + offset, top: t.top }, rb: { left: t.right + offset, top: t.bottom - p.height } }; return formula[position] || { left: 0, top: 0 }; } function getWrapperViewEdge(popupSize, wrapperRect, edgeOffset = 0) { const viewport = { left: edgeOffset, right: window.innerWidth - popupSize.width - edgeOffset, top: edgeOffset, bottom: window.innerHeight - popupSize.height - edgeOffset }; if (!wrapperRect) { return viewport; } return { left: Math.max(viewport.left, wrapperRect.left), top: Math.max(viewport.top, wrapperRect.top), right: Math.min(viewport.right, wrapperRect.right - popupSize.width), bottom: Math.min(viewport.bottom, wrapperRect.bottom - popupSize.height) }; } function getPopupEdge(popupSize, targetRect, anchorOffset = 0) { const { left, right, top, bottom } = targetRect; const { width, height } = popupSize; return { left: left - width + anchorOffset, top: top - height + anchorOffset, right: right - anchorOffset, bottom: bottom - anchorOffset }; } function getPopupWrapOffset(pos, wrapperEl, wrapperContentRect) { if (!wrapperEl) { return pos; } const cs = getScroll(wrapperEl); if (wrapperContentRect) { return { left: pos.left + cs.scrollLeft - wrapperContentRect.left, top: pos.top + cs.scrollTop - wrapperContentRect.top }; } return { left: pos.left + cs.scrollLeft, top: pos.top + cs.scrollTop }; } function getDirection(position) { switch (position) { case "tl": case "tr": case "top": return "top"; case "bl": case "br": case "bottom": return "bottom"; case "lt": case "lb": case "left": return "left"; case "rt": case "rb": case "right": return "right"; } } function adjustPosition(position, direction) { const fixFn = { top: (p) => { if (p === "bottom") { return "top"; } else if (p === "bl") { return "tl"; } else if (p === "br") { return "tr"; } return p; }, bottom: (p) => { if (p === "top") { return "bottom"; } else if (p === "tl") { return "bl"; } else if (p === "tr") { return "br"; } return p; }, left: (p) => { if (p === "right") { return "left"; } else if (p === "rt") { return "lt"; } else if (p === "rb") { return "lb"; } return p; }, right: (p) => { if (p === "left") { return "right"; } else if (p === "lt") { return "rt"; } else if (p === "lb") { return "rb"; } return p; } }; const fn = fixFn[direction]; return fn ? fn(position) : position; } function adjustOffset(position, popupPosition, popupSize, pRect, tRect, wRect, { anchorOffset, offset, edgeOffset } = {}) { const { top, left } = popupPosition; const edge = getWrapperViewEdge(popupSize, wRect, edgeOffset); let fixedPosition = position; let style = popupPosition; const d = getDirection(position); if (d === "top") { if (edge.top > top) { fixedPosition = adjustPosition(position, "bottom"); style = getPopupViewOffset(fixedPosition, tRect, pRect, { offset }); } } else if (d === "left") { if (edge.left > left) { fixedPosition = adjustPosition(position, "right"); style = getPopupViewOffset(fixedPosition, tRect, pRect, { offset }); } } else if (d === "right") { if (edge.right < left) { fixedPosition = adjustPosition(position, "left"); style = getPopupViewOffset(fixedPosition, tRect, pRect, { offset }); } } else if (d === "bottom") { if (edge.bottom < top) { fixedPosition = adjustPosition(position, "top"); style = getPopupViewOffset(fixedPosition, tRect, pRect, { offset }); } } const popEdge = getPopupEdge(popupSize, tRect, anchorOffset); if (["top", "bottom"].includes(d)) { if (edge.left > left) { style.left = edge.left < popEdge.right ? edge.left : popEdge.right; } else if (edge.right < left) { style.left = edge.right > popEdge.left ? edge.right : popEdge.left; } } if (["left", "right"].includes(d)) { if (edge.top > top) { style.top = edge.top < popEdge.bottom ? edge.top : popEdge.bottom; } else if (edge.bottom < top) { style.top = edge.bottom > popEdge.top ? edge.bottom : popEdge.top; } } return { position: fixedPosition, popupStyle: style }; } function getAnchorOffset(position, tRect, popupStyle, popupSize, anchorOffset = 8) { const pos = {}; const limit = anchorOffset; const { left: pl, top: pt } = popupStyle; if (["top", "tl", "tr", "bottom", "bl", "br"].includes(position)) { let l = tRect.left + tRect.width / 2 - pl; if (l > popupSize.width - limit) { l = popupSize.width - limit; } else if (l < limit) { l = limit; } pos.left = l; if (["top", "tl", "tr"].includes(position)) { pos.bottom = 0; } else { pos.top = 0; } } else if (["left", "lt", "lb", "right", "rt", "rb"].includes(position)) { let t = tRect.top + tRect.height / 2 - pt; if (t > popupSize.height - limit) { t = popupSize.height - limit; } else if (t < limit) { t = limit; } pos.top = t; if (["left", "lt", "lb"].includes(position)) { pos.right = 0; } else { pos.left = 0; } } return pos; } function calcPopupStyle(popupEl, targetEl, position, { adaptive = true, // 自适应容器边缘 anchor = true, // 是否计算anchor anchorOffset = 8, // anchor与容器边缘偏移量 offset = 8, // popup 距离target偏移量 edgeOffset = 0 // popup 与容器边缘最小距离 } = {}) { const tRect = targetEl.getBoundingClientRect(); const pRect = popupEl.getBoundingClientRect(); const popupSize = getElementSize(popupEl); let popupStyle = getPopupViewOffset(position, tRect, pRect, { offset }); let anchorStyle = {}; const wrapperEl = getOffsetElement(popupEl); if (!wrapperEl) { return { popupStyle, position, anchorStyle }; } const wrapperRect = wrapperEl.getBoundingClientRect(); let wrapperContentRect = void 0; if (wrapperEl.nodeName !== "HTML") { wrapperContentRect = getWrapperContentRect(wrapperEl, wrapperRect); } let fixedPosition = position; if (adaptive) { const rlt = adjustOffset(position, popupStyle, popupSize, pRect, tRect, wrapperContentRect, { offset, anchorOffset: anchor ? anchorOffset : 0, edgeOffset }); fixedPosition = rlt.position; popupStyle = rlt.popupStyle; } if (anchor) { anchorStyle = getAnchorOffset(fixedPosition, tRect, popupStyle, popupSize, anchorOffset); } popupStyle = getPopupWrapOffset(popupStyle, wrapperEl, wrapperContentRect); return { position: fixedPosition, popupStyle, anchorStyle, isFixed: fixedPosition === position }; } function bindTrigger(el, popupRef, triggers, { updateFn, hoverDelay = 100, autoHide = true }) { if (!el) { return []; } const outClick = useOutClick(); const listeners = []; const showFn = () => { updateFn(true); }; const hideFn = () => { updateFn(false); }; const toggleFn = () => { updateFn(); }; const enterFn = () => { updateFn(true, hoverDelay); }; const leavefn = () => { updateFn(false, hoverDelay); }; const onClickFn = () => { hideFn(); outClick.removeListener(el, onClickFn); }; const clickFn = (toggle) => { const handlerFn = toggle ? toggleFn : showFn; return () => { el == null ? void 0 : el.addEventListener("click", () => { handlerFn(); if (autoHide) { outClick.addListener(el, onClickFn, { exception: (e) => { var _a; return !!((_a = popupRef.value) == null ? void 0 : _a.contains(e.target)); } }); } }); listeners.push(() => { el == null ? void 0 : el.removeEventListener("click", handlerFn); }); }; }; const triggerHandlers = { hover: () => { el == null ? void 0 : el.addEventListener("mouseenter", enterFn); listeners.push(() => { el == null ? void 0 : el.removeEventListener("mouseenter", enterFn); }); if (autoHide) { el == null ? void 0 : el.addEventListener("mouseleave", leavefn); listeners.push(() => { el == null ? void 0 : el.removeEventListener("mouseleave", leavefn); }); } }, // 点击目标,切换显示 click: clickFn(true), // 点击目标,始终显示,当点击外部才隐藏 "click-outclick": clickFn(false), focus: () => { el == null ? void 0 : el.addEventListener("focusin", showFn); listeners.push(() => { el == null ? void 0 : el.removeEventListener("focusin", showFn); }); if (autoHide) { el == null ? void 0 : el.addEventListener("focusout", hideFn); listeners.push(() => { el == null ? void 0 : el.removeEventListener("focusout", hideFn); }); } }, contextmenu: () => { const fn = (e) => { e.preventDefault(); showFn(); }; el == null ? void 0 : el.addEventListener("contextmenu", fn); listeners.push(() => { el == null ? void 0 : el.removeEventListener("contextmenu", fn); }); if (autoHide) { outClick.addListener(el, hideFn, { exception: (e) => { var _a; return !!((_a = popupRef.value) == null ? void 0 : _a.contains(e.target)); } }); listeners.push(() => { outClick.removeListener(el, hideFn); }); } }, none: () => { }, // hover 显示 outclick隐藏 "hover-outclick": () => { el == null ? void 0 : el.addEventListener("mouseenter", enterFn); listeners.push(() => { el == null ? void 0 : el.removeEventListener("mouseenter", enterFn); }); if (autoHide) { outClick.addListener(el, hideFn, { exception: (e) => { var _a; return !!((_a = popupRef.value) == null ? void 0 : _a.contains(e.target)); } }); listeners.push(() => { outClick.removeListener(el, hideFn); }); } } }; triggers.forEach((tr) => { const fn = triggerHandlers[tr]; if (fn) { fn(); } }); return listeners; } function getTransformOrigin(position) { let left = "0px"; let top = "0px"; if (["lt", "lb", "left", "tr", "br"].includes(position)) { left = "100%"; } else if (["top", "bottom"].includes(position)) { left = "50%"; } if (["tl", "tr", "top", "lb", "rb"].includes(position)) { top = "100%"; } else if (["left", "right"].includes(position)) { top = "50%"; } return { top, left }; } export { bindTrigger, calcPopupStyle, getTransformOrigin };