"use client";
import { useRef, useState, type ReactNode } from "react";
import { useFormStatus } from "react-dom";
import { AlertDialog, Spinner } from "@unlocalhosted/metalui";
import { Button, type ButtonVariant } from "./ui";
function Trigger({ children, variant, onOpen }: { children: ReactNode; variant: ButtonVariant; onOpen: () => void }) {
const { pending } = useFormStatus();
return (
<Button
type="button"
variant={variant}
disabled={pending}
aria-busy={pending || undefined}
icon={pending ? <Spinner size="small" label="Working" /> : undefined}
onClick={onOpen}
>
{children}
</Button>
);
}
/** "Delete the label “bug”? It will be removed from 3 issues." → the question, then what follows. */
function splitMessage(message: string) {
const end = message.indexOf("?");
if (end < 0) return { question: message, rest: "" };
return { question: message.slice(0, end + 1), rest: message.slice(end + 1).trim() };
}
/**
* A small form with one button that asks before running a server action: MetalUI's AlertDialog, with
* the question as its title, what is lost below it, Cancel, and the confirm cap naming the action.
* Confirming submits the form, so the server action runs as a normal form submission.
*/
export function ConfirmButton({
action,
message,
children,
variant = "danger",
title,
description,
confirmLabel,
}: {
action: () => Promise<void>;
/** The question and what it costs, in one sentence pair: "Delete X? Anything using it stops working." */
message: string;
children: ReactNode;
variant?: ButtonVariant;
/** Overrides the question taken from `message`. */
title?: string;
/** Overrides what is lost, taken from `message` after its question. */
description?: ReactNode;
/** The confirm cap's words; defaults to the button's own. */
confirmLabel?: ReactNode;
}) {
const form = useRef<HTMLFormElement>(null);
const [open, setOpen] = useState(false);
const { question, rest } = splitMessage(message);
return (
<form ref={form} action={action}>
<Trigger variant={variant} onOpen={() => setOpen(true)}>
{children}
</Trigger>
<AlertDialog open={open} onOpenChange={setOpen}>
<AlertDialog.Popup>
<AlertDialog.Title>{title ?? question}</AlertDialog.Title>
<AlertDialog.Description>{description ?? (rest || "This can’t be undone.")}</AlertDialog.Description>
<AlertDialog.Actions>
<AlertDialog.Cancel />
<AlertDialog.Confirm tone={variant === "primary" || variant === "success" ? "primary" : "destructive"} onClick={() => form.current?.requestSubmit()}>
{confirmLabel ?? children}
</AlertDialog.Confirm>
</AlertDialog.Actions>
</AlertDialog.Popup>
</AlertDialog>
</form>
);
}