Skip to content

Dialog

Versatile Dialog component for displaying modals (centered) or drawers (side, top/bottom, full screen). It offers complete accessibility management, focus trap, and customization options to adapt to various use cases.

Usage examples

Centered Modal (display="center")

Confirmation Modal

vue
<script setup>
import { ref } from 'vue'
import { SuDialog, SuButton } from '@surgeui/ds-vue'

const showCenterModal = ref(false)
</script>

<template>
  <SuButton @click="showCenterModal = true">Open Modal</SuButton>
  <SuDialog v-model:modelValue="showCenterModal" display="center" title="Confirmation" description="Do you really want to delete this item?" width="400px">
    <template #default>
      <p>This action is irreversible. Please confirm your choice.</p>
    </template>
    <template #footer>
      <SuButton variant="secondary" @click="showCenterModal = false">Cancel</SuButton>
      <SuButton variant="primary" @click="showCenterModal = false">Confirm</SuButton>
    </template>
  </SuDialog>
</template>

Left Side Drawer (display="left")

Side Menu

vue
<script setup>
import { ref } from 'vue'
import { SuDialog, SuButton, SuLinkGroup, SuLink } from '@surgeui/ds-vue'

const showLeftDrawer = ref(false)
</script>

<template>
  <SuButton @click="showLeftDrawer = true">Open Left Drawer</SuButton>
  <SuDialog v-model:modelValue="showLeftDrawer" display="left" title="Navigation Menu" width="300px">
    <template #default>
      <SuLinkGroup direction="vertical" gap="md">
        <SuLink href="/dashboard">Dashboard</SuLink>
        <SuLink href="/profile">My Profile</SuLink>
        <SuLink href="/settings">Settings</SuLink>
        <SuLink href="/logout">Logout</SuLink>
      </SuLinkGroup>
    </template>
    <template #footer>
      <SuButton variant="outline" @click="showLeftDrawer = false">Close</SuButton>
    </template>
  </SuDialog>
</template>

Right Side Drawer (display="right")

Product Details Panel

vue
<script setup>
import { ref } from 'vue'
import { SuDialog, SuButton } from '@surgeui/ds-vue'

const showRightDrawer = ref(false)
</script>

<template>
  <SuButton @click="showRightDrawer = true">Open Right Drawer</SuButton>
  <SuDialog v-model:modelValue="showRightDrawer" display="right" title="Product Details" width="450px">
    <template #default>
      <h3>Product X</h3>
      <p>Detailed description of Product X. Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>
      <img src="https://via.placeholder.com/300x200" alt="Product X" style="max-width: 100%; height: auto; margin-top: 1rem;">
    </template>
    <template #footer>
      <SuButton variant="primary" @click="showRightDrawer = false">Add to Cart</SuButton>
    </template>
  </SuDialog>
</template>

Top Drawer (display="top")

Notification Bar

vue
<script setup>
import { ref } from 'vue'
import { SuDialog, SuButton } from '@surgeui/ds-vue'

const showTopDrawer = ref(false)
</script>

<template>
  <SuButton @click="showTopDrawer = true">Open Top Drawer</SuButton>
  <SuDialog v-model:modelValue="showTopDrawer" display="top" title="New Notification" height="100px">
    <template #default>
      <p>You have 3 new unread messages!</p>
    </template>
    <template #footer>
      <SuButton variant="primary" @click="showTopDrawer = false">View Messages</SuButton>
    </template>
  </SuDialog>
</template>

Bottom Drawer (display="bottom")

Quick Actions

vue
<script setup>
import { ref } from 'vue'
import { SuDialog, SuButton, SuButtonGroup } from '@surgeui/ds-vue'

const showBottomDrawer = ref(false)
</script>

<template>
  <SuButton @click="showBottomDrawer = true">Open Bottom Drawer</SuButton>
  <SuDialog v-model:modelValue="showBottomDrawer" display="bottom" title="Quick Actions" height="250px">
    <template #default>
      <SuButtonGroup direction="vertical" gap="md">
        <SuButton variant="primary">Take Photo</SuButton>
        <SuButton variant="secondary">Upload File</SuButton>
        <SuButton variant="outline">Share</SuButton>
      </SuButtonGroup>
    </template>
    <template #footer>
      <SuButton variant="ghost" @click="showBottomDrawer = false">Close</SuButton>
    </template>
  </SuDialog>
</template>

Full Screen (display="full")

Complex Form

vue
<script setup>
import { ref } from 'vue'
import { SuDialog, SuButton, SuFormFields, SuInput, SuPassword, SuTextarea, SuFileUpload } from '@surgeui/ds-vue'

const showFullScreen = ref(false)
</script>

<template>
  <SuButton @click="showFullScreen = true">Open Full Screen</SuButton>
  <SuDialog v-model:modelValue="showFullScreen" display="full" title="Complete Registration Form">
    <template #default>
      <SuFormFields gap="lg" sectionGap="xl">
        <SuInput label="Full Name" placeholder="Your name" required />
        <SuInput type="email" label="Email" placeholder="your@example.com" required />
        <SuPassword label="Password" showProgress required />
        <SuTextarea label="Bio" placeholder="Tell us about yourself..." autoResize maxLength="500" showCounter />
        <SuFileUpload label="Profile Picture" accept="image/*" />
      </SuFormFields>
    </template>
    <template #footer>
      <SuButton variant="secondary" @click="showFullScreen = false">Cancel</SuButton>
      <SuButton variant="primary">Submit</SuButton>
    </template>
  </SuDialog>
</template>

API

Props

PropTypeDefaultDescription
modelValuebooleanfalseControls the open/close state of the Dialog (used with v-model)
display'center' | 'left' | 'right' | 'top' | 'bottom' | 'full''center'Display mode of the Dialog
titlestringundefinedTitle of the Dialog (displayed in the head slot by default and for aria-labelledby)
descriptionstringundefinedDescription of the Dialog (used for aria-describedby if title is not provided or if the head slot is used without a title)
closeOnOverlayClickbooleantrueAllows closing the Dialog by clicking on the overlay
closeOnEscapebooleantrueAllows closing the Dialog with the Escape key
widthstringautoCustom width of the Dialog (e.g., '500px', '80%')
heightstringautoCustom height of the Dialog (e.g., '400px', '90vh')
zIndexnumber1000Z-index of the Dialog and its overlay
disableScrollbooleantrueDisables page body scrolling when the Dialog is open

Accessibility Attributes

PropTypeDefaultDescription
ariaLabelstringundefinedAccessible label for the Dialog
ariaLabelledBystringundefinedID of the element that labels the Dialog (takes precedence over title)
ariaDescribedBystringundefinedID of the element that describes the Dialog (takes precedence over description)

Events

EventTypeDescription
update:modelValue(value: boolean) => voidEmitted when the open/close state changes
open() => voidEmitted when the Dialog is opened
close() => voidEmitted when the Dialog is closed

Slots

SlotDescription
headContent for the Dialog header (can contain a title, close button, etc.)
defaultMain content of the Dialog
footerContent for the Dialog footer (often used for action buttons)

Accessibility

The Dialog component is designed to comply with WCAG 2.1 AA standards:

✅ Accessibility Features

  • ARIA Role: role="dialog" and aria-modal="true" to indicate to screen readers that it is a modal window.
  • Focus Management: Implements a "focus trap" to ensure that focus remains within the Dialog when open, preventing the user from accidentally navigating outside.
  • Initial Focus: Focus is automatically moved to the first focusable element within the Dialog upon opening.
  • Focus Restoration: Focus is restored to the element that triggered the Dialog's opening after it closes.
  • Labels and Descriptions: Uses aria-labelledby (via the title prop or ariaLabelledBy) and aria-describedby (via the description prop or ariaDescribedBy) to provide semantic context to screen readers.
  • Accessible Closing: The Dialog can be closed with the Escape key (if closeOnEscape is true).
  • Scroll Disablement: Page body scrolling is disabled when the Dialog is open to prevent unwanted interactions with underlying content.
  • Smooth Transitions: Animations are designed to be smooth and respect prefers-reduced-motion.

🎯 Best Practices

  • Always provide a title or ariaLabel: This is crucial for accessibility so that screen reader users understand the purpose of the Dialog.
  • Place primary actions in the footer: This helps with UI consistency and predictability.
  • Avoid nested Dialogs: If possible, design your UI to avoid opening one Dialog on top of another, as this can complicate focus management and user experience.

Keyboard Navigation

KeyAction
TabNavigate between focusable elements inside the Dialog
Shift + TabNavigate between focusable elements in reverse order
EscapeClose the Dialog (if closeOnEscape is true)

Styling and Customization

The Dialog component uses CSS transitions for smooth opening and closing. The display prop applies specific styles to position the Dialog correctly.

  • display="center": Centered modal, with a scale animation.
  • display="left" / display="right": Side drawers, with a horizontal translation animation.
  • display="top" / display="bottom": Vertical drawers, with a vertical translation animation.
  • display="full": Full screen, with a slight scale animation.

You can customize the width (width) and height (height) for center, left, right, top, bottom modes. For full mode, the Dialog always occupies 100% of the width and height.

Publié sous licence MIT.