persian-number-input 4.3.4 → 4.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.fa.md +231 -227
- package/README.md +198 -221
- package/dist/components/PersianNumberInput.d.ts +1 -1
- package/dist/components/PersianNumberInput.d.ts.map +1 -1
- package/dist/components/PersianNumberInput.js +22 -17
- package/dist/components/PersianNumberInput.js.map +1 -1
- package/dist/components/PersianNumberInput.test.js +24 -6
- package/dist/components/PersianNumberInput.test.js.map +1 -1
- package/dist/hooks/useCursorManager.d.ts +4 -0
- package/dist/hooks/useCursorManager.d.ts.map +1 -0
- package/dist/hooks/useCursorManager.js +19 -0
- package/dist/hooks/useCursorManager.js.map +1 -0
- package/dist/hooks/useNumberFormatter.d.ts +18 -0
- package/dist/hooks/useNumberFormatter.d.ts.map +1 -0
- package/dist/hooks/useNumberFormatter.js +29 -0
- package/dist/hooks/useNumberFormatter.js.map +1 -0
- package/dist/hooks/useNumberValidation.d.ts +11 -0
- package/dist/hooks/useNumberValidation.d.ts.map +1 -0
- package/dist/hooks/useNumberValidation.js +22 -0
- package/dist/hooks/useNumberValidation.js.map +1 -0
- package/dist/hooks/useNumericInputEvents.d.ts +18 -0
- package/dist/hooks/useNumericInputEvents.d.ts.map +1 -0
- package/dist/hooks/useNumericInputEvents.js +39 -0
- package/dist/hooks/useNumericInputEvents.js.map +1 -0
- package/dist/hooks/usePersianNumberInput.d.ts +7 -2
- package/dist/hooks/usePersianNumberInput.d.ts.map +1 -1
- package/dist/hooks/usePersianNumberInput.js +66 -63
- package/dist/hooks/usePersianNumberInput.js.map +1 -1
- package/dist/test-utils.d.ts +7 -0
- package/dist/test-utils.d.ts.map +1 -0
- package/dist/test-utils.js +27 -0
- package/dist/test-utils.js.map +1 -0
- package/dist/utils/digitUtils.d.ts +4 -0
- package/dist/utils/digitUtils.d.ts.map +1 -1
- package/dist/utils/digitUtils.js +44 -17
- package/dist/utils/digitUtils.js.map +1 -1
- package/dist/utils/keyFilter.d.ts +16 -0
- package/dist/utils/keyFilter.d.ts.map +1 -0
- package/dist/utils/keyFilter.js +31 -0
- package/dist/utils/keyFilter.js.map +1 -0
- package/dist/utils/validation.d.ts +22 -0
- package/dist/utils/validation.d.ts.map +1 -0
- package/dist/utils/validation.js +85 -0
- package/dist/utils/validation.js.map +1 -0
- package/package.json +15 -4
package/README.md
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
# Persian Number Input
|
|
1
|
+
# Persian Number Input — React Component for Farsi, Arabic & RTL Number Formatting
|
|
2
2
|
|
|
3
3
|
[فارسی](./README.fa.md) | English
|
|
4
|
-
|
|
4
|
+
|
|
5
|
+
A lightweight React library for **Persian (Farsi) and Arabic number inputs** with automatic digit conversion, thousand-separator formatting, decimal precision, and full RTL support. No more manual conversion — users type in their native digits, your form receives clean English numbers.
|
|
5
6
|
|
|
6
7
|
[](https://www.npmjs.com/package/persian-number-input)
|
|
7
8
|
[](https://www.npmjs.com/package/persian-number-input)
|
|
@@ -12,32 +13,39 @@ A lightweight, powerful React library for handling Persian (Farsi) and Arabic nu
|
|
|
12
13
|
|
|
13
14
|
---
|
|
14
15
|
|
|
15
|
-
|
|
16
|
+
## Why Persian Number Input?
|
|
16
17
|
|
|
17
|
-
|
|
18
|
+
Building forms for Persian or Arabic-speaking users comes with real challenges:
|
|
19
|
+
|
|
20
|
+
- Users naturally type `۱۲۳۴` — but your API expects `1234`
|
|
21
|
+
- Standard `<input type="number">` doesn't support Persian or Arabic digits at all
|
|
22
|
+
- Cursor jumps erratically when formatting thousand separators on the fly
|
|
23
|
+
- Decimal handling differs across locales (`٫` vs `.`)
|
|
24
|
+
- RTL inputs need right-aligned text and correct suffix placement
|
|
18
25
|
|
|
19
|
-
This library
|
|
26
|
+
This library solves all of these automatically:
|
|
20
27
|
|
|
21
28
|
```
|
|
22
|
-
|
|
29
|
+
User types: ۱۲۳۴۵۶۷
|
|
30
|
+
Displayed as: ۱,۲۳۴,۵۶۷
|
|
31
|
+
Form receives: "1234567"
|
|
23
32
|
```
|
|
24
33
|
|
|
25
|
-

|
|
26
|
-
|
|
27
34
|
---
|
|
28
35
|
|
|
29
36
|
## ✨ Features
|
|
30
37
|
|
|
31
|
-
- 🔢 **Automatic
|
|
32
|
-
- 🌍 **Multi-locale
|
|
33
|
-
- 📊 **
|
|
34
|
-
- 💰 **Currency
|
|
35
|
-
- ⚡ **
|
|
36
|
-
- 🎯 **
|
|
37
|
-
-
|
|
38
|
-
-
|
|
39
|
-
-
|
|
40
|
-
|
|
38
|
+
- 🔢 **Automatic digit conversion** — Persian (۰-۹) and Arabic (٠-٩) to English and back
|
|
39
|
+
- 🌍 **Multi-locale** — `fa` (Farsi/Persian), `ar` (Arabic), `en` (English)
|
|
40
|
+
- 📊 **Thousand separator formatting** — customizable separator character and group size
|
|
41
|
+
- 💰 **Currency-ready** — add prefix, suffix (e.g. تومان, ریال, ر.س), and custom decimal separator
|
|
42
|
+
- ⚡ **~1KB gzipped** — zero extra dependencies, pure TypeScript
|
|
43
|
+
- 🎯 **TypeScript** — full type definitions included
|
|
44
|
+
- 🔄 **Cursor-position preservation** — no jump on re-format
|
|
45
|
+
- 📋 **Smart paste handling** — pasted content is sanitized and auto-converted
|
|
46
|
+
- ✅ **Min/max validation** — soft enforcement with `aria-invalid` and automatic clamp on blur
|
|
47
|
+
|
|
48
|
+
- ♿ **Accessible** — follows WCAG input best practices
|
|
41
49
|
|
|
42
50
|
---
|
|
43
51
|
|
|
@@ -45,22 +53,18 @@ persian-number-input: ~1KB (minified + gzipped)
|
|
|
45
53
|
|
|
46
54
|
```bash
|
|
47
55
|
npm install persian-number-input
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
```bash
|
|
56
|
+
# or
|
|
51
57
|
yarn add persian-number-input
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
```bash
|
|
58
|
+
# or
|
|
55
59
|
pnpm add persian-number-input
|
|
56
60
|
```
|
|
57
61
|
|
|
62
|
+
> **Requirements:** React 16.8+, works with Next.js, Vite, CRA, and any React-based framework.
|
|
63
|
+
|
|
58
64
|
---
|
|
59
65
|
|
|
60
66
|
## 🎯 Quick Start
|
|
61
67
|
|
|
62
|
-
### Basic Usage
|
|
63
|
-
|
|
64
68
|
```tsx
|
|
65
69
|
import { PersianNumberInput } from "persian-number-input";
|
|
66
70
|
|
|
@@ -69,19 +73,19 @@ function App() {
|
|
|
69
73
|
<PersianNumberInput
|
|
70
74
|
initialValue={1234567}
|
|
71
75
|
locale="fa"
|
|
72
|
-
onValueChange={(value) => console.log(value)}
|
|
76
|
+
onValueChange={(value) => console.log(value)} // "1234567"
|
|
73
77
|
/>
|
|
74
78
|
);
|
|
75
79
|
}
|
|
76
80
|
```
|
|
77
81
|
|
|
78
|
-
|
|
82
|
+
Output displayed to user: `۱,۲۳۴,۵۶۷`
|
|
79
83
|
|
|
80
84
|
---
|
|
81
85
|
|
|
82
86
|
## 📚 Usage Examples
|
|
83
87
|
|
|
84
|
-
###
|
|
88
|
+
### Persian Toman currency input
|
|
85
89
|
|
|
86
90
|
```tsx
|
|
87
91
|
<PersianNumberInput
|
|
@@ -94,11 +98,27 @@ function App() {
|
|
|
94
98
|
/>
|
|
95
99
|
```
|
|
96
100
|
|
|
97
|
-
|
|
101
|
+
Displayed: `۵,۰۰۰,۰۰۰ تومان`
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
### Arabic locale (SAR)
|
|
106
|
+
|
|
107
|
+
```tsx
|
|
108
|
+
<PersianNumberInput
|
|
109
|
+
initialValue={987654}
|
|
110
|
+
locale="ar"
|
|
111
|
+
separatorChar=","
|
|
112
|
+
suffix="ر.س"
|
|
113
|
+
onValueChange={(value) => console.log(value)}
|
|
114
|
+
/>
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Displayed: `٩٨٧,٦٥٤ ر.س`
|
|
98
118
|
|
|
99
119
|
---
|
|
100
120
|
|
|
101
|
-
### Decimal
|
|
121
|
+
### Decimal numbers with custom separator
|
|
102
122
|
|
|
103
123
|
```tsx
|
|
104
124
|
<PersianNumberInput
|
|
@@ -111,11 +131,11 @@ function App() {
|
|
|
111
131
|
/>
|
|
112
132
|
```
|
|
113
133
|
|
|
114
|
-
|
|
134
|
+
Displayed: `۱,۲۳۴٫۵۶`
|
|
115
135
|
|
|
116
136
|
---
|
|
117
137
|
|
|
118
|
-
### Price
|
|
138
|
+
### Price input with min/max validation
|
|
119
139
|
|
|
120
140
|
```tsx
|
|
121
141
|
<PersianNumberInput
|
|
@@ -129,27 +149,41 @@ function App() {
|
|
|
129
149
|
/>
|
|
130
150
|
```
|
|
131
151
|
|
|
132
|
-
**Output:** `۰ ریال`
|
|
133
|
-
|
|
134
152
|
---
|
|
135
153
|
|
|
136
|
-
###
|
|
154
|
+
### With React Hook Form
|
|
137
155
|
|
|
138
156
|
```tsx
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
locale="ar"
|
|
142
|
-
separatorChar=","
|
|
143
|
-
suffix="ر.س"
|
|
144
|
-
onValueChange={(value) => console.log(value)}
|
|
145
|
-
/>
|
|
146
|
-
```
|
|
157
|
+
import { useForm, Controller } from "react-hook-form";
|
|
158
|
+
import { PersianNumberInput } from "persian-number-input";
|
|
147
159
|
|
|
148
|
-
|
|
160
|
+
function ProductForm() {
|
|
161
|
+
const { control, handleSubmit } = useForm();
|
|
162
|
+
|
|
163
|
+
return (
|
|
164
|
+
<form onSubmit={handleSubmit(console.log)}>
|
|
165
|
+
<Controller
|
|
166
|
+
name="price"
|
|
167
|
+
control={control}
|
|
168
|
+
rules={{ required: true }}
|
|
169
|
+
render={({ field }) => (
|
|
170
|
+
<PersianNumberInput
|
|
171
|
+
locale="fa"
|
|
172
|
+
suffix="تومان"
|
|
173
|
+
onValueChange={field.onChange}
|
|
174
|
+
initialValue={field.value}
|
|
175
|
+
/>
|
|
176
|
+
)}
|
|
177
|
+
/>
|
|
178
|
+
<button type="submit">ثبت</button>
|
|
179
|
+
</form>
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
```
|
|
149
183
|
|
|
150
184
|
---
|
|
151
185
|
|
|
152
|
-
### Using the
|
|
186
|
+
### Using the hook directly (advanced)
|
|
153
187
|
|
|
154
188
|
```tsx
|
|
155
189
|
import { usePersianNumberInput } from "persian-number-input";
|
|
@@ -162,10 +196,7 @@ function CustomInput() {
|
|
|
162
196
|
maxDecimals: 2,
|
|
163
197
|
min: 0,
|
|
164
198
|
max: 1000000,
|
|
165
|
-
onValueChange: (val) =>
|
|
166
|
-
console.log("Raw value:", val); // "1000"
|
|
167
|
-
console.log("Displayed value:", value); // "۱,۰۰۰"
|
|
168
|
-
},
|
|
199
|
+
onValueChange: (val) => console.log("English value:", val),
|
|
169
200
|
});
|
|
170
201
|
|
|
171
202
|
return (
|
|
@@ -174,288 +205,234 @@ function CustomInput() {
|
|
|
174
205
|
value={value}
|
|
175
206
|
onChange={onChange}
|
|
176
207
|
onBlur={onBlur}
|
|
177
|
-
|
|
208
|
+
onPaste={onPaste}
|
|
209
|
+
dir="rtl"
|
|
178
210
|
/>
|
|
179
211
|
);
|
|
180
212
|
}
|
|
181
213
|
```
|
|
182
214
|
|
|
215
|
+
The hook also returns `isInvalid` (boolean) when the value exceeds `max`, and `onPaste` for paste event handling.
|
|
216
|
+
|
|
183
217
|
---
|
|
184
218
|
|
|
185
219
|
## 🛠️ API Reference
|
|
186
220
|
|
|
187
|
-
### PersianNumberInput
|
|
221
|
+
### `PersianNumberInput` props
|
|
188
222
|
|
|
189
|
-
| Prop | Type | Default | Description
|
|
190
|
-
| ---------------- | -------------------------------------- | ----------- |
|
|
191
|
-
| `initialValue` | `number \| string` | `undefined` | Initial value of the input
|
|
192
|
-
| `locale` | `"fa" \| "ar" \| "en"` | `"fa"` |
|
|
193
|
-
| `separatorCount` | `number` | `3` |
|
|
194
|
-
| `separatorChar` | `string` | `","` |
|
|
195
|
-
| `decimalChar` | `string` | Auto | Decimal separator
|
|
196
|
-
| `suffix` | `string` | `undefined` |
|
|
197
|
-
| `maxDecimals` | `number` | `undefined` | Maximum decimal places
|
|
198
|
-
| `min` | `number` | `undefined` | Minimum allowed value
|
|
199
|
-
| `max` | `number` | `undefined` | Maximum allowed value
|
|
200
|
-
| `showZero` | `boolean` | `false` |
|
|
201
|
-
| `onValueChange` | `(value: string \| undefined) => void` | `undefined` |
|
|
223
|
+
| Prop | Type | Default | Description |
|
|
224
|
+
| ---------------- | -------------------------------------- | ----------- | ---------------------------------------------------- |
|
|
225
|
+
| `initialValue` | `number \| string` | `undefined` | Initial value of the input |
|
|
226
|
+
| `locale` | `"fa" \| "ar" \| "en"` | `"fa"` | Digit locale — Farsi, Arabic, or English |
|
|
227
|
+
| `separatorCount` | `number` | `3` | Digits per group (3 = thousand separator) |
|
|
228
|
+
| `separatorChar` | `string` | `","` | Thousand separator character |
|
|
229
|
+
| `decimalChar` | `string` | Auto | Decimal separator (`٫` for fa, `.` for en) |
|
|
230
|
+
| `suffix` | `string` | `undefined` | Currency or unit suffix (e.g. `تومان`, `ریال`) |
|
|
231
|
+
| `maxDecimals` | `number` | `undefined` | Maximum allowed decimal places |
|
|
232
|
+
| `min` | `number` | `undefined` | Minimum allowed value |
|
|
233
|
+
| `max` | `number` | `undefined` | Maximum allowed value — soft validation (marks invalid, clamps on blur) |
|
|
234
|
+
| `showZero` | `boolean` | `false` | Display `0` instead of empty when value is zero |
|
|
235
|
+
| `onValueChange` | `(value: string \| undefined) => void` | `undefined` | Fires on change — always returns English digits |
|
|
202
236
|
|
|
203
|
-
All standard HTML input props are also supported
|
|
237
|
+
All standard HTML `<input>` props are also supported, including `onChange`, `onKeyDown`, `onPaste`, `className`, `style`, `placeholder`, `dir`, and `disabled`.
|
|
204
238
|
|
|
205
239
|
---
|
|
206
240
|
|
|
207
|
-
### Utility
|
|
241
|
+
### Utility functions
|
|
208
242
|
|
|
209
243
|
#### `transformNumber(rawValue, options)`
|
|
210
244
|
|
|
211
|
-
|
|
245
|
+
Format any number string according to locale and options.
|
|
212
246
|
|
|
213
247
|
```tsx
|
|
214
248
|
import { transformNumber } from "persian-number-input";
|
|
215
249
|
|
|
216
|
-
|
|
250
|
+
transformNumber("1234567.89", {
|
|
217
251
|
locale: "fa",
|
|
218
252
|
separatorCount: 3,
|
|
219
253
|
separatorChar: ",",
|
|
220
254
|
maxDecimals: 2,
|
|
221
255
|
suffix: "تومان",
|
|
222
256
|
});
|
|
223
|
-
|
|
224
|
-
console.log(formatted); // "۱,۲۳۴,۵۶۷٫۸۹ تومان"
|
|
257
|
+
// → "۱,۲۳۴,۵۶۷٫۸۹ تومان"
|
|
225
258
|
```
|
|
226
259
|
|
|
227
260
|
#### `toEnglishDigits(str, decimalChar?)`
|
|
228
261
|
|
|
229
|
-
|
|
262
|
+
Convert Persian or Arabic digits to English.
|
|
230
263
|
|
|
231
264
|
```tsx
|
|
232
265
|
import { toEnglishDigits } from "persian-number-input";
|
|
233
266
|
|
|
234
|
-
|
|
235
|
-
|
|
267
|
+
toEnglishDigits("۱۲۳۴"); // "1234"
|
|
268
|
+
toEnglishDigits("٩٨٧٦"); // "9876"
|
|
236
269
|
```
|
|
237
270
|
|
|
238
271
|
#### `toLocalizedDigits(numStr, locale)`
|
|
239
272
|
|
|
240
|
-
|
|
273
|
+
Convert English digits to the target locale.
|
|
241
274
|
|
|
242
275
|
```tsx
|
|
243
276
|
import { toLocalizedDigits } from "persian-number-input";
|
|
244
277
|
|
|
245
|
-
|
|
246
|
-
|
|
278
|
+
toLocalizedDigits("1234", "fa"); // "۱۲۳۴"
|
|
279
|
+
toLocalizedDigits("5678", "ar"); // "٥٦٧٨"
|
|
247
280
|
```
|
|
248
281
|
|
|
249
282
|
#### `sanitizeNumericInput(value, maxDecimals?, decimalChar?)`
|
|
250
283
|
|
|
251
|
-
|
|
284
|
+
Strip non-numeric characters and enforce decimal limits.
|
|
252
285
|
|
|
253
286
|
```tsx
|
|
254
287
|
import { sanitizeNumericInput } from "persian-number-input";
|
|
255
288
|
|
|
256
|
-
|
|
257
|
-
|
|
289
|
+
sanitizeNumericInput("۱۲۳abc۴۵۶", 2); // "123456"
|
|
290
|
+
sanitizeNumericInput("12.345.67", 2); // "12.34"
|
|
258
291
|
```
|
|
259
292
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
## 🎨 Styling
|
|
293
|
+
#### `stripNonNumeric(str)`
|
|
263
294
|
|
|
264
|
-
|
|
295
|
+
Remove all non-digit and non-dot characters.
|
|
265
296
|
|
|
266
297
|
```tsx
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
className="custom-input"
|
|
271
|
-
style={{
|
|
272
|
-
padding: "12px",
|
|
273
|
-
fontSize: "16px",
|
|
274
|
-
border: "2px solid #4F46E5",
|
|
275
|
-
borderRadius: "8px",
|
|
276
|
-
textAlign: "right",
|
|
277
|
-
}}
|
|
278
|
-
/>
|
|
298
|
+
import { stripNonNumeric } from "persian-number-input";
|
|
299
|
+
|
|
300
|
+
stripNonNumeric("12abc34.56xyz"); // "1234.56"
|
|
279
301
|
```
|
|
280
302
|
|
|
281
|
-
|
|
303
|
+
#### `normalizeDecimals(str)`
|
|
304
|
+
|
|
305
|
+
Keep only the first decimal point.
|
|
282
306
|
|
|
283
307
|
```tsx
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
className="w-full px-4 py-3 text-lg border-2 border-indigo-500 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-600 text-right"
|
|
288
|
-
/>
|
|
308
|
+
import { normalizeDecimals } from "persian-number-input";
|
|
309
|
+
|
|
310
|
+
normalizeDecimals("12.34.56"); // "12.3456"
|
|
289
311
|
```
|
|
290
312
|
|
|
291
|
-
|
|
313
|
+
#### `stripLeadingZeros(str)`
|
|
292
314
|
|
|
293
|
-
|
|
315
|
+
Strip leading zeros from a numeric string.
|
|
294
316
|
|
|
295
|
-
|
|
317
|
+
```tsx
|
|
318
|
+
import { stripLeadingZeros } from "persian-number-input";
|
|
319
|
+
|
|
320
|
+
stripLeadingZeros("001234.56"); // "1234.56"
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
#### `applyDecimalPrecision(str, maxDecimals?)`
|
|
324
|
+
|
|
325
|
+
Truncate fractional part to the specified decimal places.
|
|
296
326
|
|
|
297
327
|
```tsx
|
|
298
|
-
import {
|
|
299
|
-
import { PersianNumberInput } from "persian-number-input";
|
|
328
|
+
import { applyDecimalPrecision } from "persian-number-input";
|
|
300
329
|
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
const [rate, setRate] = useState<string>();
|
|
304
|
-
const [years, setYears] = useState<string>();
|
|
330
|
+
applyDecimalPrecision("1234.5678", 2); // "1234.56"
|
|
331
|
+
```
|
|
305
332
|
|
|
306
|
-
|
|
307
|
-
if (!principal || !rate || !years) return 0;
|
|
308
|
-
const p = parseFloat(principal);
|
|
309
|
-
const r = parseFloat(rate) / 100 / 12;
|
|
310
|
-
const n = parseFloat(years) * 12;
|
|
311
|
-
return (p * r * Math.pow(1 + r, n)) / (Math.pow(1 + r, n) - 1);
|
|
312
|
-
};
|
|
333
|
+
#### `roundToDecimals(value, maxDecimals?)`
|
|
313
334
|
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
suffix="تومان"
|
|
321
|
-
onValueChange={setPrincipal}
|
|
322
|
-
min={0}
|
|
323
|
-
/>
|
|
324
|
-
</div>
|
|
325
|
-
<div>
|
|
326
|
-
<label>نرخ سود (٪):</label>
|
|
327
|
-
<PersianNumberInput
|
|
328
|
-
locale="fa"
|
|
329
|
-
maxDecimals={2}
|
|
330
|
-
onValueChange={setRate}
|
|
331
|
-
min={0}
|
|
332
|
-
max={100}
|
|
333
|
-
/>
|
|
334
|
-
</div>
|
|
335
|
-
<div>
|
|
336
|
-
<label>مدت زمان (سال):</label>
|
|
337
|
-
<PersianNumberInput
|
|
338
|
-
locale="fa"
|
|
339
|
-
onValueChange={setYears}
|
|
340
|
-
min={1}
|
|
341
|
-
max={30}
|
|
342
|
-
/>
|
|
343
|
-
</div>
|
|
344
|
-
<p>
|
|
345
|
-
پرداخت ماهیانه: {calculateMonthlyPayment().toLocaleString("fa-IR")}{" "}
|
|
346
|
-
تومان
|
|
347
|
-
</p>
|
|
348
|
-
</div>
|
|
349
|
-
);
|
|
350
|
-
}
|
|
335
|
+
Alias for `applyDecimalPrecision` — truncate fractional part.
|
|
336
|
+
|
|
337
|
+
```tsx
|
|
338
|
+
import { roundToDecimals } from "persian-number-input";
|
|
339
|
+
|
|
340
|
+
roundToDecimals("1234.5678", 2); // "1234.56"
|
|
351
341
|
```
|
|
352
342
|
|
|
353
343
|
---
|
|
354
344
|
|
|
355
|
-
|
|
345
|
+
## 🎨 Styling
|
|
346
|
+
|
|
347
|
+
The component accepts all standard input props including `className` and `style`:
|
|
356
348
|
|
|
357
349
|
```tsx
|
|
358
|
-
|
|
359
|
-
|
|
350
|
+
<PersianNumberInput
|
|
351
|
+
locale="fa"
|
|
352
|
+
className="w-full px-4 py-3 text-lg border-2 border-indigo-500 rounded-lg text-right focus:outline-none focus:ring-2 focus:ring-indigo-600"
|
|
353
|
+
/>
|
|
354
|
+
```
|
|
360
355
|
|
|
361
|
-
|
|
362
|
-
const { control, handleSubmit } = useForm();
|
|
356
|
+
For RTL layouts, add `dir="rtl"` to the wrapper or `style={{ textAlign: "right" }}` on the input.
|
|
363
357
|
|
|
364
|
-
|
|
365
|
-
console.log(data);
|
|
366
|
-
};
|
|
358
|
+
---
|
|
367
359
|
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
control={control}
|
|
373
|
-
rules={{ required: true }}
|
|
374
|
-
render={({ field }) => (
|
|
375
|
-
<PersianNumberInput
|
|
376
|
-
locale="fa"
|
|
377
|
-
suffix="تومان"
|
|
378
|
-
onValueChange={field.onChange}
|
|
379
|
-
initialValue={field.value}
|
|
380
|
-
/>
|
|
381
|
-
)}
|
|
382
|
-
/>
|
|
383
|
-
<button type="submit">ثبت</button>
|
|
384
|
-
</form>
|
|
385
|
-
);
|
|
386
|
-
}
|
|
360
|
+
## 📊 Bundle Size
|
|
361
|
+
|
|
362
|
+
```
|
|
363
|
+
persian-number-input: ~1KB (minified + gzipped)
|
|
387
364
|
```
|
|
388
365
|
|
|
366
|
+
One of the smallest solutions for Persian/Arabic number input in the React ecosystem.
|
|
367
|
+
|
|
389
368
|
---
|
|
390
369
|
|
|
391
|
-
##
|
|
370
|
+
## 🏆 Comparison
|
|
392
371
|
|
|
393
|
-
|
|
372
|
+
| Feature | persian-number-input | Native `<input>` | Other libraries |
|
|
373
|
+
| ------------------------------ | :------------------: | :--------------: | :-------------: |
|
|
374
|
+
| Persian & Arabic digit input | ✅ | ❌ | ⚠️ Partial |
|
|
375
|
+
| Cursor preservation on format | ✅ | ❌ | ⚠️ Often buggy |
|
|
376
|
+
| Multi-locale (fa / ar / en) | ✅ | ❌ | ❌ |
|
|
377
|
+
| Thousand separator formatting | ✅ | ❌ | ⚠️ Limited |
|
|
378
|
+
| Decimal precision control | ✅ | ❌ | ⚠️ Limited |
|
|
379
|
+
| Min/max validation | ✅ | Partial | ⚠️ Varies |
|
|
380
|
+
| TypeScript support | ✅ | ✅ | ⚠️ Varies |
|
|
381
|
+
| Bundle size | 🟢 ~1KB | 🟢 Native | 🔴 5–30KB |
|
|
382
|
+
| Zero peer dependencies | ✅ | ✅ | ❌ |
|
|
394
383
|
|
|
395
|
-
|
|
384
|
+
---
|
|
396
385
|
|
|
397
|
-
|
|
398
|
-
- Number formatting varies across locales
|
|
399
|
-
- Maintaining cursor position during formatting is complex
|
|
400
|
-
- Decimal precision handling requires careful implementation
|
|
386
|
+
## ❓ FAQ
|
|
401
387
|
|
|
402
|
-
|
|
388
|
+
**Q: Does this work with Next.js and Server Components?**
|
|
389
|
+
A: Yes. The component is a client component — add `"use client"` at the top of your file when using it inside a Next.js App Router layout.
|
|
403
390
|
|
|
404
|
-
|
|
391
|
+
**Q: How do I get the numeric value back in English for my API?**
|
|
392
|
+
A: The `onValueChange` callback always returns the raw English digit string (e.g. `"1234567"`), regardless of which locale is displayed.
|
|
405
393
|
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
// Component displays: ۱,۲۳۴,۵۶۷
|
|
409
|
-
// Form receives: "1234567"
|
|
410
|
-
```
|
|
394
|
+
**Q: Can I use this inside a form with native `onSubmit`?**
|
|
395
|
+
A: Use `onValueChange` to store the value in local state, then read from state on submit. The component also supports React Hook Form via the `Controller` pattern (see example above).
|
|
411
396
|
|
|
412
|
-
|
|
397
|
+
**Q: Does it handle right-to-left text direction automatically?**
|
|
398
|
+
A: The component formats numbers correctly for RTL — for full RTL layout, set `dir="rtl"` on your container or `style={{ textAlign: "right" }}` on the input.
|
|
413
399
|
|
|
414
|
-
|
|
400
|
+
**Q: What's the difference between `locale="fa"` and `locale="ar"`?**
|
|
401
|
+
A: `fa` uses Persian digits (۰–۹) and the `٫` decimal separator. `ar` uses Eastern Arabic digits (٠–٩). Both return English digits to `onValueChange`.
|
|
415
402
|
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
| Auto digit conversion | ✅ | ❌ | ⚠️ Partial |
|
|
419
|
-
| Cursor preservation | ✅ | ❌ | ⚠️ Buggy |
|
|
420
|
-
| TypeScript support | ✅ | ✅ | ⚠️ Varies |
|
|
421
|
-
| Multi-locale | ✅ | ❌ | ❌ |
|
|
422
|
-
| Bundle size | 🟢 Small | 🟢 N/A | 🔴 Large |
|
|
423
|
-
| Decimal precision | ✅ | ❌ | ⚠️ Limited |
|
|
403
|
+
**Q: Is there a maximum number size?**
|
|
404
|
+
A: The library uses string-based numeric comparison for validation, so it handles arbitrarily large and precise numbers without floating-point artifacts.
|
|
424
405
|
|
|
425
406
|
---
|
|
426
407
|
|
|
427
408
|
## 🤝 Contributing
|
|
428
409
|
|
|
429
|
-
Contributions are welcome!
|
|
410
|
+
Contributions are welcome!
|
|
430
411
|
|
|
431
412
|
1. Fork the repository
|
|
432
|
-
2. Create your feature branch
|
|
433
|
-
3. Commit your changes
|
|
434
|
-
4. Push
|
|
413
|
+
2. Create your feature branch: `git checkout -b feature/your-feature`
|
|
414
|
+
3. Commit your changes: `git commit -m 'Add your feature'`
|
|
415
|
+
4. Push: `git push origin feature/your-feature`
|
|
435
416
|
5. Open a Pull Request
|
|
436
417
|
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
## 📄 License
|
|
440
|
-
|
|
441
|
-
MIT © javad Sharifi
|
|
418
|
+
Please open an issue first for major changes so we can discuss the approach.
|
|
442
419
|
|
|
443
420
|
---
|
|
444
421
|
|
|
445
|
-
##
|
|
422
|
+
## 📄 License
|
|
446
423
|
|
|
447
|
-
|
|
448
|
-
- Uses [decimal.js](https://github.com/MikeMcl/decimal.js/) for precise decimal calculations
|
|
449
|
-
- Inspired by the needs of Persian and Arabic speaking developers
|
|
424
|
+
MIT © [Javad Sharifi](https://github.com/javadSharifi)
|
|
450
425
|
|
|
451
426
|
---
|
|
452
427
|
|
|
453
428
|
## 📞 Support
|
|
454
429
|
|
|
455
|
-
-
|
|
430
|
+
- 💬 Telegram: [@Javad_sharifi98](https://t.me/Javad_sharifi98)
|
|
456
431
|
- 🐛 [Issue Tracker](https://github.com/javadSharifi/persian-number-input/issues)
|
|
457
432
|
- 💬 [Discussions](https://github.com/javadSharifi/persian-number-input/discussions)
|
|
458
433
|
|
|
459
434
|
---
|
|
460
435
|
|
|
461
|
-
**Made with ❤️ for
|
|
436
|
+
**Made with ❤️ for Persian and Arabic developer communities**
|
|
437
|
+
|
|
438
|
+
<!-- Keywords for discoverability: persian number input react, farsi digit input, arabic number react component, rtl number input, persian input component, تبدیل اعداد فارسی به انگلیسی, ورودی عدد فارسی ریکت -->
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import type { TransformNumberOptions } from "../utils/transformNumber";
|
|
3
|
-
interface PersianNumberInputProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, "
|
|
3
|
+
interface PersianNumberInputProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, "value" | "min" | "max">, Omit<TransformNumberOptions, "maxDecimals"> {
|
|
4
4
|
initialValue?: number | string;
|
|
5
5
|
onValueChange?: (value: string | undefined) => void;
|
|
6
6
|
min?: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PersianNumberInput.d.ts","sourceRoot":"","sources":["../../src/components/PersianNumberInput.tsx"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"PersianNumberInput.d.ts","sourceRoot":"","sources":["../../src/components/PersianNumberInput.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA8B,MAAM,OAAO,CAAC;AAKnD,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAEvE,UAAU,uBACR,SAAQ,IAAI,CACR,KAAK,CAAC,mBAAmB,CAAC,gBAAgB,CAAC,EAC3C,OAAO,GAAG,KAAK,GAAG,KAAK,CACxB,EACD,IAAI,CAAC,sBAAsB,EAAE,aAAa,CAAC;IAC7C,YAAY,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC/B,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,KAAK,IAAI,CAAC;IACpD,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAgBD,QAAA,MAAM,kBAAkB,kGA6DtB,CAAC;AAIH,eAAe,kBAAkB,CAAC"}
|