import React, { useEffect, useState } from "react"; type BaseProps = Omit< React.InputHTMLAttributes, "value" | "onChange" > & { className?: string; }; type StringProps = BaseProps & { value: string; onValue: (v: string) => void; valueCents?: never; onChange?: never; }; type CentsProps = BaseProps & { valueCents: number; onChange: (cents: number) => void; value?: never; onValue?: never; }; type Props = StringProps | CentsProps; export default function CurrencyInput({ className, placeholder = "0.00", ...rest }: Props) { const mergedClass = ["input", className].filter(Boolean).join(" "); const formatString = (raw: string) => { const cleanedRaw = raw.replace(/,/g, ".").replace(/[^0-9.]/g, ""); const parts = cleanedRaw.split("."); return parts.length === 1 ? parts[0] : `${parts[0]}.${parts.slice(1).join("").slice(0, 2)}`; }; if ("valueCents" in rest) { const { valueCents, onChange, ...inputProps } = rest as CentsProps; const [display, setDisplay] = useState(""); // keep display in sync when valueCents prop changes externally useEffect(() => { const asNumber = (valueCents ?? 0) / 100; setDisplay(Number.isFinite(asNumber) ? asNumber.toString() : ""); }, [valueCents]); const handleChange = (e: React.ChangeEvent) => { const formatted = formatString(e.target.value); setDisplay(formatted); const parsed = Number.parseFloat(formatted); if (Number.isFinite(parsed)) { onChange(Math.round(parsed * 100)); } }; return ( ); } const { value, onValue, ...inputProps } = rest as StringProps; const handleChange = (e: React.ChangeEvent) => { onValue(formatString(e.target.value)); }; return ( ); }