@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>
428 lines (427 loc) • 14.7 kB
JavaScript
import { defineComponent, computed, ref, reactive, onMounted, toRefs, onUnmounted, watch, nextTick, createElementBlock, openBlock, Fragment, createBlock, createCommentVNode, unref, withCtx, renderSlot, Teleport, createVNode, mergeProps, Transition, withDirectives, createElementVNode, normalizeStyle, normalizeClass, vShow } from "vue";
import { popupProps } from "./types.mjs";
import { isHtmlElement, getScrollParents } from "../_utils/dom.mjs";
import { debounce, throttleRAF } from "../_utils/helper.mjs";
import { isArray, isFunction } from "../_utils/is.mjs";
import { bindTrigger, calcPopupStyle, getTransformOrigin } from "./popup.mjs";
import { useResizeObserver } from "../hooks/use-resize-observer.mjs";
import OResizeObserver from "../resize-observer/resize-observer.mjs";
import { useIntersectionObserver } from "../hooks/use-intersection-observer.mjs";
import { useScreen } from "../hooks/use-screen.mjs";
import OChildOnly from "../child-only/child-only.mjs";
import ClientOnly from "../_components/client-only.mjs";
import { resolveHtmlElement } from "../_utils/vue-utils.mjs";
import { createTopZIndex, removeZIndex } from "../_utils/z-index.mjs";
const __default__ = {
inheritAttrs: false
};
const _sfc_main = /* @__PURE__ */ defineComponent({
...__default__,
__name: "OPopup",
props: popupProps,
emits: ["update:visible", "change"],
setup(__props, { emit: __emit }) {
const props = __props;
const emits = __emit;
const { isPhonePad } = useScreen();
const triggers = computed(() => {
const triggers2 = isArray(props.trigger) ? props.trigger : [props.trigger];
if (isPhonePad.value) {
const r = triggers2.filter((item) => ["none", "click-outclick", "click"].includes(item));
return r.length > 0 ? r : ["click"];
}
return triggers2;
});
const visible = ref(false);
const targetElRef = ref(null);
let targetEl = null;
const isTargetInViewport = ref(true);
let wrapperEl = ref(null);
const popupRef = ref(null);
const popStyle = reactive({
"--popup-edge-offset": `${props.edgeOffset}px`
});
const popPosition = ref(props.position);
const wrapOrigin = ref({ left: "0px", top: "0px" });
const wrapStyle = computed(() => ({
transformOrigin: `${wrapOrigin.value.left} ${wrapOrigin.value.top}`
}));
const anchorStyle = reactive({});
const toMount = ref(false);
const isAnimating = ref(false);
let ro = null;
let io = null;
const updateZIndex = (show) => {
if (show) {
popStyle["--popup-z-index"] = createTopZIndex();
} else {
removeZIndex(popStyle["--popup-z-index"]);
}
};
onMounted(() => {
ro = useResizeObserver();
io = useIntersectionObserver();
visible.value = props.visible;
if (props.visible) {
updateZIndex(props.visible);
}
const { target, wrapper } = toRefs(props);
resolveHtmlElement(target).then((el) => {
if (el) {
bindTargetEvent(el);
}
});
resolveHtmlElement(wrapper).then((el) => {
if (el) {
wrapperEl.value = el;
}
});
});
let triggerListener = [];
const bindTargetEvent = (el) => {
if (!el) {
return;
}
targetEl = el;
if (props.adjustMinWidth) {
popStyle.minWidth = `${targetEl.offsetWidth}px`;
} else if (props.adjustWidth) {
popStyle.width = `${targetEl.offsetWidth}px`;
}
triggerListener = bindTrigger(el, popupRef, triggers.value, {
updateFn: updateVisible,
hoverDelay: props.hoverDelay,
autoHide: props.autoHide
});
if (props.hideWhenTargetInvisible) {
io == null ? void 0 : io.observe(targetEl, onTargetInterscting);
}
};
onUnmounted(() => {
triggerListener.forEach((fn) => {
fn();
});
if (wrapperEl.value) {
ro == null ? void 0 : ro.unobserve(wrapperEl.value, onResize);
}
if (targetEl) {
ro == null ? void 0 : ro.unobserve(targetEl, onResize);
}
});
const updatePopupStyle = () => {
if (props.hideWhenTargetInvisible && !isTargetInViewport.value) {
return;
}
if (!targetEl || !popupRef.value || !wrapperEl.value) {
return;
}
const {
popupStyle: pStyle,
position,
anchorStyle: aStyle
} = calcPopupStyle(popupRef.value, targetEl, props.position, {
adaptive: props.adaptive,
offset: props.offset,
edgeOffset: props.edgeOffset,
anchor: props.anchor
});
wrapOrigin.value = getTransformOrigin(position);
if (pStyle) {
popStyle.top = `${Math.floor(pStyle.top)}px`;
popStyle.left = `${Math.floor(pStyle.left)}px`;
popPosition.value = position;
}
if (aStyle) {
Object.keys(aStyle).forEach((k) => {
const val = aStyle[k];
anchorStyle[k] = `${Math.floor(val)}px`;
});
}
};
let oldIntersecting = null;
const onTargetInterscting = (entry) => {
isTargetInViewport.value = entry.isIntersecting;
if (oldIntersecting !== null && entry.isIntersecting) {
if (visible.value) {
nextTick(() => {
updatePopupStyle();
});
}
}
oldIntersecting = isTargetInViewport.value;
};
const beforeToggle = async (show) => {
let goon = true;
if (show) {
if (isFunction(props.beforeShow)) {
goon = await props.beforeShow();
}
} else {
if (isFunction(props.beforeHide)) {
goon = await props.beforeHide();
}
}
return goon !== false;
};
watch(
() => props.visible,
async (val) => {
if (visible.value === val) {
return;
}
const goon = await beforeToggle(val);
if (!goon) {
emits("update:visible", visible.value);
return;
}
updateVisible(val);
updateZIndex(val);
if (val) {
nextTick(() => {
updatePopupStyle();
});
}
}
);
let visibleTimer = 0;
const clearVisibleTimer = () => {
if (visibleTimer) {
window.clearTimeout(visibleTimer);
visibleTimer = 0;
}
};
const updateVisible = async (isVisible, delay) => {
if (props.disabled) {
return;
}
const v = isVisible === void 0 ? !visible.value : isVisible;
if (v === visible.value && visibleTimer === 0) {
return;
}
const update = () => {
if (visible.value === v) {
return;
}
visible.value = v;
updateZIndex(v);
emits("update:visible", v);
emits("change", v);
if (v) {
toMount.value = true;
if (props.hideWhenTargetInvisible && targetEl) {
io == null ? void 0 : io.observe(targetEl, onTargetInterscting);
}
}
};
const goon = await beforeToggle(v);
if (!goon) {
return;
}
if (delay) {
clearVisibleTimer();
visibleTimer = window.setTimeout(update, delay);
} else {
update();
}
};
watch(targetElRef, (elRef) => {
if (isHtmlElement(elRef == null ? void 0 : elRef.$el)) {
bindTargetEvent(elRef == null ? void 0 : elRef.$el);
}
});
const onResize = (_en, isFirst) => {
if (visible.value && !isFirst) {
updatePopupStyle();
}
};
const onPopupResize = debounce((en) => {
onResize(en, false);
}, 100);
const handleTransitionStart = () => {
isAnimating.value = true;
};
const handleTransitionEnd = () => {
isAnimating.value = false;
if (!visible.value && props.unmountOnHide) {
toMount.value = false;
}
};
const scrollListener = throttleRAF(() => {
if (visible.value) {
updatePopupStyle();
}
});
const listenScroll = (el) => {
el.addEventListener("scroll", scrollListener, { passive: true });
return () => {
el.removeEventListener("scroll", scrollListener);
};
};
watch(popupRef, (popEl) => {
let handles = [];
if (popEl) {
if (targetEl) {
const scrollers = getScrollParents(targetEl);
handles = scrollers.map((el) => {
return listenScroll(el);
});
ro == null ? void 0 : ro.observe(targetEl, (en, isFirst) => {
if (props.adjustMinWidth) {
popStyle.minWidth = `${targetEl == null ? void 0 : targetEl.offsetWidth}px`;
} else if (props.adjustWidth) {
popStyle.width = `${targetEl == null ? void 0 : targetEl.offsetWidth}px`;
}
onResize(en, isFirst);
});
}
if (wrapperEl.value) {
ro == null ? void 0 : ro.observe(wrapperEl.value, onResize);
}
} else {
handles.forEach((hl) => hl());
if (wrapperEl.value) {
ro == null ? void 0 : ro.unobserve(wrapperEl.value, onResize);
}
if (targetEl) {
ro == null ? void 0 : ro.unobserve(targetEl, onResize);
io == null ? void 0 : io.unobserve(targetEl, onTargetInterscting);
isTargetInViewport.value = true;
}
}
});
const onPopupHoverIn = () => {
if (triggers.value.includes("hover")) {
updateVisible(true, props.hoverDelay);
}
};
const onPopupHoverOut = () => {
if (triggers.value.includes("hover") && props.autoHide) {
updateVisible(false, props.hoverDelay);
}
};
const sholdUmMount = computed(() => {
return toMount.value || visible.value || !props.unmountOnHide;
});
return (_ctx, _cache) => {
return openBlock(), createElementBlock(
Fragment,
null,
[
_ctx.$slots.target ? (openBlock(), createBlock(
unref(OChildOnly),
{
key: 0,
ref_key: "targetElRef",
ref: targetElRef
},
{
default: withCtx(() => [
renderSlot(_ctx.$slots, "target")
]),
_: 3
/* FORWARDED */
},
512
/* NEED_PATCH */
)) : createCommentVNode("v-if", true),
!props.disabled ? (openBlock(), createBlock(unref(ClientOnly), { key: 1 }, {
default: withCtx(() => [
(openBlock(), createBlock(Teleport, {
to: props.wrapper,
disabled: !props.wrapper
}, [
createVNode(unref(OResizeObserver), { onResize: unref(onPopupResize) }, {
default: withCtx(() => [
sholdUmMount.value ? (openBlock(), createElementBlock(
"div",
mergeProps({
key: 0,
ref_key: "popupRef",
ref: popupRef,
class: ["o-popup", [
`o-popup-pos-${popPosition.value}`,
{
"out-view": props.hideWhenTargetInvisible && !isTargetInViewport.value,
animating: isAnimating.value
}
]],
style: popStyle
}, _ctx.$attrs, {
onMouseenter: onPopupHoverIn,
onMouseleave: onPopupHoverOut
}),
[
createVNode(Transition, {
name: props.transition,
appear: true,
onBeforeEnter: handleTransitionStart,
onAfterEnter: handleTransitionEnd,
onBeforeLeave: handleTransitionStart,
onAfterLeave: handleTransitionEnd,
persisted: ""
}, {
default: withCtx(() => [
withDirectives(createElementVNode(
"div",
{
class: normalizeClass(["o-popup-wrap", props.wrapClass]),
style: normalizeStyle(wrapStyle.value)
},
[
createElementVNode(
"div",
{
class: normalizeClass(["o-popup-body", props.bodyClass])
},
[
renderSlot(_ctx.$slots, "default")
],
2
/* CLASS */
),
props.anchor ? (openBlock(), createElementBlock(
"div",
{
key: 0,
class: normalizeClass(["o-popup-anchor", props.anchorClass]),
style: normalizeStyle(anchorStyle)
},
[
renderSlot(_ctx.$slots, "anchor")
],
6
/* CLASS, STYLE */
)) : createCommentVNode("v-if", true)
],
6
/* CLASS, STYLE */
), [
[vShow, visible.value]
])
]),
_: 3
/* FORWARDED */
}, 8, ["name"])
],
16
/* FULL_PROPS */
)) : createCommentVNode("v-if", true)
]),
_: 3
/* FORWARDED */
}, 8, ["onResize"])
], 8, ["to", "disabled"]))
]),
_: 3
/* FORWARDED */
})) : createCommentVNode("v-if", true)
],
64
/* STABLE_FRAGMENT */
);
};
}
});
export {
_sfc_main as default
};