UNPKG

vue-devui

Version:

DevUI components based on Vite and Vue3

269 lines (268 loc) 7.21 kB
import { defineComponent, createVNode, Transition, ref, computed, watch, onUnmounted, Teleport, mergeProps, reactive, createApp, isVNode } from "vue"; import { onClickOutside } from "@vueuse/core"; const drawerProps = { modelValue: { type: Boolean, default: false }, zIndex: { type: Number, default: 1040 }, showOverlay: { type: Boolean, default: true }, escKeyCloseable: { type: Boolean, default: true }, position: { type: String, default: "right" }, lockScroll: { type: Boolean, default: true }, closeOnClickOverlay: { type: Boolean, default: true }, beforeClose: { type: Function } }; const drawerOverlayProps = { visible: { type: Boolean, default: false }, onClick: { type: Function } }; 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 drawerOverlay = ""; var DrawerOverlay = defineComponent({ name: "DDrawerOverlay", props: drawerOverlayProps, emits: ["click"], setup(props, ctx) { const ns = useNamespace("drawer"); const handleClick = (e) => { ctx.emit("click", e); }; return () => createVNode(Transition, { "name": "drawer-overlay-fade" }, { default: () => [props.visible && createVNode("div", { "class": ns.e("overlay"), "onClick": handleClick }, null)] }); } }); 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 useDrawer(props, emit) { const ns = useNamespace("drawer"); const modalNs = useNamespace("modal", true); const drawerRef = ref(); const overlayRef = ref(); const drawerClasses = computed(() => ({ [ns.b()]: true, [ns.m(props.position)]: true })); const close = () => { emit("update:modelValue", false); emit("close"); }; let lockScrollCb; const execClose = () => { props.beforeClose ? props.beforeClose(close) : close(); }; const handleOverlayClick = () => { props.closeOnClickOverlay && execClose(); }; const handleEscClose = (e) => { e.code === "Escape" && execClose(); }; const handleClickOutside = (e) => { const composedPath = e.composedPath(); const modalOverlay = document.querySelectorAll(modalNs.e("overlay")); const modal = document.querySelectorAll(modalNs.b()); const isClickModalOverlay = Array.from(modalOverlay).filter((item) => composedPath.includes(item)); const isClickModal = Array.from(modal).filter((item) => composedPath.includes(item)); if (isClickModalOverlay.length || isClickModal.length) { return; } execClose(); }; setTimeout(() => { onClickOutside(drawerRef, handleClickOutside, { capture: false, ignore: [overlayRef] }); }); const removeBodyAdditions = () => { lockScrollCb == null ? void 0 : lockScrollCb(); document.removeEventListener("keyup", handleEscClose); }; watch( () => props.modelValue, (val) => { if (val) { emit("open"); props.lockScroll && (lockScrollCb = lockScroll()); props.escKeyCloseable && document.addEventListener("keyup", handleEscClose); } else { removeBodyAdditions(); } } ); onUnmounted(removeBodyAdditions); return { overlayRef, drawerRef, drawerClasses, handleOverlayClick }; } var drawer = ""; var Drawer = defineComponent({ name: "DDrawer", inheritAttrs: false, props: drawerProps, emits: ["close", "update:modelValue", "open"], setup(props, { emit, slots, attrs }) { const { overlayRef, drawerRef, drawerClasses, handleOverlayClick } = useDrawer(props, emit); return () => createVNode(Teleport, { "to": "body" }, { default: () => [props.showOverlay && createVNode(DrawerOverlay, { "ref": overlayRef, "visible": props.modelValue, "style": { zIndex: props.zIndex - 1 }, "onClick": handleOverlayClick }, null), createVNode(Transition, { "name": `drawer-fly-${props.position}` }, { default: () => { var _a; return [props.modelValue && createVNode("div", mergeProps({ "ref": drawerRef, "class": drawerClasses.value, "style": { zIndex: props.zIndex } }, attrs), [(_a = slots.default) == null ? void 0 : _a.call(slots)])]; } })] }); } }); function _isSlot(s) { return typeof s === "function" || Object.prototype.toString.call(s) === "[object Object]" && !isVNode(s); } const defaultOptions = { modelValue: false, content: "", zIndex: 1e3, showOverlay: true, escKeyCloseable: true, position: "right", lockScroll: true, closeOnClickOverlay: true }; function initInstance(state) { const container = document.createElement("div"); const content = state.content; delete state.content; const app = createApp({ setup() { const handleVisibleChange = () => { state.modelValue = false; }; onUnmounted(() => { document.body.removeChild(container); }); return () => createVNode(Drawer, mergeProps(state, { "onUpdate:modelValue": handleVisibleChange }), _isSlot(content) ? content : { default: () => [content] }); } }); document.body.appendChild(container); app.mount(container); return app; } class DrawerService { open(options) { const state = reactive({ ...defaultOptions, ...options }); const app = initInstance(state); state.modelValue = true; return { close: () => { state.modelValue = false; app.unmount(); } }; } } var index = { title: "Drawer \u62BD\u5C49\u677F", category: "\u53CD\u9988", status: "75%", install(app) { app.component(Drawer.name, Drawer); app.config.globalProperties.$drawerService = new DrawerService(); } }; export { Drawer, DrawerService, index as default };