UNPKG

vue-devui

Version:

DevUI components based on Vite and Vue3

331 lines (330 loc) 9.6 kB
import { watch, onUnmounted, defineComponent, toRefs, createVNode, Transition, mergeProps, ref, computed, nextTick, unref, withModifiers } from "vue"; import { offset, flip, arrow, computePosition } from "@floating-ui/dom"; const fixedOverlayProps = { modelValue: { type: Boolean, default: false }, lockScroll: { type: Boolean, default: true }, closeOnClickOverlay: { type: Boolean, default: true } }; function lockScroll() { if (document.documentElement.scrollHeight > document.documentElement.clientHeight) { const scrollTop = document.documentElement.scrollTop; const style = document.documentElement.getAttribute("style"); document.documentElement.style.position = "fixed"; document.documentElement.style.top = `-${scrollTop}px`; document.documentElement.style.width = document.documentElement.style.width || "100%"; document.documentElement.style.overflowY = "scroll"; return () => { if (style) { document.documentElement.setAttribute("style", style); } else { document.documentElement.removeAttribute("style"); } document.documentElement.scrollTop = scrollTop; }; } return; } function useFixedOverlay(props, ctx) { let lockScrollCb; const onClick = (event) => { event.preventDefault(); ctx.emit("click", event); if (props.closeOnClickOverlay) { ctx.emit("update:modelValue", false); } }; const removeBodyAdditions = () => { lockScrollCb == null ? void 0 : lockScrollCb(); }; watch( () => props.modelValue, (val) => { if (val) { props.lockScroll && (lockScrollCb = lockScroll()); } else { removeBodyAdditions(); } } ); onUnmounted(removeBodyAdditions); return { onClick }; } function createBem(namespace, element, modifier) { let cls = namespace; if (element) { cls += `__${element}`; } if (modifier) { cls += `--${modifier}`; } return cls; } function useNamespace(block, needDot = false) { const namespace = needDot ? `.devui-${block}` : `devui-${block}`; const b = () => createBem(namespace); const e = (element) => element ? createBem(namespace, element) : ""; const m = (modifier) => modifier ? createBem(namespace, "", modifier) : ""; const em = (element, modifier) => element && modifier ? createBem(namespace, element, modifier) : ""; return { b, e, m, em }; } var fixedOverlay = ""; const FixedOverlay = defineComponent({ name: "DFixedOverlay", inheritAttrs: false, props: fixedOverlayProps, emits: ["update:modelValue", "click"], setup(props, ctx) { const { modelValue } = toRefs(props); const ns = useNamespace("fixed-overlay"); const { onClick } = useFixedOverlay(props, ctx); return () => createVNode(Transition, { "name": ns.m("fade") }, { default: () => { var _a, _b; return [modelValue.value && createVNode("div", mergeProps({ "class": ns.b() }, ctx.attrs, { "onClick": onClick }), [(_b = (_a = ctx.slots).default) == null ? void 0 : _b.call(_a)])]; } }); } }); const flexibleOverlayProps = { modelValue: { type: Boolean, default: false }, origin: { type: Object, require: true }, position: { type: Array, default: ["bottom"] }, offset: { type: [Number, Object], default: 8 }, shiftOffset: { type: Number }, align: { type: String, default: null }, showArrow: { type: Boolean, default: false }, isArrowCenter: { type: Boolean, default: true }, clickEventBubble: { type: Boolean, default: false }, fitOriginWidth: { type: Boolean, default: false } }; function adjustArrowPosition(isArrowCenter, point, placement, originRect) { let { x, y } = point; if (!isArrowCenter) { const { width, height } = originRect; if (x && placement.includes("start")) { x = 12; } if (x && placement.includes("end")) { x = Math.round(width - 24); } if (y && placement.includes("start")) { y = 10; } if (y && placement.includes("end")) { y = height - 14; } } return { x, y }; } function useOverlay(props, emit) { const { fitOriginWidth, position, showArrow } = toRefs(props); const overlayRef = ref(); const arrowRef = ref(); const overlayWidth = ref(0); let originObserver; const styles = computed(() => { if (fitOriginWidth.value) { return { width: overlayWidth.value + "px" }; } else { return {}; } }); const updateArrowPosition = (arrowEl, placement, point, overlayEl) => { const { x, y } = adjustArrowPosition(props.isArrowCenter, point, placement, overlayEl.getBoundingClientRect()); const staticSide = { top: "bottom", right: "left", bottom: "top", left: "right" }[placement.split("-")[0]]; Object.assign(arrowEl.style, { left: x ? `${x}px` : "", top: y ? `${y}px` : "", right: "", bottom: "", [staticSide]: "-4px" }); }; const updatePosition = async () => { const hostEl = props.origin; const overlayEl = unref(overlayRef.value); const arrowEl = unref(arrowRef.value); const [mainPosition, ...fallbackPosition] = position.value; const middleware = [offset(props.offset)]; middleware.push(fallbackPosition.length ? flip({ fallbackPlacements: fallbackPosition }) : flip()); if (showArrow.value) { middleware.push(arrow({ element: arrowRef.value })); } const { x, y, placement, middlewareData } = await computePosition(hostEl, overlayEl, { strategy: "fixed", placement: mainPosition, middleware }); let applyX = x; let applyY = y; emit("positionChange", placement); Object.assign(overlayEl.style, { top: `${applyY}px`, left: `${applyX}px` }); props.showArrow && updateArrowPosition(arrowEl, placement, middlewareData.arrow, overlayEl); }; const scrollCallback = (e) => { var _a, _b; const scrollElement = e.target; if (scrollElement == null ? void 0 : scrollElement.contains((_b = (_a = props.origin) == null ? void 0 : _a.$el) != null ? _b : props.origin)) { updatePosition(); } }; const updateWidth = (originEl) => { overlayWidth.value = originEl.getBoundingClientRect().width; updatePosition(); }; const observeOrigin = () => { var _a, _b; if (fitOriginWidth.value && typeof window !== "undefined") { const originEl = (_b = (_a = props.origin) == null ? void 0 : _a.$el) != null ? _b : props.origin; if (originEl) { originObserver = new window.ResizeObserver(() => updateWidth(originEl)); originObserver.observe(originEl); } } }; const unobserveOrigin = () => { var _a, _b; const originEl = (_b = (_a = props.origin) == null ? void 0 : _a.$el) != null ? _b : props.origin; originEl && (originObserver == null ? void 0 : originObserver.unobserve(originEl)); }; watch( () => props.modelValue, () => { if (props.modelValue && props.origin) { nextTick(updatePosition); window.addEventListener("scroll", scrollCallback, true); window.addEventListener("resize", updatePosition); observeOrigin(); } else { window.removeEventListener("scroll", scrollCallback, true); window.removeEventListener("resize", updatePosition); unobserveOrigin(); } } ); onUnmounted(() => { window.removeEventListener("scroll", scrollCallback, true); window.removeEventListener("resize", updatePosition); unobserveOrigin(); }); return { arrowRef, overlayRef, styles, updatePosition }; } var flexibleOverlay = ""; const FlexibleOverlay = defineComponent({ name: "DFlexibleOverlay", inheritAttrs: false, props: flexibleOverlayProps, emits: ["update:modelValue", "positionChange"], setup(props, { slots, attrs, emit, expose }) { const ns = useNamespace("flexible-overlay"); const { clickEventBubble } = toRefs(props); const { arrowRef, overlayRef, styles, updatePosition } = useOverlay(props, emit); expose({ updatePosition }); return () => { var _a; return props.modelValue && createVNode("div", mergeProps({ "ref": overlayRef, "class": ns.b(), "style": styles.value }, attrs, { "onClick": withModifiers(() => ({}), [clickEventBubble.value ? "" : "stop"]), "onPointerup": withModifiers(() => ({}), ["stop"]) }), [(_a = slots.default) == null ? void 0 : _a.call(slots), props.showArrow && createVNode("div", { "ref": arrowRef, "class": ns.e("arrow") }, null)]); }; } }); const inBrowser = typeof window !== "undefined"; var index = { title: "Overlay \u906E\u7F69\u5C42", category: "\u901A\u7528", status: "100%", install(app) { app.component(FixedOverlay.name, FixedOverlay); app.component(FlexibleOverlay.name, FlexibleOverlay); if (inBrowser && !document.getElementById("d-overlay-anchor")) { const overlayAnchor = document.createElement("div"); overlayAnchor.setAttribute("id", "d-overlay-anchor"); overlayAnchor.style.position = "fixed"; overlayAnchor.style.left = "0"; overlayAnchor.style.top = "0"; overlayAnchor.style.zIndex = "1000"; document.body.appendChild(overlayAnchor); } } }; export { FixedOverlay, FlexibleOverlay, index as default, fixedOverlayProps, flexibleOverlayProps };