Alert Dialog
A dialog requiring a response.
Anatomy
<script setup>
import { AlertDialog } from '@shardsui/vue/alert-dialog'
</script>
<template>
<AlertDialog.Root>
<AlertDialog.Trigger />
<AlertDialog.Portal>
<AlertDialog.Backdrop />
<AlertDialog.Viewport>
<AlertDialog.Popup>
<AlertDialog.Title />
<AlertDialog.Description />
<AlertDialog.Close />
</AlertDialog.Popup>
</AlertDialog.Viewport>
</AlertDialog.Portal>
</AlertDialog.Root>
</template>Examples
Open from a menu
To open an alert dialog from a menu, keep the alert dialog controlled and flip its state from the menu item's @click handler.
<script setup>
import { AlertDialog } from '@shardsui/vue/alert-dialog'
import { Menu } from '@shardsui/vue/menu'
import { shallowRef } from 'vue'
const dialogOpen = shallowRef(false)
</script>
<template>
<Menu.Root>
<Menu.Trigger>Open menu</Menu.Trigger>
<Menu.Portal>
<Menu.Positioner>
<Menu.Popup>
<!-- Open the dialog when the menu item is clicked -->
<Menu.Item @click="dialogOpen = true">Open dialog</Menu.Item>
</Menu.Popup>
</Menu.Positioner>
</Menu.Portal>
</Menu.Root>
<!-- Control the dialog state -->
<AlertDialog.Root v-model:open="dialogOpen">
<AlertDialog.Portal>
<AlertDialog.Backdrop />
<AlertDialog.Popup>
<!-- Rest of the dialog -->
</AlertDialog.Popup>
</AlertDialog.Portal>
</AlertDialog.Root>
</template>Close confirmation
A nested confirmation dialog guards against losing work: it opens when the text typed into the parent dialog is about to be discarded.
Veto the close by controlling open and deciding in the @update:open handler. When a close is requested the handler runs; if you don't commit the new value, the prop keeps the old one and the dialog stays open. Open the confirmation there instead, so the prompt appears whether the user presses Esc or hits a close button. An alert dialog is never dismissed by clicking the backdrop.
<template>
<AlertDialog.Root
:open="open"
@update:open="
(next) => {
if (!next && hasUnsavedChanges) return // veto: don't commit, dialog stays open
open = next
}
"
>
...
</AlertDialog.Root>
</template>Style the parent dialog through the [data-nested-dialog-open] selector and the var(--nested-dialogs) CSS variable. Child dialogs render their own backdrop, marked with data-nested. Hide it with [data-nested] { opacity: 0 } to keep the parent visible behind the one on top.
The demo below uses Dialog. The same pattern applies to AlertDialog.
Detached triggers
<AlertDialog.Trigger> normally sits inside the root. When the trigger and the alert dialog's content can't share a spot in the markup, render <AlertDialog.Trigger> wherever it fits and connect it to the root with a shared handle from AlertDialog.createHandle().
The handle's imperative methods — open(), openWithPayload() and close() — only take effect while an <AlertDialog.Root> using the same handle is mounted. Calls made before a root mounts or after it unmounts are ignored, not queued: each mount starts from fresh state.
<!-- [!code word::handle="h"] -->
<script setup>
const h = AlertDialog.createHandle()
</script>
<template>
<AlertDialog.Trigger :handle="h">Open</AlertDialog.Trigger>
<AlertDialog.Root :handle="h">...</AlertDialog.Root>
</template>Multiple triggers
Several triggers can open the same alert dialog. Share one handle across detached triggers, or drop multiple <AlertDialog.Trigger> components inside a single <AlertDialog.Root>.
<template>
<AlertDialog.Root>
<AlertDialog.Trigger>Trigger 1</AlertDialog.Trigger>
<AlertDialog.Trigger>Trigger 2</AlertDialog.Trigger>
...
</AlertDialog.Root>
</template><script setup>
const h = AlertDialog.createHandle()
</script>
<template>
<AlertDialog.Trigger :handle="h">Trigger 1</AlertDialog.Trigger>
<AlertDialog.Trigger :handle="h">Trigger 2</AlertDialog.Trigger>
<AlertDialog.Root :handle="h">...</AlertDialog.Root>
</template>To show different content depending on which trigger opened the alert dialog, pass a payload to each <AlertDialog.Trigger> and read it through the default slot on <AlertDialog.Root>. Give AlertDialog.createHandle() a type argument to type the payload:
<script setup lang="ts">
const h = AlertDialog.createHandle<{ message: string }>()
</script>
<template>
<AlertDialog.Trigger :handle="h" :payload="{ message: 'Trigger 1' }"
>Trigger 1</AlertDialog.Trigger
>
<AlertDialog.Trigger :handle="h" :payload="{ message: 'Trigger 2' }"
>Trigger 2</AlertDialog.Trigger
>
<AlertDialog.Root v-slot="{ payload }" :handle="h">
<AlertDialog.Portal>
<AlertDialog.Popup>
<AlertDialog.Title>Alert dialog</AlertDialog.Title>
<AlertDialog.Description v-if="payload !== undefined">
Confirming {{ payload.message }}
</AlertDialog.Description>
</AlertDialog.Popup>
</AlertDialog.Portal>
</AlertDialog.Root>
</template>Controlled mode with multiple triggers
When the alert dialog's visibility depends on your app's state, drive it with v-model:open on <AlertDialog.Root>. With multiple triggers, give each <AlertDialog.Trigger> an id and add v-model:trigger-id to <AlertDialog.Root>: each trigger publishes its own id when it opens the dialog, and setting triggerId yourself associates the dialog with that trigger.
API reference
Root
Groups all parts of the alert dialog. Doesn't render its own HTML element.
Trigger
A button that opens the alert dialog.
Renders a <button> element.
Portal
A portal that moves the popup out to <body>, clear of ancestor clipping and stacking.
Renders a <div> element.
Backdrop
An overlay displayed beneath the popup.
Renders a <div> element.
Viewport
A positioning container for the dialog popup that can be made scrollable.
Renders a <div> element.
Popup
A container for the dialog contents.
Renders a <div> element.
Title
A heading that labels the alert dialog.
Renders an <h2> element.
Description
A paragraph with additional information about the alert dialog.
Renders a <p> element.
Close
A button that closes the dialog.
Renders a <button> element.
Handle
Connects an <AlertDialog.Root> with detached <AlertDialog.Trigger> components, and controls the alert dialog imperatively. Pass a type argument to type the payload.
const alertDialog = AlertDialog.createHandle<Payload>()