Confirm Dialog
An imperative confirmation prompt — await a boolean, no JSX wiring, in React (@bpdm/ui) and Angular (@bpdm/ng).
The Confirm Dialog asks a yes/no question imperatively — no JSX to wire up per
call. Mount the provider once, then await confirm({ … }) anywhere and branch on
the boolean it resolves to (true on confirm; false on cancel, outside-click,
or Esc). It's built on the Dialog. In Angular it's the
injectable BpdmConfirm service.
Setup
React — mount ConfirmProvider once near your app root; that's the only setup.
Angular has nothing to mount: BpdmConfirm is providedIn: 'root', so you just
inject it where you need it (shown in Usage below).
import { ConfirmProvider } from '@bpdm/ui/confirm-dialog';
export function App() {
return (
<ConfirmProvider>
<YourApp />
</ConfirmProvider>
);
}Usage
Call it and await the result. Pass confirmText / cancelText to relabel the
buttons.
import { useConfirm } from '@bpdm/ui/confirm-dialog';
import { Button } from '@bpdm/ui/button';
export function PublishButton() {
const confirm = useConfirm();
async function onPublish() {
const ok = await confirm({
title: 'Publish changes?',
description: 'This will make your edits live for everyone in the workspace.',
});
if (ok) {
// publish the changes here
}
}
return <Button onClick={onPublish}>Publish changes</Button>;
}import { Component, inject } from '@angular/core';
import { BpdmConfirm } from '@bpdm/ng';
import { BpdmButton } from '@bpdm/ng';
@Component({
selector: 'publish-button',
imports: [BpdmButton],
template: `<button bpdmButton (click)="onPublish()">Publish changes</button>`,
})
export class PublishButton {
private readonly confirm = inject(BpdmConfirm);
async onPublish() {
const ok = await this.confirm.confirm({
title: 'Publish changes?',
description: 'This will make your edits live for everyone in the workspace.',
});
if (ok) {
// publish the changes here
}
}
}Destructive
Set destructive: true to colour the confirm button red — for deletes and other
irreversible actions. Pair it with a clear confirmText.
import { useConfirm } from '@bpdm/ui/confirm-dialog';
import { Button } from '@bpdm/ui/button';
export function DeleteButton() {
const confirm = useConfirm();
async function onDelete() {
const ok = await confirm({
title: 'Delete project?',
description: 'This permanently deletes the project and all its data. This cannot be undone.',
confirmText: 'Delete',
destructive: true,
});
if (ok) {
// delete the project here
}
}
return <Button variant="destructive" onClick={onDelete}>Delete project</Button>;
}import { Component, inject } from '@angular/core';
import { BpdmConfirm } from '@bpdm/ng';
import { BpdmButton } from '@bpdm/ng';
@Component({
selector: 'delete-button',
imports: [BpdmButton],
template: `<button bpdmButton variant="destructive" (click)="onDelete()">Delete project</button>`,
})
export class DeleteButton {
private readonly confirm = inject(BpdmConfirm);
async onDelete() {
const ok = await this.confirm.confirm({
title: 'Delete project?',
description: 'This permanently deletes the project and all its data. This cannot be undone.',
confirmText: 'Delete',
destructive: true,
});
if (ok) {
// delete the project here
}
}
}Internationalization
The confirm dialog carries only a little copy of its own — the fallback title, the
Confirm / Cancel button labels, and the close button's accessible name. Set those
once for the whole app, and any per-call title / confirmText / cancelText
still overrides at the call site:
- React — pass
messagestoConfirmProvider(<ConfirmProvider messages={{ … }}>). It's aPartial<ConfirmMessages>merged over the English defaults, so override one key or all four. - Angular — register
provideBpdmConfirmMessages({ … })in your app providers (it configures theBPDM_CONFIRM_MESSAGESinjection token).
Being built on the Dialog, it mirrors under dir="rtl" with
no extra prop — the close button and footer buttons flip to the inline-start side.
The example below is German (de-DE) — localised app-wide messages plus a
translated, destructive per-call prompt.
import { ConfirmProvider, useConfirm } from '@bpdm/ui/confirm-dialog';
import { Button } from '@bpdm/ui/button';
// Set the app-wide defaults once, near the root.
export function App() {
return (
<ConfirmProvider
messages={{
title: 'Sind Sie sicher?',
confirm: 'Bestätigen',
cancel: 'Abbrechen',
close: 'Schließen',
}}
>
<ProjektLoeschen />
</ConfirmProvider>
);
}
function ProjektLoeschen() {
const confirm = useConfirm();
async function onDelete() {
const ok = await confirm({
title: 'Projekt löschen?',
description: 'Dadurch werden das Projekt und alle zugehörigen Daten dauerhaft gelöscht.',
destructive: true,
confirmText: 'Löschen',
});
if (ok) {
// Projekt hier löschen
}
}
return (
<Button variant="destructive" onClick={onDelete}>
Projekt löschen
</Button>
);
}import { bootstrapApplication } from '@angular/platform-browser';
import { provideBpdmConfirmMessages } from '@bpdm/ng';
import { App } from './app';
bootstrapApplication(App, {
providers: [
provideBpdmConfirmMessages({
title: 'Sind Sie sicher?',
confirm: 'Bestätigen',
cancel: 'Abbrechen',
close: 'Schließen',
}),
],
});import { Component, inject } from '@angular/core';
import { BpdmConfirm, BpdmButton } from '@bpdm/ng';
@Component({
selector: 'projekt-loeschen',
imports: [BpdmButton],
template: `<button bpdmButton variant="destructive" (click)="onDelete()">Projekt löschen</button>`,
})
export class ProjektLoeschen {
private readonly confirm = inject(BpdmConfirm);
async onDelete() {
const ok = await this.confirm.confirm({
title: 'Projekt löschen?',
description: 'Dadurch werden das Projekt und alle zugehörigen Daten dauerhaft gelöscht.',
destructive: true,
confirmText: 'Löschen',
});
if (ok) {
// Projekt hier löschen
}
}
}API
confirm(options) (React useConfirm() / Angular BpdmConfirm.confirm()) returns
Promise<boolean>.
| Option | Type | Default | Description |
|---|---|---|---|
title | ReactNode / string | Are you sure? | Heading. |
description | ReactNode / string | — | Supporting text. |
confirmText | string | Confirm | Confirm button label. |
cancelText | string | Cancel | Cancel button label. |
destructive | boolean | false | Style the confirm button as destructive (red). |
App-wide copy — messages
Configure the default copy once (see Internationalization).
Per-call title / confirmText / cancelText always take precedence over these.
- React — the
messagesprop onConfirmProvider(Partial<ConfirmMessages>). - Angular —
provideBpdmConfirmMessages(Partial<ConfirmMessages>)in the app providers, backed by theBPDM_CONFIRM_MESSAGESinjection token.
ConfirmMessages key | Default | Description |
|---|---|---|
title | Are you sure? | Fallback title when a call omits title. |
confirm | Confirm | Confirm button label (per-call confirmText overrides). |
cancel | Cancel | Cancel button label (per-call cancelText overrides). |
close | Close | Accessible name (aria-label) for the dialog's close button. |
Accessibility
- It's a modal built on the Dialog —
role="dialog"+aria-modal="true", with a focus trap and scroll lock while it's open. - The
titleis the dialog's accessible name (aria-labelledby) and thedescription, when set, is linked viaaria-describedby. - The Confirm and Cancel buttons are labelled by their text (
confirmText/cancelText, or themessagesdefaults); the close button carries an accessible name viamessages.close. - Cancel, outside-click, and
Escall resolve the promise tofalse, so any dismissal is never treated as a confirmation. - Focus moves into the dialog on open and returns to the element that was focused before it opened (the trigger) on close.
- All copy — title, confirm, cancel, and the close label — is translatable via
messages, and everything mirrors correctly underdir="rtl".