@coreui/vue
Version:
UI Components Library for Vue.js
268 lines (265 loc) • 9.51 kB
JavaScript
import { defineComponent, ref, watch, provide, h, Transition, withDirectives, vShow } from 'vue';
import { CBackdrop } from '../backdrop/CBackdrop.js';
import { CConditionalTeleport } from '../conditional-teleport/CConditionalTeleport.js';
import { executeAfterTransition } from '../../utils/transition.js';
const CModal = defineComponent({
name: 'CModal',
inheritAttrs: false,
props: {
/**
* Align the modal in the center or top of the screen.
*
* @values 'top', 'center'
*/
alignment: {
default: 'top',
validator: (value) => {
return ['top', 'center'].includes(value);
},
},
/**
* Apply a backdrop on body while offcanvas is open.
*
* @values boolean | 'static'
*/
backdrop: {
type: [Boolean, String],
default: true,
validator: (value) => {
if (typeof value == 'string') {
return ['static'].includes(value);
}
if (typeof value == 'boolean') {
return true;
}
return false;
},
},
/**
* Appends the vue popover to a specific element. You can pass an HTML element or function that returns a single element. By default `document.body`.
*
* @since 5.3.0
*/
container: {
type: [Object, String],
default: 'body',
},
/**
* A string of all className you want applied to the modal content component.
*/
contentClassName: String,
/**
* Puts the focus on the modal when shown.
*
* @since 5.0.0
*/
focus: {
type: Boolean,
default: true,
},
/**
* Set modal to covers the entire user viewport
*
* @values boolean, 'sm', 'md', 'lg', 'xl', 'xxl'
*/
fullscreen: {
type: [Boolean, String],
validator: (value) => {
if (typeof value == 'string') {
return ['sm', 'md', 'lg', 'xl', 'xxl'].includes(value);
}
if (typeof value == 'boolean') {
return true;
}
return false;
},
},
/**
* Closes the modal when escape key is pressed.
*/
keyboard: {
type: Boolean,
default: true,
},
/**
* Create a scrollable modal that allows scrolling the modal body.
*/
scrollable: Boolean,
/**
* Size the component small, large, or extra large.
*
* @values 'sm', 'lg', 'xl'
*/
size: {
type: String,
validator: (value) => {
return ['sm', 'lg', 'xl'].includes(value);
},
},
/**
* Generates modal using Teleport.
*
* @since 5.3.0
*/
teleport: {
type: Boolean,
default: false,
},
/**
* Remove animation to create modal that simply appear rather than fade in to view.
*/
transition: {
type: Boolean,
default: true,
},
/**
* By default the component is unmounted after close animation, if you want to keep the component mounted set this property to false.
*/
unmountOnClose: {
type: Boolean,
default: true,
},
/**
* Toggle the visibility of alert component.
*/
visible: Boolean,
},
emits: [
/**
* Callback fired when the component requests to be closed.
*/
'close',
/**
* Callback fired when the component requests to be closed.
*/
'close-prevented',
/**
* Callback fired when the modal is shown, its backdrop is static and a click outside the modal or an escape key press is performed with the keyboard option set to false.
*/
'show',
],
setup(props, { slots, attrs, emit }) {
const activeElementRef = ref();
const modalRef = ref();
const modalContentRef = ref();
const visible = ref(props.visible);
watch(() => props.visible, () => {
visible.value = props.visible;
});
const handleEnter = (el, done) => {
activeElementRef.value = document.activeElement;
executeAfterTransition(() => done(), el);
document.body.classList.add('modal-open');
document.body.style.overflow = 'hidden';
document.body.style.paddingRight = '0px';
el.style.display = 'block';
setTimeout(() => {
el.classList.add('show');
}, 1);
emit('show');
};
const handleAfterEnter = () => {
props.focus && modalRef.value?.focus();
window.addEventListener('mousedown', handleMouseDown);
window.addEventListener('keyup', handleKeyUp);
};
// eslint-disable-next-line unicorn/consistent-function-scoping
const handleLeave = (el, done) => {
executeAfterTransition(() => done(), el);
document.body.classList.remove('modal-open');
document.body.style.removeProperty('overflow');
document.body.style.removeProperty('padding-right');
if (document.body.className === '') {
document.body.removeAttribute('class');
}
el.classList.remove('show');
};
const handleAfterLeave = (el) => {
activeElementRef.value?.focus();
window.removeEventListener('mousedown', handleMouseDown);
window.removeEventListener('keyup', handleKeyUp);
el.style.display = 'none';
};
const handleDismiss = () => {
emit('close');
visible.value = false;
};
const handleKeyUp = (event) => {
if (modalContentRef.value && !modalContentRef.value.contains(event.target)) {
if (props.backdrop !== 'static' && event.key === 'Escape' && props.keyboard) {
handleDismiss();
}
if (props.backdrop === 'static') {
modalRef.value.classList.add('modal-static');
emit('close-prevented');
setTimeout(() => {
modalRef.value.classList.remove('modal-static');
}, 300);
}
}
};
const handleMouseDown = (event) => {
window.addEventListener('mouseup', () => handleMouseUp(event), { once: true });
};
const handleMouseUp = (event) => {
if (modalContentRef.value && !modalContentRef.value.contains(event.target)) {
if (props.backdrop !== 'static') {
handleDismiss();
}
if (props.backdrop === 'static') {
modalRef.value.classList.add('modal-static');
setTimeout(() => {
modalRef.value.classList.remove('modal-static');
}, 300);
}
}
};
provide('handleDismiss', handleDismiss);
const modal = () => h('div', {
...attrs,
class: [
'modal',
{
['fade']: props.transition,
},
attrs.class,
],
...(visible.value ? { 'aria-modal': true, role: 'dialog' } : { 'aria-hidden': 'true' }),
ref: modalRef,
}, h('div', {
class: [
'modal-dialog',
{
'modal-dialog-centered': props.alignment === 'center',
[`modal-fullscreen-${props.fullscreen}-down`]: props.fullscreen && typeof props.fullscreen === 'string',
'modal-fullscreen': props.fullscreen && typeof props.fullscreen === 'boolean',
['modal-dialog-scrollable']: props.scrollable,
[`modal-${props.size}`]: props.size,
},
],
}, h('div', { class: ['modal-content', props.contentClassName], ref: modalContentRef }, slots.default && slots.default())));
return () => h(CConditionalTeleport, {
container: props.container,
teleport: props.teleport,
}, {
default: () => [
h(Transition, {
css: false,
onEnter: (el, done) => handleEnter(el, done),
onAfterEnter: () => handleAfterEnter(),
onLeave: (el, done) => handleLeave(el, done),
onAfterLeave: (el) => handleAfterLeave(el),
}, () => props.unmountOnClose
? visible.value && modal()
: withDirectives(modal(), [[vShow, visible.value]])),
props.backdrop &&
h(CBackdrop, {
class: 'modal-backdrop',
visible: visible.value,
}),
],
});
},
});
export { CModal };
//# sourceMappingURL=CModal.js.map