import React, { useEffect, useState } from "react"; import { cn } from "@/lib/utils"; import { InputGroup, InputGroupAddon, InputGroupInput, } from "@/components/ui/input-group"; /** * Standalone currency input with a "$" prefix and thousands-separator formatting. * Strips the slider and option buttons from the app's BaseMoneyInput — use this * for expense row inputs or any context where just the dollar field is needed. * * Consumers should wrap in + / for label/error display. */ export type CurrencyInputProps = { /** Current dollar value (whole dollars) */ value: number; /** Called on blur with the parsed integer value */ onChange: (value: number) => void; /** HTML id forwarded to the inner — use for Field/label association */ id?: string; disabled?: boolean; placeholder?: string; /** Minimum allowed value (default: 0) */ min?: number; /** When true, renders the InputGroup in its error visual state */ error?: boolean; className?: string; }; function formatDisplay(n: number): string { return n.toLocaleString("en-AU", { maximumFractionDigits: 0 }); } function parseRaw(raw: string): number { const digits = raw.replace(/[^0-9.]/g, ""); const n = parseFloat(digits); return isNaN(n) ? 0 : Math.round(n); } export function CurrencyInput({ value, onChange, id, disabled = false, placeholder = "0", min = 0, error = false, className, }: CurrencyInputProps) { const [displayValue, setDisplayValue] = useState(() => value > 0 ? formatDisplay(value) : "", ); const [focused, setFocused] = useState(false); // Keep display in sync when value changes externally (e.g. form reset) useEffect(() => { if (!focused) { setDisplayValue(value > 0 ? formatDisplay(value) : ""); } }, [value, focused]); function handleChange(e: React.ChangeEvent) { // Allow digits, comma separators, and one decimal point while typing const cleaned = e.target.value.replace(/[^0-9,.]/g, ""); setDisplayValue(cleaned); } function handleFocus() { setFocused(true); // Show bare digits while editing — no commas setDisplayValue(value > 0 ? String(value) : ""); } function handleBlur() { setFocused(false); const parsed = Math.max(min, parseRaw(displayValue)); onChange(parsed); setDisplayValue(parsed > 0 ? formatDisplay(parsed) : ""); } return ( $ ); }