UNPKG

@opensig/opendesign

Version:

473 lines (472 loc) 15.9 kB
"use strict"; const vue = require("vue"); const types = require("./types.js"); const dom = require("../_utils/dom.js"); const helper = require("../_utils/helper.js"); const is = require("../_utils/is.js"); const popup = require("./popup.js"); const useResizeObserver = require("../hooks/use-resize-observer.js"); const resizeObserver = require("../resize-observer/resize-observer.js"); const useIntersectionObserver = require("../hooks/use-intersection-observer.js"); require("../hooks/use-theme.js"); require("../_utils/global.js"); require("@vueuse/core"); const vueUtils = require("../_utils/vue-utils.js"); const childOnly = require("../child-only/child-only.js"); const clientOnly = require("../_components/client-only.js"); const zIndex = require("../_utils/z-index.js"); const __default__ = { inheritAttrs: false }; const _sfc_main = /* @__PURE__ */ vue.defineComponent({ ...__default__, __name: "OPopup", props: types.popupProps, emits: ["update:visible", "change"], setup(__props, { emit: __emit }) { const props = __props; const emits = __emit; const triggers = vue.computed(() => { const triggers2 = is.isArray(props.trigger) ? props.trigger : [props.trigger]; if (is.isTouchDevice) { const hasClick = triggers2.some((item) => item === "click" || item === "click-outclick"); return hasClick || triggers2.includes("none") ? triggers2 : [...triggers2, "click"]; } return triggers2; }); const visible = vue.ref(false); const targetElRef = vue.ref(null); let targetEl = null; const isTargetInViewport = vue.ref(true); const wrapperEl = vue.ref(null); const popupRef = vue.ref(null); const popStyle = vue.reactive({ "--popup-edge-offset": `${props.edgeOffset}px`, // left, top 恒为 0px left: "0px", top: "0px" }); const popPosition = vue.ref(props.position); const wrapOrigin = vue.ref({ left: "0px", top: "0px" }); const wrapStyle = vue.computed(() => ({ transformOrigin: `${wrapOrigin.value.left} ${wrapOrigin.value.top}` })); const anchorStyle = vue.ref({}); const toMount = vue.ref(false); const isAnimating = vue.ref(false); let ro = null; let io = null; const updateZIndex = (show) => { if (show) { popStyle["--popup-z-index"] = zIndex.createTopZIndex(); } else { zIndex.removeZIndex(popStyle["--popup-z-index"]); } }; const { target, wrapper } = vue.toRefs(props); vue.onMounted(() => { ro = useResizeObserver.useResizeObserver(); io = useIntersectionObserver.useIntersectionObserver(); visible.value = props.visible; if (props.visible) { updateZIndex(props.visible); } }); vue.onMounted(() => { vue.watch( target, (newVal) => { if (newVal && targetEl) { ro == null ? void 0 : ro.unobserve(targetEl, onResize); } if (newVal) { const el = vueUtils.getHtmlElement(newVal); if (el) { bindTargetEvent(el); updatePopupStyle(); } } }, { immediate: true } ); }); vue.onMounted(() => { vue.watch( wrapper, () => { if (wrapperEl.value) { ro == null ? void 0 : ro.unobserve(wrapperEl.value, onResize); } vueUtils.resolveHtmlElement(wrapper).then((el) => { if (el) { wrapperEl.value = el; } }); }, { immediate: true } ); }); let triggerListener = []; const removeTriggerListener = () => triggerListener.forEach((fn) => fn()); const bindTargetEvent = (el) => { if (!el) { return; } removeTriggerListener(); targetEl = el; if (props.adjustMinWidth) { popStyle.minWidth = `${targetEl.offsetWidth}px`; } else if (props.adjustWidth) { popStyle.width = `${targetEl.offsetWidth}px`; } triggerListener = popup.bindTrigger({ el, popupRef, triggers: triggers.value, updateFn: setVisible, hoverDelay: props.hoverDelay, autoHide: props.autoHide }); if (props.hideWhenTargetInvisible) { io == null ? void 0 : io.observe(targetEl, onTargetInterscting); } }; vue.onUnmounted(() => { removeTriggerListener(); if (wrapperEl.value) { ro == null ? void 0 : ro.unobserve(wrapperEl.value, onResize); } if (targetEl) { ro == null ? void 0 : ro.unobserve(targetEl, onResize); } }); const isHiddenWhenTargetOutViewport = () => props.hideWhenTargetInvisible && !isTargetInViewport.value; const updatePopupStyle = () => { if (isHiddenWhenTargetOutViewport()) { return; } if (!targetEl || !popupRef.value || !popupContent.value) { return; } const { popupStyle: pStyle, position, anchorStyle: aStyle } = popup.calcPopupStyle({ popupEl: popupRef.value, targetEl, position: props.position, adaptive: props.adaptive, offset: props.offset, edgeOffset: props.edgeOffset, anchor: props.anchor }); wrapOrigin.value = popup.getTransformOrigin(position); popPosition.value = position; popStyle.transform = `translate(${pStyle.left}px, ${pStyle.top}px)`; anchorStyle.value = aStyle; }; let oldIntersecting = null; const onTargetInterscting = (entry) => { isTargetInViewport.value = entry.isIntersecting; if (oldIntersecting !== null && entry.isIntersecting) { if (visible.value) { vue.nextTick(() => { updatePopupStyle(); }); } } oldIntersecting = isTargetInViewport.value; }; const beforeToggle = async (show) => { let goon = true; if (show) { if (is.isFunction(props.beforeShow)) { goon = await props.beforeShow(); } } else { if (is.isFunction(props.beforeHide)) { goon = await props.beforeHide(); } } return goon !== false; }; vue.watch( () => props.visible, async (val) => { setVisible(val); } ); let visibleTimer = 0; const clearVisibleTimer = () => { if (visibleTimer) { window.clearTimeout(visibleTimer); visibleTimer = 0; } }; const applyVisible = (isVisible) => { visible.value = isVisible; updateZIndex(isVisible); if (props.visible !== isVisible) { emits("update:visible", isVisible); emits("change", isVisible); } if (visible.value) { toMount.value = true; if (props.hideWhenTargetInvisible && targetEl) { io == null ? void 0 : io.observe(targetEl, onTargetInterscting); } } }; let visibleToggleId = 0; const setVisible = async (isVisible, delay) => { if (props.disabled) { return; } const currentVisibleToggleId = ++visibleToggleId; const v = isVisible ?? !visible.value; if (v === visible.value && visibleTimer === 0) { return; } if (!await beforeToggle(v)) { return; } if (currentVisibleToggleId !== visibleToggleId) { return; } clearVisibleTimer(); if (delay) { visibleTimer = window.setTimeout( () => { applyVisible(v); visibleTimer = 0; }, delay /** delay 时间相同,无竞态问题 */ ); } else { applyVisible(v); } }; vue.watch(targetElRef, (elRef) => { if (dom.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 = helper.debounce( (en) => { onResize(en, false); }, 100, true, true ); const handleTransitionStart = () => { isAnimating.value = true; }; const popupContent = vue.ref(); const checkVisibleState = helper.debounce( () => { if (popupContent.value && visible.value === false && popupContent.value.style.display !== "none") { popupContent.value.style.display = "none"; } }, 200, // 动画播放时间 false ); const onBeforeLeave = () => { checkVisibleState(); handleTransitionStart(); }; const handleTransitionEnd = () => { isAnimating.value = false; if (!visible.value && props.unmountOnHide) { toMount.value = false; } }; const scrollListener = helper.throttleRAF(() => { if (visible.value) { updatePopupStyle(); } }); const listenScroll = (el) => { el.addEventListener("scroll", scrollListener, { passive: true }); return () => { el.removeEventListener("scroll", scrollListener); }; }; vue.watch(popupRef, (popEl) => { let handles = []; if (popEl) { if (targetEl) { const scrollers = dom.getScrollParents(targetEl); handles = scrollers.map((el) => { return listenScroll(el); }); handles.push(listenScroll(window)); 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")) { setVisible(true, props.hoverDelay); } }; const onPopupHoverOut = () => { if (triggers.value.includes("hover") && props.autoHide) { setVisible(false, props.hoverDelay); } }; const shouldMount = vue.computed(() => { return toMount.value || visible.value || !props.unmountOnHide; }); return (_ctx, _cache) => { return vue.openBlock(), vue.createElementBlock( vue.Fragment, null, [ _ctx.$slots.target ? (vue.openBlock(), vue.createBlock( vue.unref(childOnly), { key: 0, ref_key: "targetElRef", ref: targetElRef }, { default: vue.withCtx(() => [ vue.renderSlot(_ctx.$slots, "target") ]), _: 3 /* FORWARDED */ }, 512 /* NEED_PATCH */ )) : vue.createCommentVNode("v-if", true), !props.disabled ? (vue.openBlock(), vue.createBlock(vue.unref(clientOnly), { key: 1 }, { default: vue.withCtx(() => [ (vue.openBlock(), vue.createBlock(vue.Teleport, { to: props.wrapper, disabled: !props.wrapper }, [ vue.createVNode(vue.unref(resizeObserver), { onResize: vue.unref(onPopupResize) }, { default: vue.withCtx(() => [ shouldMount.value ? (vue.openBlock(), vue.createElementBlock( "div", vue.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 }), [ vue.createVNode(vue.Transition, { name: props.transition, appear: true, onBeforeEnter: handleTransitionStart, onAfterEnter: handleTransitionEnd, onBeforeLeave, onAfterLeave: handleTransitionEnd, persisted: "" }, { default: vue.withCtx(() => [ vue.withDirectives(vue.createElementVNode( "div", { ref_key: "popupContent", ref: popupContent, class: vue.normalizeClass(["o-popup-wrap", props.wrapClass]), style: vue.normalizeStyle(wrapStyle.value) }, [ vue.createElementVNode( "div", { class: vue.normalizeClass(["o-popup-body", props.bodyClass]) }, [ vue.renderSlot(_ctx.$slots, "default") ], 2 /* CLASS */ ), props.anchor ? (vue.openBlock(), vue.createElementBlock( "div", { key: 0, class: vue.normalizeClass(["o-popup-anchor", props.anchorClass]), style: vue.normalizeStyle(anchorStyle.value) }, [ vue.renderSlot(_ctx.$slots, "anchor") ], 6 /* CLASS, STYLE */ )) : vue.createCommentVNode("v-if", true) ], 6 /* CLASS, STYLE */ ), [ [vue.vShow, visible.value] ]) ]), _: 3 /* FORWARDED */ }, 8, ["name"]) ], 16 /* FULL_PROPS */ )) : vue.createCommentVNode("v-if", true) ]), _: 3 /* FORWARDED */ }, 8, ["onResize"]) ], 8, ["to", "disabled"])) ]), _: 3 /* FORWARDED */ })) : vue.createCommentVNode("v-if", true) ], 64 /* STABLE_FRAGMENT */ ); }; } }); module.exports = _sfc_main;