vue-devui
Version:
DevUI components based on Vite and Vue3
331 lines (330 loc) • 9.6 kB
JavaScript
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 };