Modal
The VModal component is used to create modal or dialog. It is based on @headlessui/vue dialog component.
Usage
Basic Usage
To use the VModal component, just use it in the template.
INFO
The VModal component is registered globally when you install with @morpheme/ui. So you don't need to import it manually.
Confirm
Use confirm prop to make modal confirmable. You can also customize the confirm text with confirmText prop, color with confirmColor prop and even more with confirmProps.
Hide Header
Use hide-header prop to hide the modal header.
Hide Footer
Use hide-footer prop to hide the modal footer.
Hide X Button
Use hide-x-button prop to hide the modal close button.
Fullscreen
Use fullscreen prop to make modal fullscreen.
Centered
Use centered prop to make the modal centered of the page.
Loading
Use loading prop to set modal loading state. When loading is true, modal can not be closed.
Persistent
Use persistent prop to prevent closing modal when clicking the overlay.
Custom Width
Use width prop set modal width and use max-width prop to set modal max width.
Custom Class
We can also customize modal via classes props. This is useful when working with Tailwind CSS.
modal-class: To set class on modal panelheader-class: To set class on modal headertitle-class: To set class on modal title onlybody-class: To set class on modal bodyfooter-class: To set class on modal footer
Custom Style
We can also customize modal via CSS Properties.
Declarative Modal
Starting from version v1.0.0-beta.11, we have introduced a new declarative approach to use the modal component, providing you with enhanced flexibility. This approach involves breaking down the modal component into five separate components, each serving a specific purpose. To utilize this feature, you will need to manually import these components.
Component List
The modal components are as follows:
ModalModalHeaderModalTitleModalBodyModalFooter
Usage
Unlike before, these components are not auto-imported. Therefore, you must import them individually based on your requirements.
Here's a brief overview of each modal component and its purpose:
Modal: This is the main component that serves as the container for the entire modal dialog.ModalHeader: Use this component to display the header section of the modal. Typically, it contains the title and close button.ModalTitle: This component is used to display the title of the modal. It is usually placed inside theModalHeader.ModalBody: This component allows you to add content to the body of the modal. Any information or forms you wish to present should be placed within this component.ModalFooter: The footer component is utilized to include any additional elements at the bottom of the modal, such as action buttons (e.g., Save, Cancel).
Example
Props
| Name | Type | Default |
|---|---|---|
title | string | 'Modal Header' |
confirm | boolean | false |
confirmColor | string | 'primary' |
confirmProps | object | '{}' |
confirmText | string | Confirm |
closeText | string | Close |
closeProps | object | '{}' |
headerClass | string | '' |
titleClass | string | '' |
bodyClass | string | '' |
modalClass | string | '' |
footerClass | string | '' |
footerClass | string | '' |
hideHeader | boolean | false |
hideFooter | boolean | false |
Events
onConfirm
Event for confirmation modal.
<script setup lang="ts">
import {ref} from 'vue';
const isOpen = ref(false);
const onConfirm = async (e) => {
await approveRequest();
e.close();
};
const approveRequest = () => {
return new Promise((resolve: (value?: any) => void) => {
setTimeout(() => {
resolve();
}, 2000);
});
};
</script>
<template>
<VModal v-model="isOpen" confirm @confirm="onConfirm">
<template #activator="{open}">
<v-btn @click="open" color="error">Approve</v-btn>
</template>
<p>Are you sure want to approve this request?</p>
</VModal>
</template><script setup lang="ts">
import {ref} from 'vue';
const isOpen = ref(false);
const onConfirm = async (e) => {
await approveRequest();
e.close();
};
const approveRequest = () => {
return new Promise((resolve: (value?: any) => void) => {
setTimeout(() => {
resolve();
}, 2000);
});
};
</script>
<template>
<VModal v-model="isOpen" confirm @confirm="onConfirm">
<template #activator="{open}">
<v-btn @click="open" color="error">Approve</v-btn>
</template>
<p>Are you sure want to approve this request?</p>
</VModal>
</template>Slots
default
The default slot is used to place modal content/body. You can put your text or even HTML tags on it.
<template>
<VModal>
<p>Modal message</p>
</VModal>
</template><template>
<VModal>
<p>Modal message</p>
</VModal>
</template>activator
The activator slot is used to place modal activator. You can place button that trigger modal to open here.
Slot Props:
{
open: () => void
}{
open: () => void
}Example:
<template>
<VModal>
<template #activator="{open}">
<VBtn @click="open">Open Modal</VBtn>
</template>
<p>Modal message</p>
</VModal>
</template><template>
<VModal>
<template #activator="{open}">
<VBtn @click="open">Open Modal</VBtn>
</template>
<p>Modal message</p>
</VModal>
</template>header
The header slot is used to place modal header. Use this slot to customize modal header content.
<template>
<VModal>
<template #header>
<p>Custom Header</p>
</template>
<p>Modal message</p>
</VModal>
</template><template>
<VModal>
<template #header>
<p>Custom Header</p>
</template>
<p>Modal message</p>
</VModal>
</template>footer
The footer slot is used to place modal footer. Use this slot to customize modal footer content.
Slot Props:
{
loading: boolean
confirmProps: Record<string, any>
onConfirm: ((payload: ConfirmEventPayload) => any) & (() => void)
loading: boolean
close: () => void
}{
loading: boolean
confirmProps: Record<string, any>
onConfirm: ((payload: ConfirmEventPayload) => any) & (() => void)
loading: boolean
close: () => void
}Example:
<template>
<VModal>
<template #footer="{close}">
<VBtn>View Link</VBtn>
<VBtn @click="close">Close</VBtn>
</template>
<p>Modal message</p>
</VModal>
</template><template>
<VModal>
<template #footer="{close}">
<VBtn>View Link</VBtn>
<VBtn @click="close">Close</VBtn>
</template>
<p>Modal message</p>
</VModal>
</template>CSS Variables
:root {
--v-modal-text-color: var(--color-gray-700);
--v-modal-bg-color: var(--color-white);
--v-modal-border-radius: 12px;
--v-modal-z-index: 30;
--v-modal-shadow: var(--effect-shadow-xl);
--v-modal-width: 25rem;
--v-modal-max-width: 100%;
/* content */
--v-modal-content-padding-x: var(--size-spacing-4);
--v-modal-content-padding-y: var(--size-spacing-4);
--v-modal-content-text-align: center;
/* panel */
--v-modal-panel-padding-x: var(--size-spacing-4);
--v-modal-panel-padding-y: var(--size-spacing-4);
--v-modal-margin-padding-x: var(--size-spacing-4);
--v-modal-margin-padding-y: var(--size-spacing-4);
--v-modal-content-text-align: center;
/* title */
--v-modal-title-font-weight: var(--font-weight-semibold);
--v-modal-title-font-size: var(--size-font-lg);
--v-modal-title-line-height: var(--size-font-xl);
--v-modal-title-letter-spacing: initial;
--v-modal-title-color: var(--color-gray-900);
/* body */
--v-modal-body-margin-top: var(--size-spacing-2);
--v-modal-body-text-align: left;
--v-modal-body-font-size: var(--size-font-sm);
--v-modal-body-line-height: var(--size-font-md);
/* footer */
--v-modal-footer-margin-top: var(--size-spacing-6);
--v-modal-footer-justify-content: flex-end;
--v-modal-footer-gap: var(--size-spacing-2);
// overlay
--v-modal-overlay-blur-size: 8px;
}:root {
--v-modal-text-color: var(--color-gray-700);
--v-modal-bg-color: var(--color-white);
--v-modal-border-radius: 12px;
--v-modal-z-index: 30;
--v-modal-shadow: var(--effect-shadow-xl);
--v-modal-width: 25rem;
--v-modal-max-width: 100%;
/* content */
--v-modal-content-padding-x: var(--size-spacing-4);
--v-modal-content-padding-y: var(--size-spacing-4);
--v-modal-content-text-align: center;
/* panel */
--v-modal-panel-padding-x: var(--size-spacing-4);
--v-modal-panel-padding-y: var(--size-spacing-4);
--v-modal-margin-padding-x: var(--size-spacing-4);
--v-modal-margin-padding-y: var(--size-spacing-4);
--v-modal-content-text-align: center;
/* title */
--v-modal-title-font-weight: var(--font-weight-semibold);
--v-modal-title-font-size: var(--size-font-lg);
--v-modal-title-line-height: var(--size-font-xl);
--v-modal-title-letter-spacing: initial;
--v-modal-title-color: var(--color-gray-900);
/* body */
--v-modal-body-margin-top: var(--size-spacing-2);
--v-modal-body-text-align: left;
--v-modal-body-font-size: var(--size-font-sm);
--v-modal-body-line-height: var(--size-font-md);
/* footer */
--v-modal-footer-margin-top: var(--size-spacing-6);
--v-modal-footer-justify-content: flex-end;
--v-modal-footer-gap: var(--size-spacing-2);
// overlay
--v-modal-overlay-blur-size: 8px;
}Standalone Installation
You can also install the Modal component individually via @morpheme/modal package:
npm i @morpheme/modalnpm i @morpheme/modal<script setup lang="ts">
import VModal from '@morpheme/modal';
</script>
<template>
<VModal v-model="isOpen">
<template #activator="{open}">
<v-btn @click="open">Click Me</v-btn>
</template>
Hello World
</VModal>
</template><script setup lang="ts">
import VModal from '@morpheme/modal';
</script>
<template>
<VModal v-model="isOpen">
<template #activator="{open}">
<v-btn @click="open">Click Me</v-btn>
</template>
Hello World
</VModal>
</template>Storybook
View Storybook documentation here.
Morpheme