Great chapter, looking forward to the next one.
Is this plot different from the original?
Bookmarked.
<script setup lang="ts">
import { ref } from 'vue'
import { Button, Inline, Popconfirm, Stack, Text } from '@hina-ui/vue'
const comments = ref([
'Great chapter, looking forward to the next one.',
'Is this plot different from the original?',
'Bookmarked.',
])
function remove(index: number) {
comments.value.splice(index, 1)
}
</script>
<template>
<Stack gap="sm" align="stretch" class="w-96">
<Inline
v-for="(comment, index) in comments"
:key="comment"
align="center"
justify="between"
gap="sm"
>
<Text size="sm">{{ comment }}</Text>
<Popconfirm
title="Delete this comment?"
description="This cannot be undone."
tone="danger"
confirm-text="Delete"
@confirm="remove(index)"
>
<Button size="sm" variant="ghost" tone="neutral">Delete</Button>
</Popconfirm>
</Inline>
<Text v-if="!comments.length" tone="muted" size="sm">No comments left.</Text>
</Stack>
</template>
Usage
import { Popconfirm } from '@hina-ui/vue'
The popconfirm asks one question right next to its trigger, which suits small, local confirmations such as deleting a row or undoing an action; a weighty confirmation that has to interrupt the current task belongs to an AlertDialog. title is the question, description spells out the consequence, and the pair of buttons at the bottom takes cancelText and confirmText for its text. The default slot is the trigger.
<script setup lang="ts">
import { ref } from 'vue'
import { Button, Popconfirm, Stack, Text } from '@hina-ui/vue'
const done = ref(false)
</script>
<template>
<Stack gap="sm" align="start">
<Popconfirm title="Mark every notification as read?" @confirm="done = true">
<Button variant="outline" tone="neutral">Mark all as read</Button>
</Popconfirm>
<Text v-if="done" tone="muted" size="sm">Everything was marked as read.</Text>
</Stack>
</template>
Examples
Destructive actions
tone="danger" gives the confirm button the danger color for irreversible actions such as deleting.
<script setup lang="ts">
import { Button, Popconfirm } from '@hina-ui/vue'
</script>
<template>
<Popconfirm
title="Remove this member?"
description="They will lose access to this project."
tone="danger"
confirm-text="Remove"
>
<Button variant="outline" tone="danger">Remove member</Button>
</Popconfirm>
</template>
Asynchronous confirm
When the confirm handler returns a Promise the bubble waits for it before closing: the confirm button shows a loading indicator, and Cancel, Esc and clicking outside are unavailable meanwhile. If the handler throws, the bubble stays open.
<script setup lang="ts">
import { ref } from 'vue'
import { Button, Popconfirm, Stack, Switch, Text } from '@hina-ui/vue'
const fail = ref(false)
const status = ref('')
async function retry() {
status.value = ''
await new Promise(resolve => setTimeout(resolve, 1000))
if (fail.value) {
status.value = 'Sending failed, please try again later.'
throw new Error('retry failed')
}
status.value = 'Sent again.'
}
</script>
<template>
<Stack gap="md" align="start">
<Switch v-model="fail">Simulate a failure</Switch>
<Popconfirm title="Send this email again?" confirm-text="Resend" @confirm="retry">
<Button variant="outline" tone="neutral">Resend</Button>
<template #content>
<Text v-if="status" tone="muted" size="sm">{{ status }}</Text>
</template>
</Popconfirm>
</Stack>
</template>
Placement
side and align work as in Popover; the default is right below the trigger, flipping to the other side when space runs out.
<script setup lang="ts">
import { Button, Inline, Popconfirm } from '@hina-ui/vue'
const sides = ['top', 'right', 'bottom', 'left'] as const
const names = { top: 'Top', right: 'Right', bottom: 'Bottom', left: 'Left' }
</script>
<template>
<Inline gap="sm">
<Popconfirm v-for="side in sides" :key="side" title="Confirm this placement?" :side="side">
<Button variant="outline" tone="neutral">{{ names[side] }}</Button>
</Popconfirm>
</Inline>
</template>
Controlled
open supports two-way binding, so the bubble can be opened or closed from outside.
<script setup lang="ts">
import { ref } from 'vue'
import { Button, Inline, Popconfirm } from '@hina-ui/vue'
const open = ref(false)
</script>
<template>
<Inline gap="sm">
<Button variant="outline" tone="neutral" @click="open = true">Open from outside</Button>
<Popconfirm
v-model:open="open"
title="Discard the draft?"
description="Everything in the draft is thrown away."
>
<Button variant="outline" tone="neutral">Discard draft</Button>
</Popconfirm>
</Inline>
</template>
Behavior
- Focus lands on the Cancel button when the bubble opens, so an accidental Enter does nothing; focus returns to the trigger on close.
- Esc and clicking outside only close the bubble and do not fire
cancel; only the Cancel button does. - The page stops scrolling while it is open, and the trigger keeps its pressed look.
- When the confirm handler returns a Promise the bubble stays busy until it settles and then closes; a thrown error keeps it open.
Accessibility
- The panel is
role="dialog", named after its trigger, with the question and description linked together througharia-describedby. - The panel carries
aria-busywhile busy. - Button text comes from the current locale and can be replaced.
API
Props
| Prop | Type | Default | Description |
|---|---|---|---|
title | string | — | Required. The question |
description | string | — | Supporting text |
confirmText | string | locale | Text of the confirm button |
cancelText | string | locale | Text of the cancel button |
tone | 'accent' | 'danger' | 'accent' | Tone of the confirm button |
side | 'top' | 'right' | 'bottom' | 'left' | 'bottom' | Side the bubble floats out on |
align | 'start' | 'center' | 'end' | 'center' | Alignment against the trigger |
sideOffset | number | 8 | Distance from the trigger in pixels |
open | boolean | — | Whether it is open, supports two-way binding |
class | string | — | Classes appended to the panel |
Slots
| Slot | Description |
|---|---|
| default | The trigger |
content | Content between the description and buttons |
Events
| Event | Payload | Description |
|---|---|---|
confirm | — | Confirm was clicked; a Promise from the handler is awaited |
cancel | — | Cancel was clicked |