UNPKG

naive-ui

Version:

A Vue 3 Component Library. Fairly Complete, Theme Customizable, Uses TypeScript, Fast

256 lines 8.17 kB
import { Transition, cloneVNode, computed, defineComponent, h, inject, mergeProps, nextTick, provide, ref, toRef, vShow, watch, withDirectives } from 'vue'; import { clickoutside } from 'vdirs'; import { VFocusTrap } from 'vueuc'; import { dialogPropKeys } from "../../dialog/src/dialogProps.mjs"; import { NDialog } from "../../dialog/src/Dialog.mjs"; import { cardBasePropKeys } from "../../card/src/Card.mjs"; import { drawerBodyInjectionKey } from "../../drawer/src/interface.mjs"; import { popoverBodyInjectionKey } from "../../popover/src/interface.mjs"; import { NScrollbar } from "../../_internal/index.mjs"; import { NCard } from "../../card/index.mjs"; import { getFirstSlotVNode, keep, useLockHtmlScroll, warn } from "../../_utils/index.mjs"; import { modalBodyInjectionKey, modalInjectionKey } from "./interface.mjs"; import { presetProps } from "./presetProps.mjs"; export default defineComponent({ name: 'ModalBody', inheritAttrs: false, props: Object.assign(Object.assign({ show: { type: Boolean, required: true }, preset: String, displayDirective: { type: String, required: true }, trapFocus: { type: Boolean, default: true }, autoFocus: { type: Boolean, default: true }, blockScroll: Boolean }, presetProps), { renderMask: Function, // events onClickoutside: Function, onBeforeLeave: { type: Function, required: true }, onAfterLeave: { type: Function, required: true }, onPositiveClick: { type: Function, required: true }, onNegativeClick: { type: Function, required: true }, onClose: { type: Function, required: true }, onAfterEnter: Function, onEsc: Function }), setup(props) { const bodyRef = ref(null); const scrollbarRef = ref(null); const displayedRef = ref(props.show); const transformOriginXRef = ref(null); const transformOriginYRef = ref(null); watch(toRef(props, 'show'), value => { if (value) displayedRef.value = true; }); useLockHtmlScroll(computed(() => props.blockScroll && displayedRef.value)); const NModal = inject(modalInjectionKey); function styleTransformOrigin() { if (NModal.transformOriginRef.value === 'center') { return ''; } const { value: transformOriginX } = transformOriginXRef; const { value: transformOriginY } = transformOriginYRef; if (transformOriginX === null || transformOriginY === null) { return ''; } else if (scrollbarRef.value) { const scrollTop = scrollbarRef.value.containerScrollTop; return `${transformOriginX}px ${transformOriginY + scrollTop}px`; } return ''; } function syncTransformOrigin(el) { if (NModal.transformOriginRef.value === 'center') { return; } const mousePosition = NModal.getMousePosition(); if (!mousePosition) { return; } if (!scrollbarRef.value) return; const scrollTop = scrollbarRef.value.containerScrollTop; const { offsetLeft, offsetTop } = el; if (mousePosition) { const top = mousePosition.y; const left = mousePosition.x; transformOriginXRef.value = -(offsetLeft - left); transformOriginYRef.value = -(offsetTop - top - scrollTop); } el.style.transformOrigin = styleTransformOrigin(); } function handleEnter(el) { void nextTick(() => { syncTransformOrigin(el); }); } function handleBeforeLeave(el) { el.style.transformOrigin = styleTransformOrigin(); props.onBeforeLeave(); } function handleAfterLeave() { displayedRef.value = false; transformOriginXRef.value = null; transformOriginYRef.value = null; props.onAfterLeave(); } function handleCloseClick() { const { onClose } = props; if (onClose) { onClose(); } } function handleNegativeClick() { props.onNegativeClick(); } function handlePositiveClick() { props.onPositiveClick(); } const childNodeRef = ref(null); watch(childNodeRef, node => { if (node) { void nextTick(() => { const el = node.el; if (el && bodyRef.value !== el) { bodyRef.value = el; } }); } }); provide(modalBodyInjectionKey, bodyRef); provide(drawerBodyInjectionKey, null); provide(popoverBodyInjectionKey, null); return { mergedTheme: NModal.mergedThemeRef, appear: NModal.appearRef, isMounted: NModal.isMountedRef, mergedClsPrefix: NModal.mergedClsPrefixRef, bodyRef, scrollbarRef, displayed: displayedRef, childNodeRef, handlePositiveClick, handleNegativeClick, handleCloseClick, handleAfterLeave, handleBeforeLeave, handleEnter }; }, render() { const { $slots, $attrs, handleEnter, handleAfterLeave, handleBeforeLeave, preset, mergedClsPrefix } = this; let childNode = null; if (!preset) { childNode = getFirstSlotVNode($slots); if (!childNode) { warn('modal', 'default slot is empty'); return; } childNode = cloneVNode(childNode); childNode.props = mergeProps({ class: `${mergedClsPrefix}-modal` }, $attrs, childNode.props || {}); } return this.displayDirective === 'show' || this.displayed || this.show ? withDirectives(h("div", { role: "none", class: `${mergedClsPrefix}-modal-body-wrapper` }, h(NScrollbar, { ref: "scrollbarRef", theme: this.mergedTheme.peers.Scrollbar, themeOverrides: this.mergedTheme.peerOverrides.Scrollbar, contentClass: `${mergedClsPrefix}-modal-scroll-content` }, { default: () => { var _a; return [(_a = this.renderMask) === null || _a === void 0 ? void 0 : _a.call(this), h(VFocusTrap, { disabled: !this.trapFocus, active: this.show, onEsc: this.onEsc, autoFocus: this.autoFocus }, { default: () => { var _a; return h(Transition, { name: "fade-in-scale-up-transition", appear: (_a = this.appear) !== null && _a !== void 0 ? _a : this.isMounted, onEnter: handleEnter, onAfterEnter: this.onAfterEnter, onAfterLeave: handleAfterLeave, onBeforeLeave: handleBeforeLeave }, { default: () => { const dirs = [[vShow, this.show]]; const { onClickoutside } = this; if (onClickoutside) { dirs.push([clickoutside, this.onClickoutside, undefined, { capture: true }]); } return withDirectives(this.preset === 'confirm' || this.preset === 'dialog' ? h(NDialog, Object.assign({}, this.$attrs, { class: [`${mergedClsPrefix}-modal`, this.$attrs.class], ref: "bodyRef", theme: this.mergedTheme.peers.Dialog, themeOverrides: this.mergedTheme.peerOverrides.Dialog }, keep(this.$props, dialogPropKeys), { "aria-modal": "true" }), $slots) : this.preset === 'card' ? h(NCard, Object.assign({}, this.$attrs, { ref: "bodyRef", class: [`${mergedClsPrefix}-modal`, this.$attrs.class], theme: this.mergedTheme.peers.Card, themeOverrides: this.mergedTheme.peerOverrides.Card }, keep(this.$props, cardBasePropKeys), { "aria-modal": "true", role: "dialog" }), $slots) : this.childNodeRef = childNode, dirs); } }); } })]; } })), [[vShow, this.displayDirective === 'if' || this.displayed || this.show]]) : null; } });