Skip to content

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.

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 panel
  • header-class: To set class on modal header
  • title-class: To set class on modal title only
  • body-class: To set class on modal body
  • footer-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:

  • Modal
  • ModalHeader
  • ModalTitle
  • ModalBody
  • ModalFooter

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 the ModalHeader.
  • 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 ​

NameTypeDefault
titlestring'Modal Header'
confirmbooleanfalse
confirmColorstring'primary'
confirmPropsobject'{}'
confirmTextstringConfirm
closeTextstringClose
closePropsobject'{}'
headerClassstring''
titleClassstring''
bodyClassstring''
modalClassstring''
footerClassstring''
footerClassstring''
hideHeaderbooleanfalse
hideFooterbooleanfalse

Events ​

onConfirm ​

Event for confirmation modal.

vue
<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.

vue
<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:

ts
{
  open: () => void
}
{
  open: () => void
}

Example:

vue
<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>

The header slot is used to place modal header. Use this slot to customize modal header content.

vue
<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>

The footer slot is used to place modal footer. Use this slot to customize modal footer content.

Slot Props:

ts
{
  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:

vue
<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 ​

scss
: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:

bash
npm i @morpheme/modal
npm i @morpheme/modal
vue
<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.