Edit an object
Keep a longer form in a scrolling body with stationary title and actions.
Components / Overlays
A native top-layer modal with a stationary header and footer and a scrollable body.
TEAM PROFILE
Ana Martins
Product designer
CAMPAIGN WORKSPACE
Bring the launch plan, assets, and tasks together in one focused view.
npx @getweini/cli@latest add dialogKeep a longer form in a scrolling body with stationary title and actions.
Open a modal view when the user needs to focus on one record.
<script setup lang="ts">
import { ref, watch } from 'vue'
import {
Button,
Checkbox,
Dialog,
DialogBody,
DialogClose,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
Field,
FieldLabel,
Input,
} from './components'
const editOpen = ref(false)
const workspaceOpen = ref(false)
const name = ref('Ana Martins')
const role = ref('Product designer')
const draftName = ref(name.value)
const draftRole = ref(role.value)
const tasks = ref([
{ id: 'copy', label: 'Review final copy', done: true },
{ id: 'assets', label: 'Approve campaign assets', done: false },
])
const draftTasks = ref(tasks.value.map((task) => ({ ...task })))
watch(editOpen, (open) => {
if (!open) return
draftName.value = name.value
draftRole.value = role.value
})
watch(workspaceOpen, (open) => {
if (open) draftTasks.value = tasks.value.map((task) => ({ ...task }))
})
function saveProfile() {
name.value = draftName.value.trim()
role.value = draftRole.value.trim()
editOpen.value = false
}
function saveWorkspace() {
tasks.value = draftTasks.value.map((task) => ({ ...task }))
workspaceOpen.value = false
}
</script>
<template>
<Dialog v-model:open="editOpen">
<DialogTrigger>Edit profile</DialogTrigger>
<DialogContent v-if="editOpen" size="sm" persistent>
<DialogHeader>
<DialogTitle>Edit profile</DialogTitle>
<DialogDescription>Update how your profile appears to teammates.</DialogDescription>
<template #actions><DialogClose aria-label="Close dialog">×</DialogClose></template>
</DialogHeader>
<DialogBody>
<form id="profile-form" class="grid gap-4" @submit.prevent="saveProfile">
<Field required><FieldLabel>Display name</FieldLabel><Input v-model="draftName" /></Field>
<Field required><FieldLabel>Role</FieldLabel><Input v-model="draftRole" /></Field>
</form>
</DialogBody>
<DialogFooter>
<DialogClose>Cancel</DialogClose>
<Button type="submit" form="profile-form">Save changes</Button>
</DialogFooter>
</DialogContent>
</Dialog>
<Dialog v-model:open="workspaceOpen">
<DialogTrigger>Open workspace</DialogTrigger>
<DialogContent v-if="workspaceOpen" fullscreen>
<DialogHeader>
<DialogTitle>Campaign workspace</DialogTitle>
<DialogDescription>Review the launch plan and save your progress.</DialogDescription>
<template #actions>
<DialogClose aria-label="Close fullscreen dialog">×</DialogClose>
</template>
</DialogHeader>
<DialogBody>
<section class="mx-auto max-w-3xl space-y-4 py-6">
<h2 class="text-xl font-semibold">Aurora launch</h2>
<p>Bring the launch plan, assets, and tasks together in one focused view.</p>
<div v-for="task in draftTasks" :key="task.id">
<Checkbox v-model="task.done">{{ task.label }}</Checkbox>
</div>
</section>
</DialogBody>
<DialogFooter>
<DialogClose>Close workspace</DialogClose>
<Button @click="saveWorkspace">Save progress</Button>
</DialogFooter>
</DialogContent>
</Dialog>
</template>The native dialog manages modality and restores trigger focus.
<script setup lang="ts">
import Dialog from './components/ui/dialog/Dialog.vue'
import DialogTrigger from './components/ui/dialog/DialogTrigger.vue'
import DialogContent from './components/ui/dialog/DialogContent.vue'
import DialogHeader from './components/ui/dialog/DialogHeader.vue'
import DialogTitle from './components/ui/dialog/DialogTitle.vue'
import DialogDescription from './components/ui/dialog/DialogDescription.vue'
import DialogBody from './components/ui/dialog/DialogBody.vue'
import DialogFooter from './components/ui/dialog/DialogFooter.vue'
import DialogClose from './components/ui/dialog/DialogClose.vue'
</script>
<template>
<Dialog>
<DialogTrigger>Edit profile</DialogTrigger>
<DialogContent size="lg">
<DialogHeader><DialogTitle>Edit profile</DialogTitle><DialogDescription>Update your public details.</DialogDescription></DialogHeader>
<DialogBody><label>Name <input name="name" /></label></DialogBody>
<DialogFooter><DialogClose>Done</DialogClose></DialogFooter>
</DialogContent>
</Dialog>
</template>Persistent content stays open on backdrop clicks but still responds to Escape.
<script setup lang="ts">
import Dialog from './components/ui/dialog/Dialog.vue'
import DialogTrigger from './components/ui/dialog/DialogTrigger.vue'
import DialogContent from './components/ui/dialog/DialogContent.vue'
import DialogHeader from './components/ui/dialog/DialogHeader.vue'
import DialogTitle from './components/ui/dialog/DialogTitle.vue'
import DialogBody from './components/ui/dialog/DialogBody.vue'
import DialogFooter from './components/ui/dialog/DialogFooter.vue'
import DialogClose from './components/ui/dialog/DialogClose.vue'
</script>
<template>
<Dialog>
<DialogTrigger>Review changes</DialogTrigger>
<DialogContent persistent>
<DialogHeader><DialogTitle>Review changes</DialogTitle></DialogHeader>
<DialogBody><p>Check these changes before you continue.</p></DialogBody>
<DialogFooter><DialogClose>Close review</DialogClose></DialogFooter>
</DialogContent>
</Dialog>
</template>The most useful props, models, slots, events, and methods for this component.
| Name | Type | Default or requirement |
|---|---|---|
v-model:open / defaultOpen | boolean | false |
DialogContent fullscreen | boolean | false |
DialogContent size | sm | md | lg | xl | md |
DialogContent dismissOnBackdrop | boolean | true |
DialogContent persistent | boolean | false |
Use DialogHeader’s #actions slot for an optional close control. Set persistent on DialogContent when backdrop clicks should leave the form open; Escape and close buttons still dismiss it. Fullscreen keeps the header and footer visible while DialogBody scrolls.