song-ui-u
Version:
vue3 + js的PC前端组件库
189 lines (163 loc) • 6.14 kB
JavaScript
import { ref, watchEffect, onUnmounted, watch, nextTick, onMounted, openBlock, createElementBlock, normalizeClass, renderSlot, createVNode, Transition, withCtx, toDisplayString, createCommentVNode } from 'vue';
import { useNamespace } from '../../../hook/use-namespace/index.mjs';
import 'song-ui-pro-icon';
import '../../../hook/use-zindex/index.mjs';
import _export_sfc from '../../../_virtual/_plugin-vue_export-helper.mjs';
const _sfc_main = /*#__PURE__*/Object.assign({
name: "x-popover",
}, {
__name: 'popover',
props: {
content: { type: String }, // 提示内容
position: { type: String, default: "top" }, // 位置:top/bottom/left/right
trigger: { type: String, default: "hover" }, // 触发方式:hover/click
delay: { type: Number, default: 300 }, // 延迟显示(仅 hover)
title: { type: String, default: "" }, // 标题
},
setup(__props, { expose: __expose }) {
__expose();
const ns = useNamespace("popover");
const props = __props;
const isVisible = ref(false);
let timer = null;
// 事件处理
const handleHover = (state) => {
if (props.trigger !== "hover") return;
clearTimeout(timer);
timer = setTimeout(() => (isVisible.value = state), state ? props.delay : 0);
};
const handleClick = () => {
if (props.trigger == "click") {
isVisible.value = !isVisible.value;
}
};
// 点击外部关闭(仅 click 模式)
const clickOutsideHandler = (e) => {
// 使用 ns.b() 获取正确的类名
if (!isVisible.value || !e.target.closest(`.${ns.b()}`)) {
isVisible.value = false;
}
};
// 生命周期
watchEffect(() => {
if (props.trigger === "click") {
document.addEventListener("click", clickOutsideHandler);
} else {
document.removeEventListener("click", clickOutsideHandler);
}
});
onUnmounted(() => {
document.removeEventListener("click", clickOutsideHandler);
});
// 添加位置计算逻辑
const popoverRef = ref(null);
const computedPosition = ref(props.position);
const updatePosition = () => {
if (!popoverRef.value) return;
// const triggerEl = popoverRef.value.parentElement;
const triggerEl =
popoverRef.value.parentElement.querySelector(":first-child");
const popoverEl = popoverRef.value;
const rect = triggerEl.getBoundingClientRect();
const popoverRect = popoverEl.getBoundingClientRect();
// 获取视口尺寸
const viewportWidth = window.innerWidth;
const viewportHeight = window.innerHeight;
// 检查各个方向的可用空间
const spaces = {
top: rect.top,
bottom: viewportHeight - rect.bottom,
left: rect.left,
right: viewportWidth - rect.right,
};
// 根据当前位置和可用空间自动调整
switch (props.position) {
case "top":
if (spaces.top < popoverRect.height) {
computedPosition.value = "bottom";
}
break;
case "bottom":
if (spaces.bottom < popoverRect.height) {
computedPosition.value = "top";
}
break;
case "left":
if (spaces.left < popoverRect.width) {
computedPosition.value = "right";
}
break;
case "right":
if (spaces.right < popoverRect.width) {
computedPosition.value = "left";
}
break;
}
};
// 监听显示状态变化时更新位置
watch(
() => isVisible.value,
(val) => {
if (val) {
nextTick(() => {
updatePosition();
});
}
}
);
// 监听窗口大小变化
onMounted(() => {
window.addEventListener("resize", updatePosition);
});
onUnmounted(() => {
window.removeEventListener("resize", updatePosition);
});
const __returned__ = { ns, props, isVisible, get timer() { return timer }, set timer(v) { timer = v; }, handleHover, handleClick, clickOutsideHandler, popoverRef, computedPosition, updatePosition, ref, watch, watchEffect, onUnmounted, onMounted, nextTick, get useNamespace() { return useNamespace } };
Object.defineProperty(__returned__, '__isScriptSetup', { enumerable: false, value: true });
return __returned__
}
});
const _hoisted_1 = { key: 1 };
const _hoisted_2 = { key: 2 };
function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
return (openBlock(), createElementBlock("div", {
class: normalizeClass($setup.ns.b()),
onMouseenter: _cache[0] || (_cache[0] = $event => ($setup.handleHover(true))),
onMouseleave: _cache[1] || (_cache[1] = $event => ($setup.handleHover(false))),
onClick: $setup.handleClick
}, [
renderSlot(_ctx.$slots, "default"),
createVNode(Transition, {
name: $setup.ns.b()
}, {
default: withCtx(() => [
($setup.isVisible)
? (openBlock(), createElementBlock("div", {
key: 0,
ref: "popoverRef",
class: normalizeClass([$setup.ns.e('content'), $setup.ns.m('content', $setup.computedPosition)])
}, [
($props.title)
? (openBlock(), createElementBlock("div", {
key: 0,
class: normalizeClass($setup.ns.e('title'))
}, toDisplayString($props.title), 3 /* TEXT, CLASS */))
: createCommentVNode("v-if", true),
($props.content)
? (openBlock(), createElementBlock("div", _hoisted_1, toDisplayString($props.content), 1 /* TEXT */))
: createCommentVNode("v-if", true),
(_ctx.$slots?.content && !$props.content)
? (openBlock(), createElementBlock("div", _hoisted_2, [
renderSlot(_ctx.$slots, "content")
]))
: createCommentVNode("v-if", true)
], 2 /* CLASS */))
: createCommentVNode("v-if", true)
]),
_: 3 /* FORWARDED */
}, 8 /* PROPS */, ["name"])
], 34 /* CLASS, NEED_HYDRATION */))
}
var Popover = /*#__PURE__*/_export_sfc(_sfc_main, [['render',_sfc_render],['__file',"E:\\code\\my-code\\song-ui-ultra\\packages\\components\\popover\\src\\popover.vue"]]);
export { Popover as default };
//# sourceMappingURL=popover.vue.mjs.map