@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
JavaScript
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
};