Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 | 51x 362x 362x 362x 138x 138x 138x 362x 10x 10x 10x 10x 362x 2x 2x 2x 362x 2x 2x 2x 362x 362x | import { useState, useRef, useCallback, useEffect } from "react";
import { ConfirmDialog } from "../components/ui/confirm-dialog";
export interface ConfirmOptions {
title: string;
description: string;
confirmLabel?: string;
cancelLabel?: string;
variant?: "default" | "destructive";
}
export type ConfirmFn = (options: ConfirmOptions) => Promise<boolean>;
interface DialogState {
open: boolean;
title: string;
description: string;
confirmLabel: string;
cancelLabel: string;
variant: "default" | "destructive";
}
const CLOSED_STATE: DialogState = {
open: false,
title: "",
description: "",
confirmLabel: "Confirm",
cancelLabel: "Cancel",
variant: "default",
};
/**
* Promise-based confirm dialog hook. Returns a `confirm()` function that opens
* a dialog and resolves to `true` (confirmed) or `false` (cancelled), plus a
* `dialog` element to render in JSX.
*
* Usage:
* ```tsx
* const { confirm, dialog } = useConfirmDialog();
* const handleDelete = async () => {
* if (!(await confirm({ title: "Delete?", description: "Cannot undo." }))) return;
* // proceed with delete
* };
* return <>{dialog}</>;
* ```
*/
export function useConfirmDialog() {
const [state, setState] = useState(CLOSED_STATE);
const resolveRef = useRef<((value: boolean) => void) | null>(null);
// Resolve any pending promise as false on unmount to prevent leaks
useEffect(() => {
return () => {
resolveRef.current?.(false);
resolveRef.current = null;
};
}, []);
const confirm = useCallback((options: ConfirmOptions): Promise<boolean> => {
// Resolve any pending promise as cancelled before opening a new dialog
resolveRef.current?.(false);
return new Promise<boolean>((resolve) => {
resolveRef.current = resolve;
setState({
open: true,
title: options.title,
description: options.description,
confirmLabel: options.confirmLabel ?? "Confirm",
cancelLabel: options.cancelLabel ?? "Cancel",
variant: options.variant ?? "default",
});
});
}, []);
const handleConfirm = useCallback(() => {
resolveRef.current?.(true);
resolveRef.current = null;
setState(CLOSED_STATE);
}, []);
const handleCancel = useCallback(() => {
resolveRef.current?.(false);
resolveRef.current = null;
setState(CLOSED_STATE);
}, []);
const dialog = (
<ConfirmDialog
open={state.open}
title={state.title}
description={state.description}
confirmLabel={state.confirmLabel}
cancelLabel={state.cancelLabel}
variant={state.variant}
onConfirm={handleConfirm}
onCancel={handleCancel}
/>
);
return { confirm, dialog };
}
|