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.