Modal Confirmation¶
Implementing confirmation dialogs for destructive actions.
Problem¶
You need to ask the user for confirmation before performing a destructive action like deleting data.
Solution¶
Use state to control modal visibility and render a centered dialog box.
Complete Example¶
This is a complete, runnable example:
import { createApp, ui, rgb } from "@rezi-ui/core";
import { createNodeBackend } from "@rezi-ui/node";
type State = {
items: Array<{ id: string; name: string }>;
confirmForId: string | null;
};
const app = createApp<State>({
backend: createNodeBackend(),
initialState: {
items: [
{ id: "1", name: "Alpha" },
{ id: "2", name: "Beta" },
{ id: "3", name: "Gamma" },
],
confirmForId: null,
},
});
function requestDelete(itemId: string) {
app.update((s) => ({ ...s, confirmForId: itemId }));
}
function cancelDelete() {
app.update((s) => ({ ...s, confirmForId: null }));
}
app.view((state) => {
const confirmId = state.confirmForId;
const confirmItem = confirmId ? state.items.find((i) => i.id === confirmId) : null;
return ui.layers([
ui.column({ gap: 1, p: 1 }, [
ui.text("Items", { style: { bold: true } }),
ui.divider(),
...state.items.map((item) =>
ui.row({ key: item.id, gap: 2, justify: "between" }, [
ui.text(item.name),
ui.button({
id: `delete-${item.id}`,
label: "Delete",
onPress: () => requestDelete(item.id),
style: { fg: rgb(255, 110, 110) },
}),
])
),
]),
confirmItem &&
ui.modal({
id: "confirm-delete",
title: "Confirm delete",
content: ui.column({ gap: 1 }, [
ui.text(`Delete "${confirmItem.name}"?`),
ui.text("This action cannot be undone.", { style: { dim: true } }),
]),
actions: [
ui.button({ id: "cancel", label: "Cancel", onPress: cancelDelete }),
ui.button({
id: "confirm",
label: "Delete",
onPress: () =>
app.update((s) => ({
...s,
items: s.items.filter((it) => it.id !== s.confirmForId),
confirmForId: null,
})),
style: { fg: rgb(255, 110, 110), bold: true },
}),
],
onClose: cancelDelete,
returnFocusTo: confirmId ? `delete-${confirmId}` : undefined,
}),
]);
});
app.keys({ q: () => app.stop(), "ctrl+c": () => app.stop(), escape: () => cancelDelete() });
await app.start();
Explanation¶
- The modal is rendered conditionally based on
confirmForId. ui.layers([...])keeps the modal on top while preserving the main content underneath.returnFocusTorestores focus back to the button that opened the modal.