UNPKG

@coreui/vue

Version:

UI Components Library for Vue.js

268 lines (265 loc) 9.51 kB
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