Files
SkyMoney/web/src/components/CurrencyInput.tsx
Ricearoni1245 cccce2c854
All checks were successful
Deploy / deploy (push) Successful in 59s
Security Tests / security-non-db (push) Successful in 18s
Security Tests / security-db (push) Successful in 24s
fixed inputs to accept decimals
2026-03-11 20:02:32 -05:00

88 lines
2.2 KiB
TypeScript

import React, { useEffect, useState } from "react";
type BaseProps = Omit<
React.InputHTMLAttributes<HTMLInputElement>,
"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<string>("");
// 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<HTMLInputElement>) => {
const formatted = formatString(e.target.value);
setDisplay(formatted);
const parsed = Number.parseFloat(formatted);
if (Number.isFinite(parsed)) {
onChange(Math.round(parsed * 100));
}
};
return (
<input
{...inputProps}
className={mergedClass}
inputMode="decimal"
placeholder={placeholder}
value={display}
onChange={handleChange}
pattern="[0-9]*[.,]?[0-9]*"
/>
);
}
const { value, onValue, ...inputProps } = rest as StringProps;
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
onValue(formatString(e.target.value));
};
return (
<input
{...inputProps}
className={mergedClass}
inputMode="decimal"
placeholder={placeholder}
value={value}
onChange={handleChange}
pattern="[0-9]*[.,]?[0-9]*"
/>
);
}