Vitral 0.2
Overlay

Dialog

A window over the page, modal or not, that takes focus in, keeps it inside and returns it when it closes.

Import

main.ts
import { Dialog } from '@vitral/vue';

Basic

The header names the dialog; the footer is the button band.

<script setup lang="ts">import { Button, Dialog, InputText, Label, StackPanel } from '@vitral/vue';import { ref } from 'vue'; const visible = ref(false);const name = ref('Ana Souza');const email = ref('ana@example.com');</script> <template>    <Button label="Edit profile" icon="pencil" @click="visible = true" />    <Dialog v-model:visible="visible" header="Edit profile" style="width: 28rem">        <StackPanel spacing="0.875rem">            <span style="font-size: 0.875rem; color: var(--vt-text-muted-color)">Update how others see you.</span>            <StackPanel spacing="0.375rem">                <Label for="dlg-name">Name</Label>                <InputText id="dlg-name" v-model="name" fluid />            </StackPanel>            <StackPanel spacing="0.375rem">                <Label for="dlg-email">Email</Label>                <InputText id="dlg-email" v-model="email" type="email" fluid />            </StackPanel>        </StackPanel>        <template #footer>            <Button label="Cancel" severity="secondary" @click="visible = false" />            <Button label="Save" @click="visible = false" />        </template>    </Dialog></template>

Position

The dialog sits in the centre, along an edge or in a corner, and slides in from there.

<script setup lang="ts">import { Button, Dialog } from '@vitral/vue';import { ref } from 'vue'; type Position = 'center' | 'top' | 'bottom' | 'left' | 'right' | 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right'; const positions: Position[] = ['top-left', 'top', 'top-right', 'left', 'center', 'right', 'bottom-left', 'bottom', 'bottom-right'];const visible = ref(false);const position = ref<Position>('top'); function openAt(where: Position) {    position.value = where;    visible.value = true;}</script> <template>    <div style="display: grid; grid-template-columns: repeat(3, 8rem); gap: 0.5rem">        <Button v-for="p in positions" :key="p" :label="p" severity="secondary" size="small" @click="openAt(p)" />    </div>    <Dialog v-model:visible="visible" :header="`Position: ${position}`" :position="position" style="width: 22rem" dismissable-mask>        A press on the mask closes this one (<code>dismissable-mask</code>).    </Dialog></template>

Maximizable

A button fills the viewport with the dialog; its name switches between Maximize and Restore.

<script setup lang="ts">import { Button, Dialog } from '@vitral/vue';import { ref } from 'vue'; const visible = ref(false);</script> <template>    <Button label="Terms of service" severity="secondary" @click="visible = true" />    <Dialog v-model:visible="visible" header="Terms of service" maximizable style="width: 36rem; height: 24rem">        <p v-for="n in 8" :key="n">            {{ n }}. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer posuere erat a ante venenatis dapibus posuere velit aliquet. Donec ullamcorper nulla non metus            auctor fringilla, maecenas faucibus mollis interdum.        </p>        <template #footer>            <Button label="Decline" severity="secondary" @click="visible = false" />            <Button label="Accept" autofocus @click="visible = false" />        </template>    </Dialog></template>

Modeless

`modal: false` keeps the page usable: no mask, no focus trap, no scroll lock. Escape still closes it.

<script setup lang="ts">import { Button, Dialog, InputText } from '@vitral/vue';import { ref } from 'vue'; const visible = ref(false);</script> <template>    <Button label="Open a modeless dialog" severity="secondary" @click="visible = true" />    <Dialog v-model:visible="visible" header="Find" :modal="false" position="bottom-right" style="width: 20rem">        <InputText aria-label="Find text" placeholder="Search the page…" fluid />    </Dialog></template>

Overlays inside

Open the select, then press Escape: the select closes first, the dialog stays.

<script setup lang="ts">import { Button, Dialog, Label, Select, StackPanel } from '@vitral/vue';import { ref } from 'vue'; const visible = ref(false);const role = ref<string | null>(null);const roles = [{ name: 'Viewer' }, { name: 'Editor' }, { name: 'Owner' }];</script> <template>    <Button label="Invite someone" severity="secondary" icon="user" @click="visible = true" />    <Dialog v-model:visible="visible" header="Invite" style="width: 24rem">        <StackPanel spacing="0.375rem">            <Label for="dlg-role">Role</Label>            <Select id="dlg-role" v-model="role" :options="roles" option-label="name" option-value="name" placeholder="Choose a role" fluid />        </StackPanel>        <template #footer>            <Button label="Send invite" :disabled="!role" @click="visible = false" />        </template>    </Dialog></template>

API

Read from packages/vue/src/components/Dialog/types.ts, so it says what the component actually accepts.

Props

NameTypeDescription
headerstringThe title; it names the dialog. Without one, name it with `aria-label` or `aria-labelledby`.
modalbooleanDim and block the page, trap focus and lock scrolling. Defaults to true.
closablebooleanShow the close button. Defaults to true.
dismissableMaskbooleanClose on a press on the mask.
closeOnEscapebooleanDefaults to true.
positionDialogPosition—
maximizablebooleanAdd a button that fills the viewport with the dialog.
blockScrollbooleanLock the page's scroll even when the dialog is not modal.
showHeaderbooleanDefaults to true.
appendTostring`'body'` (the default), `'self'` to render in place, or a selector.

Plus pt, dt and unstyled from BaseProps, see pass-through and unstyled mode.

Emits

EventPayloadDescription
show——
hide——
after-hide—After the closing animation, once the dialog has left the page.
maximize——
unmaximize——

Slots

NameSlot propsDescription
default——
header—Replaces the title text; it still names the dialog.
footer——
closeicon——
maximizeicon(props: { maximized: boolean })—