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.
Files changed (45) hide show
  1. package/README.fa.md +231 -227
  2. package/README.md +198 -221
  3. package/dist/components/PersianNumberInput.d.ts +1 -1
  4. package/dist/components/PersianNumberInput.d.ts.map +1 -1
  5. package/dist/components/PersianNumberInput.js +22 -17
  6. package/dist/components/PersianNumberInput.js.map +1 -1
  7. package/dist/components/PersianNumberInput.test.js +24 -6
  8. package/dist/components/PersianNumberInput.test.js.map +1 -1
  9. package/dist/hooks/useCursorManager.d.ts +4 -0
  10. package/dist/hooks/useCursorManager.d.ts.map +1 -0
  11. package/dist/hooks/useCursorManager.js +19 -0
  12. package/dist/hooks/useCursorManager.js.map +1 -0
  13. package/dist/hooks/useNumberFormatter.d.ts +18 -0
  14. package/dist/hooks/useNumberFormatter.d.ts.map +1 -0
  15. package/dist/hooks/useNumberFormatter.js +29 -0
  16. package/dist/hooks/useNumberFormatter.js.map +1 -0
  17. package/dist/hooks/useNumberValidation.d.ts +11 -0
  18. package/dist/hooks/useNumberValidation.d.ts.map +1 -0
  19. package/dist/hooks/useNumberValidation.js +22 -0
  20. package/dist/hooks/useNumberValidation.js.map +1 -0
  21. package/dist/hooks/useNumericInputEvents.d.ts +18 -0
  22. package/dist/hooks/useNumericInputEvents.d.ts.map +1 -0
  23. package/dist/hooks/useNumericInputEvents.js +39 -0
  24. package/dist/hooks/useNumericInputEvents.js.map +1 -0
  25. package/dist/hooks/usePersianNumberInput.d.ts +7 -2
  26. package/dist/hooks/usePersianNumberInput.d.ts.map +1 -1
  27. package/dist/hooks/usePersianNumberInput.js +66 -63
  28. package/dist/hooks/usePersianNumberInput.js.map +1 -1
  29. package/dist/test-utils.d.ts +7 -0
  30. package/dist/test-utils.d.ts.map +1 -0
  31. package/dist/test-utils.js +27 -0
  32. package/dist/test-utils.js.map +1 -0
  33. package/dist/utils/digitUtils.d.ts +4 -0
  34. package/dist/utils/digitUtils.d.ts.map +1 -1
  35. package/dist/utils/digitUtils.js +44 -17
  36. package/dist/utils/digitUtils.js.map +1 -1
  37. package/dist/utils/keyFilter.d.ts +16 -0
  38. package/dist/utils/keyFilter.d.ts.map +1 -0
  39. package/dist/utils/keyFilter.js +31 -0
  40. package/dist/utils/keyFilter.js.map +1 -0
  41. package/dist/utils/validation.d.ts +22 -0
  42. package/dist/utils/validation.d.ts.map +1 -0
  43. package/dist/utils/validation.js +85 -0
  44. package/dist/utils/validation.js.map +1 -0
  45. 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
- A lightweight, powerful React library for handling Persian (Farsi) and Arabic number inputs with automatic digit conversion, formatting, and localization support.
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
  [![npm version](https://img.shields.io/npm/v/persian-number-input.svg)](https://www.npmjs.com/package/persian-number-input)
7
8
  [![npm downloads](https://img.shields.io/npm/dm/persian-number-input.svg)](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
- Experience the component in action with our interactive demo!
16
+ ## Why Persian Number Input?
16
17
 
17
- ## 📊 Bundle Size
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 is extremely lightweight:
26
+ This library solves all of these automatically:
20
27
 
21
28
  ```
22
- persian-number-input: ~1KB (minified + gzipped)
29
+ User types: ۱۲۳۴۵۶۷
30
+ Displayed as: ۱,۲۳۴,۵۶۷
31
+ Form receives: "1234567"
23
32
  ```
24
33
 
25
- ![Bundle Size Comparison](./public/size.png)
26
-
27
34
  ---
28
35
 
29
36
  ## ✨ Features
30
37
 
31
- - 🔢 **Automatic Digit Conversion** - Seamlessly converts Persian (۰-۹) and Arabic (٠-٩) digits to English and vice versa
32
- - 🌍 **Multi-locale Support** - Built-in support for Persian (fa), Arabic (ar), and English (en)
33
- - 📊 **Number Formatting** - Automatic thousand separators with customizable characters
34
- - 💰 **Currency Ready** - Add prefixes, suffixes, and custom decimal separators
35
- - ⚡ **Lightweight** - Tiny bundle size with zero dependencies (except decimal.js for precision)
36
- - 🎯 **Type-Safe** - Full TypeScript support with complete type definitions
37
- - **Accessible** - Follows best practices for input accessibility
38
- - 🎨 **Customizable** - Extensive configuration options for any use case
39
- - 🔄 **Real-time Formatting** - Format numbers as users type with cursor position preservation
40
- - ✅ **Validation** - Built-in min/max value validation and decimal precision control
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
- **Output:** `۱,۲۳۴,۵۶۷`
82
+ Output displayed to user: `۱,۲۳۴,۵۶۷`
79
83
 
80
84
  ---
81
85
 
82
86
  ## 📚 Usage Examples
83
87
 
84
- ### Currency Input (Persian Toman)
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
- **Output:** `۵,۰۰۰,۰۰۰ تومان`
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 Numbers with Custom Separator
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
- **Output:** `۱,۲۳۴٫۵۶`
134
+ Displayed: `۱,۲۳۴٫۵۶`
115
135
 
116
136
  ---
117
137
 
118
- ### Price Input with Validation
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
- ### Arabic Locale
154
+ ### With React Hook Form
137
155
 
138
156
  ```tsx
139
- <PersianNumberInput
140
- initialValue={987654}
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
- **Output:** `٩٨٧,٦٥٤ ر.س`
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 Hook (Advanced)
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
- className="custom-input"
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 Props
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"` | Locale for digit conversion |
193
- | `separatorCount` | `number` | `3` | Number of digits between separators |
194
- | `separatorChar` | `string` | `","` | Character used for thousand separator |
195
- | `decimalChar` | `string` | Auto | Decimal separator character |
196
- | `suffix` | `string` | `undefined` | Suffix text (e.g., currency symbol) |
197
- | `maxDecimals` | `number` | `undefined` | Maximum decimal places allowed |
198
- | `min` | `number` | `undefined` | Minimum allowed value |
199
- | `max` | `number` | `undefined` | Maximum allowed value |
200
- | `showZero` | `boolean` | `false` | Show zero when value is empty |
201
- | `onValueChange` | `(value: string \| undefined) => void` | `undefined` | Callback when value changes (returns raw English digits) |
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 Functions
241
+ ### Utility functions
208
242
 
209
243
  #### `transformNumber(rawValue, options)`
210
244
 
211
- Formats a number string according to locale and options.
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
- const formatted = transformNumber("1234567.89", {
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
- Converts Persian/Arabic digits to English digits.
262
+ Convert Persian or Arabic digits to English.
230
263
 
231
264
  ```tsx
232
265
  import { toEnglishDigits } from "persian-number-input";
233
266
 
234
- console.log(toEnglishDigits("۱۲۳۴")); // "1234"
235
- console.log(toEnglishDigits("٩٨٧٦")); // "9876"
267
+ toEnglishDigits("۱۲۳۴"); // "1234"
268
+ toEnglishDigits("٩٨٧٦"); // "9876"
236
269
  ```
237
270
 
238
271
  #### `toLocalizedDigits(numStr, locale)`
239
272
 
240
- Converts English digits to localized digits.
273
+ Convert English digits to the target locale.
241
274
 
242
275
  ```tsx
243
276
  import { toLocalizedDigits } from "persian-number-input";
244
277
 
245
- console.log(toLocalizedDigits("1234", "fa")); // "۱۲۳۴"
246
- console.log(toLocalizedDigits("5678", "ar")); // "٥٦٧٨"
278
+ toLocalizedDigits("1234", "fa"); // "۱۲۳۴"
279
+ toLocalizedDigits("5678", "ar"); // "٥٦٧٨"
247
280
  ```
248
281
 
249
282
  #### `sanitizeNumericInput(value, maxDecimals?, decimalChar?)`
250
283
 
251
- Cleans and validates numeric input.
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
- console.log(sanitizeNumericInput("۱۲۳abc۴۵۶", 2)); // "123456"
257
- console.log(sanitizeNumericInput("12.345.67", 2)); // "12.34"
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
- The component accepts all standard input props, including `className` and `style`:
295
+ Remove all non-digit and non-dot characters.
265
296
 
266
297
  ```tsx
267
- <PersianNumberInput
268
- initialValue={1000}
269
- locale="fa"
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
- ### With Tailwind CSS
303
+ #### `normalizeDecimals(str)`
304
+
305
+ Keep only the first decimal point.
282
306
 
283
307
  ```tsx
284
- <PersianNumberInput
285
- initialValue={1000}
286
- locale="fa"
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
- ## 🌟 Advanced Examples
315
+ Strip leading zeros from a numeric string.
294
316
 
295
- ### Financial Calculator
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 { useState } from "react";
299
- import { PersianNumberInput } from "persian-number-input";
328
+ import { applyDecimalPrecision } from "persian-number-input";
300
329
 
301
- function LoanCalculator() {
302
- const [principal, setPrincipal] = useState<string>();
303
- const [rate, setRate] = useState<string>();
304
- const [years, setYears] = useState<string>();
330
+ applyDecimalPrecision("1234.5678", 2); // "1234.56"
331
+ ```
305
332
 
306
- const calculateMonthlyPayment = () => {
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
- return (
315
- <div className="space-y-4">
316
- <div>
317
- <label>مبلغ وام:</label>
318
- <PersianNumberInput
319
- locale="fa"
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
- ### Form Integration
345
+ ## 🎨 Styling
346
+
347
+ The component accepts all standard input props including `className` and `style`:
356
348
 
357
349
  ```tsx
358
- import { useForm, Controller } from "react-hook-form";
359
- import { PersianNumberInput } from "persian-number-input";
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
- function ProductForm() {
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
- const onSubmit = (data) => {
365
- console.log(data);
366
- };
358
+ ---
367
359
 
368
- return (
369
- <form onSubmit={handleSubmit(onSubmit)}>
370
- <Controller
371
- name="price"
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
- ## 🔍 Why Persian Number Input?
370
+ ## 🏆 Comparison
392
371
 
393
- ### The Problem
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
- Working with Persian and Arabic numerals in web applications is challenging:
384
+ ---
396
385
 
397
- - Users type in their native digits, but forms expect English digits
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
- ### The Solution
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
- Persian Number Input handles all these complexities automatically:
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
- ```tsx
407
- // User types: ۱۲۳۴۵۶۷
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
- ## 🏆 Comparison
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
- | Feature | Persian Number Input | Native Input | Other Libraries |
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! Please feel free to submit a Pull Request.
410
+ Contributions are welcome!
430
411
 
431
412
  1. Fork the repository
432
- 2. Create your feature branch (`git checkout -b feature/AmazingFeature`)
433
- 3. Commit your changes (`git commit -m 'Add some AmazingFeature'`)
434
- 4. Push to the branch (`git push origin feature/AmazingFeature`)
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
- ## 🙏 Acknowledgments
422
+ ## 📄 License
446
423
 
447
- - Built with TypeScript and React
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
- - 📧 telegram: [Javad Sharifi](https://t.me/Javad_sharifi98)
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 the Persian and Arabic developer community**
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>, "onChange" | "value" | "min" | "max">, Omit<TransformNumberOptions, "maxDecimals"> {
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,KAA0C,MAAM,OAAO,CAAC;AAE/D,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAEvE,UAAU,uBACR,SAAQ,IAAI,CACR,KAAK,CAAC,mBAAmB,CAAC,gBAAgB,CAAC,EAC3C,UAAU,GAAG,OAAO,GAAG,KAAK,GAAG,KAAK,CACrC,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;AAED,QAAA,MAAM,kBAAkB,kGAiDtB,CAAC;AAIH,eAAe,kBAAkB,CAAC"}
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"}