birdpaper-ui
Version:
一个通用的 vue3 UI组件库。A common vue3 UI component library.
201 lines (200 loc) • 5.83 kB
JavaScript
import { defineComponent, ref, onMounted, nextTick, onBeforeUnmount, watch, createVNode, h, Teleport, Transition, withDirectives, vShow, resolveDirective } from "vue";
import { getWrapperSize, getPositionData, getWrapperPositionStyle } from "./core.js";
import { on, off, throttle } from "../../utils/util.js";
import { vClickOutside } from "../../directives/clickOutside.js";
import { getScrollElements } from "../../utils/dom.js";
const _trigger = /* @__PURE__ */ defineComponent({
name: "Trigger",
directives: {
clickOutside: vClickOutside
},
props: {
popupVisible: {
type: Boolean,
default: false
},
/** 触发方式 */
trigger: {
type: String,
default: "click"
},
/** 弹出位置 */
position: {
type: String,
default: "bottom"
},
/** 距离弹出位置的偏移量 */
popupOffset: {
type: Number,
default: 10
},
/** 距离弹出位置的移动距离 */
popupTranslate: {
type: Array,
default: [0, 0]
},
/** 弹出层是否填充触发器宽度 */
autoFitWidth: {
type: Boolean,
default: false
},
/** 过渡动画名称 */
transition: {
type: String,
default: "fade"
},
/** 点击其他元素关闭触发器 */
clickOutside: {
type: Boolean,
default: true
},
/** 是否禁用 Disabled or not */
disabled: {
type: Boolean,
default: false
},
/** 隐藏触发器 */
hideTrigger: {
type: Boolean,
default: false
},
/** 是否跟随滚动元素更新 */
updateAtScroll: {
type: Boolean,
default: false
},
/** 是否在滚动时关闭 */
scrollToClose: {
type: Boolean,
default: false
},
/** 滚动触发关闭的时间 */
scrollToCloseTime: {
type: Number,
default: 400
}
},
emits: ["update:popupVisible"],
setup(props, {
emit,
slots
}) {
const name = "bp-trigger";
const triggerRef = ref();
const wrapperRef = ref();
const visible = ref(props.popupVisible || false);
const clickOutsideLock = ref(true);
const scrollElements = ref([]);
const handleClick = () => {
if (props.trigger === "hover" || props.disabled)
return;
updateVisible(!visible.value);
handleResize();
};
const handleMouseEnter = () => {
if (props.trigger === "click")
return;
updateVisible(true);
handleResize();
};
const handleMouseLeave = () => {
if (props.trigger === "click")
return;
updateVisible(false);
};
const handleResize = () => {
if (!triggerRef.value || !visible.value)
return;
const wrapperSize = getWrapperSize(wrapperRef.value);
const {
top,
left,
width
} = getPositionData(triggerRef.value.children[0], props.position, {
...wrapperSize
}, props.popupTranslate, props.popupOffset, props.autoFitWidth);
const styleStr = getWrapperPositionStyle(top, left, visible.value, props.autoFitWidth ? width : null);
wrapperRef.value.setAttribute("style", styleStr);
if (props.scrollToClose && visible.value) {
setTimeout(() => {
updateVisible(false);
}, props.scrollToCloseTime);
}
};
const onClickOutside = () => {
if (!props.clickOutside)
return;
!clickOutsideLock.value && updateVisible(false);
};
const updateVisible = (val, delay = 100) => {
visible.value = val;
emit("update:popupVisible", visible.value);
nextTick(() => {
setTimeout(() => {
if (props.trigger === "click") {
clickOutsideLock.value = !visible.value;
}
}, delay);
});
};
onMounted(() => {
nextTick(() => {
on(window, "resize", throttle(handleResize));
if (props.updateAtScroll) {
scrollElements.value = getScrollElements(triggerRef.value);
for (const item of scrollElements.value) {
on(item, "scroll", throttle(handleResize));
}
}
});
});
onBeforeUnmount(() => {
off(window, "resize", throttle(handleResize));
off(window, "scroll", throttle(handleResize));
if (props.updateAtScroll) {
scrollElements.value = getScrollElements(triggerRef.value);
for (const item of scrollElements.value) {
off(item, "scroll", throttle(handleResize));
}
}
});
watch(() => props.popupVisible, (v) => {
visible.value = v;
clickOutsideLock.value = visible.value;
});
const render = () => {
var _a, _b;
const children = ((_a = slots.default) == null ? void 0 : _a.call(slots)) || [];
return props.hideTrigger ? (_b = slots.content) == null ? void 0 : _b.call(slots) : createVNode("div", {
"class": name,
"ref": triggerRef,
"id": "triggerRef"
}, [h(children[0], {
onClick: handleClick,
onMouseenter: handleMouseEnter,
onMouseleave: handleMouseLeave
}), createVNode(Teleport, {
"to": "body"
}, {
default: () => [createVNode(Transition, {
"name": props.transition,
"appear": true
}, {
default: () => {
var _a2;
return [withDirectives(createVNode("div", {
"ref": wrapperRef,
"class": `${name}-wrapper`,
"onMouseenter": handleMouseEnter,
"onMouseleave": handleMouseLeave
}, [(_a2 = slots.content) == null ? void 0 : _a2.call(slots)]), [[vShow, visible.value], [resolveDirective("clickOutside"), onClickOutside]])];
}
})]
})]);
};
return render;
}
});
export {
_trigger as default
};