UNPKG

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