Pages

Rename item

Closing with unsaved input asks for confirmation.

Close it with Escape or the close button.

Workspace

Inbox, projects and archived notes.

Backlinks

3 notes link to this note.

Skip to content

Dialog ​

Dialogs shown on top of the page: a general modal, a confirmation dialog, and a drawer that opens from the edge of the screen.

UiModal ​

A modal dialog shown on top of the page. While it is open, the rest of the page cannot be used, keeping attention on the dialog.

Code ​

vue
<script setup lang="ts">
import { shallowRef } from 'vue'
import { UiButton, UiModal } from '@wing-land/ui'
const open = shallowRef(false)
const draft = shallowRef('')
const legacy = shallowRef<{ open: () => void; close: () => void } | null>(null)
function confirmDiscard() {
  return draft.value === '' || window.confirm('Discard your changes?')
}
</script>
<template>
  <div class="demo">
    <UiButton @click="open = true">Open with v-model</UiButton>
    <UiButton @click="legacy?.open()">Open with ref</UiButton>
    <UiModal
      v-model:open="open"
      title="Rename item"
      description="Closing with unsaved input asks for confirmation."
      :before-close="confirmDiscard"
    >
      <input v-model="draft" aria-label="New name" />
      <template #footer>
        <UiButton variant="ghost" @click="open = false">Cancel</UiButton>
        <UiButton variant="solid" @click="((draft = ''), (open = false))">Save</UiButton>
      </template>
    </UiModal>
    <UiModal ref="legacy" label="Example">
      <p>Close it with Escape or the close button.</p>
      <UiButton @click="legacy?.close()">Close</UiButton>
    </UiModal>
  </div>
</template>
<style scoped>
.demo {
  display: flex;
  gap: 8px;
  padding: 1rem 0;
}
</style>

Usage ​

Open and close it with v-model:open or the exposed open() and close() methods. To ask whether unsaved input can be discarded, return false from beforeClose to keep it open. When dismissible is false, neither Escape, the backdrop, nor the close button closes it, and the close button is hidden.

Accessibility ​

Renders a native <dialog> element opened with showModal(), so focus and screen readers cannot reach anything outside it. While open, it stops the page behind it from scrolling, and when closed, it returns focus to the element that was focused before. title or label becomes its accessible name. With nested modals, scrolling resumes when the last one closes.

API ​

Props

PropDefaultType
label

Accessible name of the dialog when there is no title.

—string
title

Heading. Becomes the accessible name of the dialog.

—string
description

Description below the heading. Linked with aria-describedby.

—string
size

Width. small is 440px, the default is 640px, and large is up to 1200px.

'default''default' | 'small' | 'large'
placement

Position. start and end turn it into a full-height panel that opens from the edge of the screen. Usually use UiDrawer instead.

'center''end' | 'start' | 'center'
role

Use alertdialog when a response is required, such as a confirmation.

'dialog''dialog' | 'alertdialog'
closeLabel

Accessible name of the close button. Defaults to the locale text.

—string
dismissible

When false, user actions do not close the dialog.

trueboolean
beforeClose

Called when the user tries to close the dialog. Return false to keep it open. reason is escape, backdrop, or close-button.

—(reason: UiModalCloseReason) => boolean | Promise<boolean>
openv-model:open

Whether the dialog is open.

falseboolean

Events

EventPayload
close

Emitted when the dialog closes.

—
open

Emitted when the dialog opens.

—
update:open

Emitted when the v-model:open value changes.

(value: boolean)

Slots

SlotSlot props
header

Content placed where the heading goes. Use it instead of title.

—
default

Body.

—
footer

Actions placed at the bottom right.

—

Exposed

NameType
close

Closes the dialog without calling beforeClose.

() => void
open

Opens the dialog.

() => void

UiConfirmDialog ​

A small dialog that asks for confirmation before an action that cannot be undone.

Code ​

vue
<script setup lang="ts">
import { shallowRef } from 'vue'
import { UiButton, UiConfirmDialog, useConfirm } from '@wing-land/ui'
const { dialog, confirm } = useConfirm()
const result = shallowRef('')
async function remove() {
  const ok = await confirm({
    title: 'Delete this item?',
    description: 'Deleted items cannot be restored.',
    confirmLabel: 'Delete',
    tone: 'danger',
  })
  result.value = ok ? 'Deleted' : 'Canceled'
}
</script>
<template>
  <div class="demo">
    <UiButton tone="danger" @click="remove">Delete item</UiButton>
    <p role="status">{{ result }}</p>
    <UiConfirmDialog v-bind="dialog" />
  </div>
</template>
<style scoped>
.demo {
  padding: 1rem 0;
}
</style>

Usage ​

The confirm button only emits confirm; closing the dialog is up to your app. While an async action runs, set loading to true to wait for it without the dialog closing.

useConfirm ​

useConfirm() lets you use one dialog placed in the same component as a Promise.

ts
const { dialog, confirm } = useConfirm()
// Template: <UiConfirmDialog v-bind="dialog" />
if (await confirm({ title: 'Delete this item?', confirmLabel: 'Delete', tone: 'danger' })) remove()

Closing with the cancel button, Escape, or the close button resolves to false.

Accessibility ​

Renders a UiModal with role="alertdialog" and title as its accessible name. To prevent accidental confirmation, initial focus goes to the cancel button.

API ​

Props

PropDefaultType
titleRequired

The question. Becomes the accessible name of the dialog.

—string
description

Extra explanation.

—string
confirmLabel

Text of the confirm button. Defaults to the locale text.

—string
cancelLabel

Text of the cancel button. Defaults to the locale text.

—string
tone

With danger, the confirm button uses the destructive color.

'neutral''danger' | 'neutral'
loading

Puts the confirm button in a busy state and prevents closing.

falseboolean
openv-model:open

Whether the dialog is open.

falseboolean

Events

EventPayload
update:open

Emitted when the v-model:open value changes.

(value: boolean)
confirm

Emitted when the confirm button is pressed.

—
cancel

Emitted when the dialog closes any other way (cancel button, Escape, close button, or backdrop).

—

Slots

SlotSlot props
default

Content added below the description.

—

UiDrawer ​

A panel that opens from the left or right edge of the screen. Use it for navigation on narrow screens or to show details.

Code ​

vue
<script setup lang="ts">
import { shallowRef } from 'vue'
import { UiButton, UiDrawer } from '@wing-land/ui'
const navigation = shallowRef(false)
const backlinks = shallowRef(false)
</script>
<template>
  <div class="demo">
    <UiButton icon="menu" @click="navigation = true">Navigation</UiButton>
    <UiButton @click="backlinks = true">Backlinks</UiButton>
    <UiDrawer v-model:open="navigation" title="Workspace">
      <p>Inbox, projects and archived notes.</p>
    </UiDrawer>
    <UiDrawer v-model:open="backlinks" side="end" title="Backlinks" size="large">
      <p>3 notes link to this note.</p>
    </UiDrawer>
  </div>
</template>
<style scoped>
.demo {
  display: flex;
  gap: 8px;
  padding: 1rem 0;
}
</style>

Usage ​

Choose the side with side and the width with size. Opening and closing, beforeClose, and dismissible work the same as UiModal.

Accessibility ​

Uses the same native <dialog> element as UiModal, and handles focus, background scrolling, and returning focus after closing the same way. title or label becomes its accessible name.

API ​

Props

PropDefaultType
side

Side to open from.

'start''end' | 'start'
label

Accessible name when there is no title.

—string
title

Heading.

—string
description

Description below the heading.

—string
size

Width. small is 280px, the default is 360px, and large is 480px.

'default''default' | 'small' | 'large'
closeLabel

Accessible name of the close button. Defaults to the locale text.

—string
dismissible

When false, user actions do not close the drawer.

trueboolean
beforeClose

Called when the user tries to close the drawer. Return false to keep it open.

—(reason: UiModalCloseReason) => boolean | Promise<boolean>
openv-model:open

Whether the drawer is open.

falseboolean

Events

EventPayload
close

Emitted when the drawer closes.

—
open

Emitted when the drawer opens.

—
update:open

Emitted when the v-model:open value changes.

(value: boolean)

Slots

SlotSlot props
default

Body.

any
header

Content placed where the heading goes.

any
footer

Actions placed at the bottom.

any

Exposed

NameType
close

Closes the drawer without calling beforeClose.

() => void | undefined
open

Opens the drawer.

() => void