Files
2026-08-07 09:40:16 +03:30

63 lines
2.1 KiB
TypeScript

"use client";
import { useEffect, useState } from "react";
/** عددِ گروه‌بندی‌شده با جداکننده‌ی هزارگان (مثلِ 12,500). */
export function fmtNum(n: number | null | undefined): string {
const v = Number(n ?? 0);
return Number.isFinite(v) ? v.toLocaleString("en-US") : "0";
}
/**
* ورودیِ عددی با جداکننده‌ی هزارگان که با کیبورد هم قابلِ تایپ است.
* برخلافِ <input type="number"> که جداکننده را نمی‌پذیرد، این یک ورودیِ متنی است
* که هنگامِ تایپ زنده گروه‌بندی می‌کند و مقدارِ عددی را به onChange می‌دهد.
*/
export default function NumberInput({
value,
onChange,
allowNegative = false,
className = "inp",
style,
disabled,
}: {
value: number;
onChange: (n: number) => void;
allowNegative?: boolean;
className?: string;
style?: React.CSSProperties;
disabled?: boolean;
}) {
const show = (n: number) => n.toLocaleString("en-US");
const [text, setText] = useState(show(value));
// اگر مقدار از بیرون عوض شد (مثلاً ریست فرم)، متن را هم‌گام کن.
useEffect(() => {
const cur = parseInt(text.replace(/[^\d-]/g, "") || "0", 10) || 0;
if (cur !== value) setText(show(value));
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [value]);
return (
<input
className={className}
style={style}
type="text"
inputMode={allowNegative ? "text" : "numeric"}
dir="ltr"
value={text}
disabled={disabled}
onChange={(e) => {
let raw = e.target.value.replace(/[^\d-]/g, "");
if (!allowNegative) raw = raw.replace(/-/g, "");
raw = raw.replace(/(?!^)-/g, ""); // فقط منفیِ ابتدایی
const n = raw === "" || raw === "-" ? 0 : parseInt(raw, 10);
const grouped =
raw === "" ? "" : raw === "-" ? "-" : (n || 0).toLocaleString("en-US");
setText(grouped);
onChange(Number.isNaN(n) ? 0 : n);
}}
/>
);
}