final touches for beta skymoney (at least i think)

This commit is contained in:
2026-01-18 00:00:44 -06:00
parent 4eae966f96
commit f4f0ae5df2
161 changed files with 26016 additions and 1966 deletions

View File

@@ -1,31 +1,75 @@
import React from "react";
export default function CurrencyInput({
value,
onValue,
placeholder = "0.00",
}: {
type BaseProps = Omit<
React.InputHTMLAttributes<HTMLInputElement>,
"value" | "onChange"
> & {
className?: string;
};
type StringProps = BaseProps & {
value: string;
onValue: (v: string) => void;
placeholder?: string;
}) {
function onChange(e: React.ChangeEvent<HTMLInputElement>) {
const raw = e.target.value.replace(/[^0-9.]/g, "");
// Keep only first dot, max 2 decimals
const parts = raw.split(".");
const cleaned =
parts.length === 1
? parts[0]
: `${parts[0]}.${parts.slice(1).join("").slice(0, 2)}`;
onValue(cleaned);
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(/[^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 handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const formatted = formatString(e.target.value);
const parsed = Number.parseFloat(formatted || "0");
onChange(Number.isFinite(parsed) ? Math.round(parsed * 100) : 0);
};
const displayValue = (valueCents ?? 0) / 100;
const value = Number.isFinite(displayValue) ? displayValue.toString() : "";
return (
<input
{...inputProps}
className={mergedClass}
inputMode="decimal"
placeholder={placeholder}
value={value}
onChange={handleChange}
/>
);
}
const { value, onValue, ...inputProps } = rest as StringProps;
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
onValue(formatString(e.target.value));
};
return (
<input
className="input"
{...inputProps}
className={mergedClass}
inputMode="decimal"
placeholder={placeholder}
value={value}
onChange={onChange}
onChange={handleChange}
/>
);
}