TypeScript 97.4%
SQL 1%
JavaScript 0.9%
CSS 0.6%
1"use client";2import * as React from "react";3import { ResponsiveDialog } from "@/components/ui/sheet";4import { Button } from "@/components/ui/button";5import { Input, Textarea } from "@/components/ui/input";67export interface PromptDialogProps {8 open: boolean;9 onOpenChange: (open: boolean) => void;10 title: React.ReactNode;11 description?: React.ReactNode;12 label?: string;13 placeholder?: string;14 defaultValue?: string;15 confirmLabel?: string;16 multiline?: boolean;17 maxLength?: number;18 onSubmit: (value: string) => Promise<void> | void;19}2021/**22 * Replacement for `window.prompt()`: bottom sheet on phones, small dialog on desktop.23 * Resolves with the trimmed value; empty submissions are ignored.24 */25export function PromptDialog({ open, onOpenChange, title, description, label, placeholder, defaultValue = "", confirmLabel = "Save", multiline, maxLength = 200, onSubmit }: PromptDialogProps) {26 const [value, setValue] = React.useState(defaultValue);27 const [busy, setBusy] = React.useState(false);28 const ref = React.useRef<HTMLInputElement & HTMLTextAreaElement>(null);29 React.useEffect(() => {30 if (open) {31 // eslint-disable-next-line react-hooks/set-state-in-effect32 setValue(defaultValue);33 setTimeout(() => {34 ref.current?.focus();35 ref.current?.select();36 }, 60);37 }38 }, [open, defaultValue]);3940 const submit = async () => {41 const v = value.trim();42 if (!v || busy) return;43 setBusy(true);44 try {45 await onSubmit(v);46 onOpenChange(false);47 } finally {48 setBusy(false);49 }50 };5152 return (53 <ResponsiveDialog54 open={open}55 onOpenChange={onOpenChange}56 title={title}57 description={description}58 size="sm"59 footer={60 <div className="flex gap-2 sm:justify-end">61 <Button variant="ghost" className="flex-1 sm:flex-none" onClick={() => onOpenChange(false)} disabled={busy}>62 Cancel63 </Button>64 <Button className="flex-1 sm:flex-none" loading={busy} disabled={!value.trim()} onClick={submit}>65 {confirmLabel}66 </Button>67 </div>68 }69 >70 <form71 className="space-y-1.5 pt-1"72 onSubmit={(e) => {73 e.preventDefault();74 void submit();75 }}76 >77 {label ? <label className="text-[13px] font-medium text-fg-muted">{label}</label> : null}78 {multiline ? (79 <Textarea ref={ref} value={value} onChange={(e) => setValue(e.target.value)} placeholder={placeholder} maxLength={maxLength} className="min-h-[120px]" />80 ) : (81 <Input ref={ref} value={value} onChange={(e) => setValue(e.target.value)} placeholder={placeholder} maxLength={maxLength} className="h-11 sm:h-9" />82 )}83 </form>84 </ResponsiveDialog>85 );86}87