naive-ui
Version:
A Vue 3 Component Library. Fairly Complete, Theme Customizable, Uses TypeScript, Fast
256 lines • 8.17 kB
JavaScript
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;
}
});