vue-devui
Version:
DevUI components based on Vite and Vue3
307 lines (306 loc) • 9.16 kB
JavaScript
import { watch, onUnmounted, defineComponent, toRefs, createVNode, Transition, mergeProps, ref, unref, nextTick, withModifiers } from "vue";
import { offset, autoPlacement, arrow, shift, 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
}
};
function getScrollParent(element) {
const overflowRegex = /(auto|scroll|hidden)/;
for (let parent = element; parent = parent.parentElement; parent.parentElement !== document.body) {
const style = window.getComputedStyle(parent);
if (overflowRegex.test(style.overflow + style.overflowX + style.overflowY)) {
return parent;
}
}
return window;
}
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 overlayRef = ref();
const arrowRef = ref();
let originParent = null;
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 middleware = [
offset(props.offset),
autoPlacement({
alignment: props.align,
allowedPlacements: props.position
})
];
props.showArrow && middleware.push(arrow({ element: arrowEl }));
props.shiftOffset !== void 0 && middleware.push(shift());
if (!overlayEl) {
return;
}
const { x, y, placement, middlewareData } = await computePosition(hostEl, overlayEl, {
strategy: "fixed",
middleware
});
let applyX = x;
let applyY = y;
if (props.shiftOffset !== void 0) {
const { x: shiftX, y: shiftY } = middlewareData.shift;
shiftX < 0 && (applyX -= props.shiftOffset);
shiftX > 0 && (applyX += props.shiftOffset);
shiftY < 0 && (applyY -= props.shiftOffset);
shiftY > 0 && (applyY += props.shiftOffset);
}
emit("positionChange", placement);
Object.assign(overlayEl.style, { top: `${applyY}px`, left: `${applyX}px` });
props.showArrow && updateArrowPosition(arrowEl, placement, middlewareData.arrow, overlayEl);
};
watch(() => props.modelValue, () => {
if (props.modelValue && props.origin) {
originParent = getScrollParent(props.origin);
nextTick(updatePosition);
originParent == null ? void 0 : originParent.addEventListener("scroll", updatePosition);
originParent !== window && window.addEventListener("scroll", updatePosition);
window.addEventListener("resize", updatePosition);
} else {
originParent == null ? void 0 : originParent.removeEventListener("scroll", updatePosition);
originParent !== window && window.removeEventListener("scroll", updatePosition);
window.removeEventListener("resize", updatePosition);
}
});
onUnmounted(() => {
originParent == null ? void 0 : originParent.removeEventListener("scroll", updatePosition);
originParent !== window && window.removeEventListener("scroll", updatePosition);
window.removeEventListener("resize", updatePosition);
});
return { arrowRef, overlayRef, 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,
updatePosition
} = useOverlay(props, emit);
expose({
updatePosition
});
return () => {
var _a;
return props.modelValue && createVNode("div", mergeProps({
"ref": overlayRef,
"class": ns.b()
}, 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 };