Skip to content

PromptDialog ​

The one dialog a question is asked in: a name to type, or a warning to agree with. Every confirm and every rename can share it, so none of them grows a layout of its own.

tsx
import { PromptDialog } from '@basmilius/react-ui';
tsx
import { useState } from 'react';
import { GitBranch } from 'lucide-react';
import { Button, PromptDialog } from '@basmilius/react-ui';

const wait = (ms: number): Promise<void> => new Promise((resolve) => window.setTimeout(resolve, ms));

export default function PromptDialogDemo() {
    const [open, setOpen] = useState(false);
    const [name, setName] = useState('main');

    return (
        <div className="flex items-center gap-3">
            <Button variant="secondary" onClick={() => setOpen(true)}>
                Rename {name}
            </Button>
            <PromptDialog
                open={open}
                onOpenChange={setOpen}
                title="Rename the branch"
                titleIcon={GitBranch}
                description="Pushes under the new name the next time you push."
                field={{ label: 'New name', initial: name, mono: true, maxLength: 60 }}
                confirmLabel="Rename"
                confirmBusyLabel="Renaming..."
                onConfirm={async (value) => {
                    await wait(800);
                    if (value === 'HEAD') {
                        throw new Error('HEAD is not a valid branch name.');
                    }
                    setName(value);
                    setOpen(false);
                }}
            />
        </div>
    );
}

Try HEAD as the name to see a failure. When onConfirm returns a promise, the confirm button goes quiet while it runs, and a rejection stays on screen as the reason while the dialog stays open. The message comes from the error, or from fallbackMessage when the rejection carries no sentence.

It never closes itself ​

onOpenChange is only ever told false, by Cancel, Escape or a click outside. After a confirm the dialog stays open until you close it. What a confirm leads to is the caller's business: a step that asks a second question would lose the dialog if it closed on its own.

Every opening starts from what the props say. The field resets to field.initial each time open turns true, so one mounted dialog can ask about one branch and then another.

A confirm ​

Without field the dialog is a confirm and nothing else. danger turns the confirm button red. ConfirmDialog wraps this for a destructive step over another dialog.

Props ​

PropTypeDefault
openbooleanRequired.
onOpenChange(open: boolean) => voidRequired.
titlestringRequired.
confirmLabelstringRequired.
onConfirm(value: string, body: string) => void | Promise<unknown>Required. Told the trimmed field and area.
titleIconLucideIcon
descriptionReactNode
field{ label?, ariaLabel?, initial?, placeholder?, mono?, maxLength? }A one-line field. Enter confirms.
area{ label, initial?, placeholder? }A taller field under it.
confirmBusyLabelstringWhat the button says while the step runs.
confirmIconLucideIcon
dangerbooleanfalse
busybooleanfalseFor a step the caller runs elsewhere.
allowEmptybooleanfalseLets the field be confirmed empty.
confirmDisabledbooleanfalse
secondary{ label, onClick }A second way out next to the confirm.
nestedbooleanfalseOver another dialog.
childrenReactNodeAnything between the description and the buttons.
fallbackMessagestring
classNamestringOn the popup.
refRef<HTMLDivElement>

PromptDialogProps is an exported type.