import React, { useRef, useState } from "react"; import { Info, Pencil } from "lucide-react"; import { cn } from "@/lib/utils"; import { colorMixSwatch } from "@/lib/colors"; import { Input } from "./input"; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, } from "./tooltip"; /** * A calculator-section row showing a color swatch, label, optional info * tooltip, a money value, and an optional pencil edit trigger. * * Two modes: * * **Display + external callback** (`onEdit` only): * Shows a formatted string and calls `onEdit` when the pencil is clicked. * The parent handles the edit flow (e.g. opens a drawer). * * **Inline edit** (`numericValue` + `onValueChange`): * Shows a formatted value in display state. Clicking the pencil (or the * value itself) switches the row to an `` field. Committing via * blur or Enter saves the value and returns to display state. * `value` is used as the display-mode formatted string; `numericValue` / * `onValueChange` drive the input. * * The pencil slot is always reserved (invisible when no edit prop) so value * text stays column-aligned in mixed read-only / editable row groups. */ export interface EditableMoneyItemProps { /** Row label (e.g. "Current Expenses") */ label: string; /** Pre-formatted display string (e.g. "$5,000") */ value: string; /** CSS color used for the swatch border and tinted fill */ color: string; /** Fill opacity for the swatch background (default: 0.4) */ fillOpacity?: number; /** Optional tooltip text shown on the info icon */ tooltip?: string; // ── External callback mode ────────────────────────────────────────────────── /** Called when the pencil is clicked (use without numericValue / onValueChange) */ onEdit?: () => void; // ── Inline edit mode ──────────────────────────────────────────────────────── /** Raw numeric value — enables inline edit when paired with onValueChange */ numericValue?: number; /** Called with the new number when the inline input is committed */ onValueChange?: (value: number) => void; className?: string; } export function EditableMoneyItem({ label, value, color, fillOpacity = 0.4, tooltip, onEdit, numericValue, onValueChange, className, }: EditableMoneyItemProps) { const [editing, setEditing] = useState(false); const [draft, setDraft] = useState(""); const inputRef = useRef(null); const inlineMode = numericValue !== undefined && onValueChange !== undefined; const hasEditAction = inlineMode || Boolean(onEdit); const startEditing = () => { if (!inlineMode) { onEdit?.(); return; } setDraft(String(numericValue ?? 0)); setEditing(true); // Focus after React re-renders setTimeout(() => inputRef.current?.select(), 0); }; const commitEdit = () => { const parsed = Number(draft.replace(/[^0-9.-]/g, "")); onValueChange?.(isNaN(parsed) ? 0 : parsed); setEditing(false); }; const handleKeyDown = (e: React.KeyboardEvent) => { if (e.key === "Enter") commitEdit(); if (e.key === "Escape") setEditing(false); }; return (
{/* Label side */}
{label} {tooltip && ( } />

{tooltip}

)}
{/* Value + pencil side */}
{editing && inlineMode ? ( setDraft(e.target.value)} onBlur={commitEdit} onKeyDown={handleKeyDown} className="h-7 w-28 text-right text-base font-semibold tabular-nums" /> ) : ( )}
); }