numora 1.0.4 → 2.0.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.md +233 -23
- package/dist/NumoraInput.d.ts +72 -0
- package/dist/config.d.ts +11 -0
- package/dist/features/compact-notation.d.ts +17 -0
- package/dist/features/decimals.d.ts +52 -0
- package/dist/features/formatting/caret-position-utils.d.ts +54 -0
- package/dist/features/formatting/change-detection.d.ts +40 -0
- package/dist/features/formatting/character-equivalence.d.ts +9 -0
- package/dist/features/formatting/constants.d.ts +29 -0
- package/dist/features/formatting/cursor-boundary.d.ts +39 -0
- package/dist/features/formatting/cursor-position.d.ts +50 -0
- package/dist/features/formatting/digit-counting.d.ts +61 -0
- package/dist/features/formatting/index.d.ts +19 -0
- package/dist/features/formatting/large-number.d.ts +39 -0
- package/dist/features/formatting/percent.d.ts +45 -0
- package/dist/features/formatting/subscript-notation.d.ts +20 -0
- package/dist/features/formatting/thousand-grouping.d.ts +34 -0
- package/dist/features/leading-zeros.d.ts +18 -0
- package/dist/features/mobile-keyboard-filtering.d.ts +18 -0
- package/dist/features/non-numeric-characters.d.ts +9 -0
- package/dist/features/sanitization.d.ts +41 -0
- package/dist/features/scientific-notation.d.ts +9 -0
- package/dist/index.d.ts +4 -4
- package/dist/index.js +1 -1
- package/dist/index.mjs +1136 -59
- package/dist/types.d.ts +34 -0
- package/dist/utils/escape-reg-exp.d.ts +16 -0
- package/dist/utils/event-handlers.d.ts +15 -14
- package/dist/utils/input-pattern.d.ts +5 -0
- package/dist/validation.d.ts +20 -0
- package/package.json +9 -9
- package/dist/NumericInput.d.ts +0 -21
- package/dist/utils/decimals.d.ts +0 -13
- package/dist/utils/sanitization.d.ts +0 -1
package/README.md
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# numora
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/numora)
|
|
4
|
+
[](https://www.npmjs.com/package/numora)
|
|
4
5
|
|
|
5
6
|
A lightweight, framework-agnostic numeric input library for handling currency and decimal inputs in **financial/DeFi** applications. Built with TypeScript and designed for modern web applications with:
|
|
6
7
|
|
|
@@ -11,63 +12,272 @@ A lightweight, framework-agnostic numeric input library for handling currency an
|
|
|
11
12
|
|
|
12
13
|
## Demo
|
|
13
14
|
|
|
14
|
-
Check out the [live demo](https://numora.
|
|
15
|
+
Check out the [live demo](https://numora.xyz/) to see Numora in action.
|
|
15
16
|
|
|
16
17
|
## Features
|
|
17
18
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
19
|
+
| Feature | Description |
|
|
20
|
+
|---------|-------------|
|
|
21
|
+
| **⭐️ Zero Dependencies** | No external dependencies, minimal bundle size |
|
|
22
|
+
| **⭐️ Framework Agnostic** | Works with any framework or vanilla JavaScript |
|
|
23
|
+
| **Decimal Precision Control** | Configure minimum and maximum decimal places (`decimalMinLength`, `decimalMaxLength`) |
|
|
24
|
+
| **Minimum Decimal Places** | Automatically pad values with zeros to ensure minimum decimal places (e.g., `"1"` with `decimalMinLength: 2` becomes `"1.00"`) |
|
|
25
|
+
| **Thousand Separators** | Customizable thousand separators with multiple grouping styles (`Thousand`, `Lakh`, `Wan`, `None`) |
|
|
26
|
+
| **Custom Decimal Separator** | Support for different decimal separators (e.g., `.` or `,`) |
|
|
27
|
+
| **Format on Blur/Change** | Choose when to apply formatting: `on blur (clean editing)` or `on change (real-time)` |
|
|
28
|
+
| **Compact Notation Expansion** | When enabled, expands compact notation during paste/setValue (e.g., `"1k"` → `"1000"`, `"1.5m"` → `"1500000"`, `"2B"` → `"2000000000"`) |
|
|
29
|
+
| **Scientific Notation Expansion** | Always automatically expands scientific notation (e.g., `"1.5e-7"` → `"0.00000015"`, `"2e+5"` → `"200000"`) |
|
|
30
|
+
| **Negative Number Support** | Optional support for negative numbers with `enableNegative` |
|
|
31
|
+
| **Leading Zeros Support** | Control leading zero behavior with `enableLeadingZeros` |
|
|
32
|
+
| **Raw Value Mode** | Get unformatted numeric values while displaying formatted values |
|
|
33
|
+
| **Paste Event Handling** | Intelligent paste handling with automatic sanitization, formatting, and cursor positioning |
|
|
34
|
+
| **Cursor Position Preservation** | Smart cursor positioning that works with thousand separators, even during formatting |
|
|
35
|
+
| **Thousand Separator Skipping** | On delete/backspace, cursor automatically skips over thousand separators for better UX |
|
|
36
|
+
| **Mobile Keyboard Optimization** | Automatic `inputmode="decimal"` for mobile numeric keyboards |
|
|
37
|
+
| **Mobile Keyboard Filtering** | Automatically filters non-breaking spaces and Unicode whitespace artifacts from mobile keyboards |
|
|
38
|
+
| **Non-numeric Character Filtering** | Automatic removal of invalid characters |
|
|
39
|
+
| **Comma/Dot Conversion** | When `thousandStyle` is `None`, typing comma or dot automatically converts to the configured `decimalSeparator` |
|
|
40
|
+
| **Character Equivalence** | Automatic conversion of commas to dots (or custom decimal separator) for easier input |
|
|
41
|
+
| **Sanitization** | Comprehensive input sanitization for security and data integrity |
|
|
42
|
+
| **TypeScript Support** | Full TypeScript definitions included |
|
|
43
|
+
|
|
44
|
+
## Display Formatting Utilities
|
|
45
|
+
|
|
46
|
+
Numora also exports utility functions for formatting numbers for display (outside of the input component):
|
|
47
|
+
|
|
48
|
+
| Utility | Description | Example |
|
|
49
|
+
|---------|-------------|---------|
|
|
50
|
+
| `formatPercent` | Format decimal values as percentages | `formatPercent("0.01", 2)` → `"1.00%"` |
|
|
51
|
+
| `formatLargePercent` | Format large percentages with scale notation (k, M, T, etc.) | `formatLargePercent("1000", 2)` → `"100000%"` |
|
|
52
|
+
| `formatLargeNumber` | Format large numbers with scale notation | `formatLargeNumber("1234")` → `"1.23k"` |
|
|
53
|
+
| `condenseDecimalZeros` | Condense leading decimal zeros to subscript notation | `condenseDecimalZeros("0.000001", 8)` → `"0₆1"` |
|
|
54
|
+
|
|
55
|
+
These utilities use string arithmetic to avoid floating-point precision issues.
|
|
56
|
+
|
|
57
|
+
## 📊 Comparison
|
|
58
|
+
|
|
59
|
+
| Feature | Numora | react-number-format | Native Number Input |
|
|
60
|
+
|---------|--------|---------------------|---------------------|
|
|
61
|
+
| **Framework Support** | ✅ All frameworks | ❌ React only | ✅ All frameworks |
|
|
62
|
+
| **Dependencies** | ✅ Zero | ⚠️ React required | ✅ Zero |
|
|
63
|
+
| **Raw Value Mode** | ✅ Yes | ⚠️ Limited | ❌ No |
|
|
64
|
+
| **Comma/Dot Conversion** | ✅ Yes | ⚠️ Limited | ❌ No |
|
|
65
|
+
| **Scientific Notation** | ✅ Auto-expand | ❌ No | ❌ No |
|
|
66
|
+
| **Display Formatting Utils** | ✅ Yes | ❌ No | ❌ No |
|
|
67
|
+
| **Compact Notation** | ✅ Auto-expand | ❌ No | ❌ No |
|
|
68
|
+
| **Mobile Support** | ✅ Yes | ✅ Yes | ⚠️ Limited |
|
|
69
|
+
| **Decimal Precision Control** | ✅ Yes | ✅ Yes | ❌ Limited |
|
|
70
|
+
| **Thousand Separators** | ✅ Customizable | ✅ Yes | ❌ No |
|
|
71
|
+
| **Custom Decimal Separator** | ✅ Yes | ✅ Yes | ❌ No (always `.`) |
|
|
72
|
+
| **Formatting Options** | ✅ Blur/Change modes | ✅ Multiple modes | ❌ No |
|
|
73
|
+
| **Cursor Preservation** | ✅ Advanced | ✅ Basic | ❌ N/A |
|
|
74
|
+
| **TypeScript Support** | ✅ Yes | ✅ Yes | ⚠️ Partial |
|
|
75
|
+
| **Paste Handling** | ✅ Intelligent | ✅ Yes | ⚠️ Basic |
|
|
76
|
+
| **Grouping Styles** | ✅ Thousand/Lakh/Wan | ✅ Thousand/Lakh/Wan | ❌ No |
|
|
77
|
+
| **Leading Zeros Control** | ✅ Yes | ✅ Yes | ❌ No |
|
|
25
78
|
|
|
26
79
|
## Installation
|
|
27
80
|
|
|
28
81
|
```bash
|
|
29
82
|
npm install numora
|
|
30
83
|
# or
|
|
31
|
-
|
|
84
|
+
bun add numora
|
|
32
85
|
# or
|
|
33
86
|
pnpm add numora
|
|
34
87
|
```
|
|
35
88
|
|
|
36
89
|
## Usage
|
|
37
90
|
|
|
91
|
+
### Basic Example
|
|
92
|
+
|
|
38
93
|
```typescript
|
|
39
|
-
import {
|
|
94
|
+
import { NumoraInput } from 'numora';
|
|
40
95
|
|
|
41
96
|
// Get the container element where you want to mount the input
|
|
42
97
|
const container = document.querySelector('#my-input-container');
|
|
43
98
|
|
|
44
|
-
// Create a new
|
|
45
|
-
const
|
|
46
|
-
|
|
99
|
+
// Create a new NumoraInput instance
|
|
100
|
+
const numoraInput = new NumoraInput(container, {
|
|
101
|
+
decimalMaxLength: 2,
|
|
47
102
|
onChange: (value) => {
|
|
48
103
|
console.log('Value changed:', value);
|
|
49
|
-
// Do something with the value
|
|
50
104
|
},
|
|
51
|
-
|
|
105
|
+
});
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### Advanced Example
|
|
109
|
+
|
|
110
|
+
```typescript
|
|
111
|
+
import { NumoraInput, FormatOn, ThousandStyle } from 'numora';
|
|
112
|
+
|
|
113
|
+
const container = document.querySelector('#my-input-container');
|
|
114
|
+
|
|
115
|
+
const numoraInput = new NumoraInput(container, {
|
|
116
|
+
// Decimal options
|
|
117
|
+
decimalMaxLength: 18,
|
|
118
|
+
decimalMinLength: 2, // Pads with zeros: "1" becomes "1.00"
|
|
119
|
+
decimalSeparator: '.',
|
|
120
|
+
|
|
121
|
+
// Thousand separator options
|
|
122
|
+
thousandSeparator: ',',
|
|
123
|
+
thousandStyle: ThousandStyle.Thousand,
|
|
124
|
+
formatOn: FormatOn.Change, // or FormatOn.Blur
|
|
125
|
+
|
|
126
|
+
// Additional features
|
|
127
|
+
enableCompactNotation: true, // Expands "1k" → "1000" on paste/setValue
|
|
128
|
+
enableNegative: false,
|
|
129
|
+
enableLeadingZeros: false,
|
|
130
|
+
rawValueMode: true, // Get unformatted values in onChange
|
|
131
|
+
|
|
132
|
+
// Standard input properties
|
|
133
|
+
placeholder: 'Enter amount',
|
|
134
|
+
className: 'numora-input',
|
|
135
|
+
|
|
136
|
+
// Event handler
|
|
137
|
+
onChange: (value) => {
|
|
138
|
+
console.log('Raw value:', value); // Unformatted if rawValueMode is true
|
|
139
|
+
console.log('Display value:', numoraInput.value); // Formatted display value
|
|
140
|
+
console.log('As number:', numoraInput.valueAsNumber); // Parsed as number
|
|
141
|
+
},
|
|
142
|
+
});
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### Compact Notation Example
|
|
146
|
+
|
|
147
|
+
```typescript
|
|
148
|
+
import { NumoraInput } from 'numora';
|
|
149
|
+
|
|
150
|
+
const container = document.querySelector('#my-input-container');
|
|
151
|
+
|
|
152
|
+
const numoraInput = new NumoraInput(container, {
|
|
153
|
+
enableCompactNotation: true,
|
|
154
|
+
onChange: (value) => {
|
|
155
|
+
console.log('Value:', value);
|
|
156
|
+
},
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
// When user pastes "1.5k" or you call setValue("1.5k")
|
|
160
|
+
// It automatically expands to "1500"
|
|
161
|
+
numoraInput.setValue('1.5k'); // Display: "1,500" (if thousand separator enabled)
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### Scientific Notation Example
|
|
165
|
+
|
|
166
|
+
```typescript
|
|
167
|
+
import { NumoraInput } from 'numora';
|
|
168
|
+
|
|
169
|
+
const container = document.querySelector('#my-input-container');
|
|
170
|
+
|
|
171
|
+
const numoraInput = new NumoraInput(container, {
|
|
172
|
+
decimalMaxLength: 18,
|
|
173
|
+
onChange: (value) => {
|
|
174
|
+
console.log('Value:', value);
|
|
175
|
+
},
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
// Scientific notation is ALWAYS automatically expanded
|
|
179
|
+
// User can paste "1.5e-7" and it becomes "0.00000015"
|
|
180
|
+
// User can paste "2e+5" and it becomes "200000"
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
### Comma/Dot Conversion Example
|
|
184
|
+
|
|
185
|
+
```typescript
|
|
186
|
+
import { NumoraInput, ThousandStyle } from 'numora';
|
|
187
|
+
|
|
188
|
+
const container = document.querySelector('#my-input-container');
|
|
189
|
+
|
|
190
|
+
const numoraInput = new NumoraInput(container, {
|
|
191
|
+
decimalSeparator: ',', // European format
|
|
192
|
+
thousandStyle: ThousandStyle.None, // Required for comma/dot conversion
|
|
193
|
+
decimalMaxLength: 2,
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
// When thousandStyle is None:
|
|
197
|
+
// - User types "." → automatically converted to ","
|
|
198
|
+
// - User types "," → automatically converted to ","
|
|
199
|
+
// This makes it easier for users without knowing the exact separator
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
### Using Display Formatting Utilities
|
|
203
|
+
|
|
204
|
+
```typescript
|
|
205
|
+
import { formatPercent, formatLargePercent, formatLargeNumber, condenseDecimalZeros } from 'numora';
|
|
206
|
+
|
|
207
|
+
// Format as percentage
|
|
208
|
+
const percent = formatPercent('0.01', 2); // "1.00%"
|
|
209
|
+
const largePercent = formatLargePercent('1000', 2); // "100000%"
|
|
210
|
+
|
|
211
|
+
// Format large numbers with scale notation
|
|
212
|
+
const large = formatLargeNumber('1234567'); // "1.23M"
|
|
213
|
+
const small = formatLargeNumber('1234'); // "1.23k"
|
|
214
|
+
|
|
215
|
+
// Condense decimal zeros
|
|
216
|
+
const condensed = condenseDecimalZeros('0.000001', 8); // "0₆1"
|
|
217
|
+
const condensed2 = condenseDecimalZeros('0.000123', 8); // "0₃123"
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
### Programmatic Value Control
|
|
221
|
+
|
|
222
|
+
```typescript
|
|
223
|
+
// Get the current value
|
|
224
|
+
const currentValue = numoraInput.getValue();
|
|
225
|
+
// or
|
|
226
|
+
const currentValue = numoraInput.value;
|
|
227
|
+
|
|
228
|
+
// Set a new value
|
|
229
|
+
numoraInput.setValue('1234.56');
|
|
230
|
+
// or
|
|
231
|
+
numoraInput.value = '1234.56';
|
|
232
|
+
|
|
233
|
+
// Set from a number
|
|
234
|
+
numoraInput.valueAsNumber = 1234.56;
|
|
235
|
+
|
|
236
|
+
// Get as a number
|
|
237
|
+
const numericValue = numoraInput.valueAsNumber;
|
|
238
|
+
|
|
239
|
+
// Enable/disable the input
|
|
240
|
+
numoraInput.disable();
|
|
241
|
+
numoraInput.enable();
|
|
242
|
+
|
|
243
|
+
// Access the underlying HTMLInputElement
|
|
244
|
+
const inputElement = numoraInput.getElement();
|
|
245
|
+
inputElement.addEventListener('focus', () => {
|
|
246
|
+
console.log('Input focused');
|
|
52
247
|
});
|
|
53
248
|
```
|
|
54
249
|
|
|
55
250
|
## Options
|
|
56
251
|
|
|
57
|
-
The
|
|
252
|
+
The NumoraInput constructor accepts the following options:
|
|
253
|
+
|
|
58
254
|
| Option | Type | Default | Description |
|
|
59
|
-
|
|
60
|
-
|
|
|
61
|
-
|
|
|
62
|
-
|
|
|
255
|
+
|--------|------|---------|-------------|
|
|
256
|
+
| `decimalMaxLength` | `number` | `2` | Maximum number of decimal places allowed |
|
|
257
|
+
| `decimalMinLength` | `number` | `0` | Minimum number of decimal places (pads with zeros) |
|
|
258
|
+
| `decimalSeparator` | `string` | `'.'` | Character used as decimal separator |
|
|
259
|
+
| `thousandSeparator` | `string` | `','` | Character used as thousand separator (set to empty string to disable) |
|
|
260
|
+
| `thousandStyle` | `ThousandStyle` | `ThousandStyle.None` | Grouping style: `Thousand`, `Lakh`, `Wan`, or `None` |
|
|
261
|
+
| `formatOn` | `FormatOn` | `FormatOn.Blur` | When to apply formatting: `Blur` or `Change` |
|
|
262
|
+
| `enableCompactNotation` | `boolean` | `false` | Enable expansion of compact notation (e.g., "1k" → "1000") on paste/setValue |
|
|
263
|
+
| `enableNegative` | `boolean` | `false` | Allow negative numbers |
|
|
264
|
+
| `enableLeadingZeros` | `boolean` | `false` | Allow leading zeros before decimal point |
|
|
265
|
+
| `rawValueMode` | `boolean` | `false` | Return unformatted values in `onChange` callback |
|
|
266
|
+
| `onChange` | `(value: string) => void` | `undefined` | Callback function that runs when the input value changes |
|
|
267
|
+
| `value` | `string` | `undefined` | Initial value (controlled mode) |
|
|
268
|
+
| `defaultValue` | `string` | `undefined` | Initial default value (uncontrolled mode) |
|
|
269
|
+
|
|
270
|
+
All standard HTMLInputElement properties are also supported (e.g., `placeholder`, `className`, `disabled`, `id`, etc.).
|
|
271
|
+
|
|
272
|
+
**Note:** Scientific notation expansion (e.g., `"1.5e-7"` → `"0.00000015"`) always happens automatically and is not configurable.
|
|
63
273
|
|
|
64
274
|
## Framework Adapters
|
|
65
275
|
|
|
66
276
|
Numora is also available for popular frameworks:
|
|
67
277
|
|
|
68
|
-
- React
|
|
69
|
-
- Vue
|
|
70
|
-
- Svelte
|
|
278
|
+
- **React**: [`numora-react`](../react/README.md) - React component wrapper
|
|
279
|
+
- **Vue**: Coming soon
|
|
280
|
+
- **Svelte**: Use the core package directly
|
|
71
281
|
|
|
72
282
|
## License
|
|
73
283
|
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { FormatOn, ThousandStyle } from './types';
|
|
2
|
+
export interface NumoraInputOptions extends Partial<Omit<HTMLInputElement, 'value' | 'defaultValue' | 'onchange'>> {
|
|
3
|
+
formatOn?: FormatOn;
|
|
4
|
+
thousandSeparator?: string;
|
|
5
|
+
thousandStyle?: ThousandStyle;
|
|
6
|
+
decimalSeparator?: string;
|
|
7
|
+
decimalMaxLength?: number;
|
|
8
|
+
decimalMinLength?: number;
|
|
9
|
+
enableCompactNotation?: boolean;
|
|
10
|
+
enableNegative?: boolean;
|
|
11
|
+
enableLeadingZeros?: boolean;
|
|
12
|
+
rawValueMode?: boolean;
|
|
13
|
+
onChange?: (value: string) => void;
|
|
14
|
+
value?: string;
|
|
15
|
+
defaultValue?: string;
|
|
16
|
+
}
|
|
17
|
+
export declare class NumoraInput {
|
|
18
|
+
private element;
|
|
19
|
+
private options;
|
|
20
|
+
private resolvedOptions;
|
|
21
|
+
private rawValue;
|
|
22
|
+
private caretPositionBeforeChange?;
|
|
23
|
+
constructor(container: HTMLElement, { decimalMaxLength, decimalMinLength, formatOn, thousandSeparator, thousandStyle, decimalSeparator, enableCompactNotation, enableNegative, enableLeadingZeros, rawValueMode, onChange, ...rest }: NumoraInputOptions);
|
|
24
|
+
private createInputElement;
|
|
25
|
+
private setupEventListeners;
|
|
26
|
+
private getResolvedOptions;
|
|
27
|
+
private buildFormattingOptions;
|
|
28
|
+
private handleValueChange;
|
|
29
|
+
private formatValueForDisplay;
|
|
30
|
+
private handleChange;
|
|
31
|
+
private handleKeyDown;
|
|
32
|
+
private handlePaste;
|
|
33
|
+
private handleFocus;
|
|
34
|
+
private handleBlur;
|
|
35
|
+
/**
|
|
36
|
+
* Extracts and stores the raw numeric value from a formatted value.
|
|
37
|
+
* Gets the raw value from the data attribute set by event handlers, or extracts it from formatted value.
|
|
38
|
+
*/
|
|
39
|
+
private updateRawValue;
|
|
40
|
+
getValue(): string;
|
|
41
|
+
setValue(value: string): void;
|
|
42
|
+
disable(): void;
|
|
43
|
+
enable(): void;
|
|
44
|
+
addEventListener(event: string, callback: EventListenerOrEventListenerObject): void;
|
|
45
|
+
removeEventListener(event: string, callback: EventListenerOrEventListenerObject): void;
|
|
46
|
+
/**
|
|
47
|
+
* Returns the underlying HTMLInputElement for direct access.
|
|
48
|
+
* This allows users to interact with the input as a normal HTMLInputElement.
|
|
49
|
+
*/
|
|
50
|
+
getElement(): HTMLInputElement;
|
|
51
|
+
/**
|
|
52
|
+
* Gets the current value of the input.
|
|
53
|
+
* In rawValueMode, returns the raw numeric value without formatting.
|
|
54
|
+
* Otherwise, returns the formatted display value.
|
|
55
|
+
*/
|
|
56
|
+
get value(): string;
|
|
57
|
+
/**
|
|
58
|
+
* Sets the value of the input.
|
|
59
|
+
* In rawValueMode, the value will be formatted for display.
|
|
60
|
+
* Otherwise, sets the value directly.
|
|
61
|
+
*/
|
|
62
|
+
set value(val: string);
|
|
63
|
+
/**
|
|
64
|
+
* Gets the value as a number, similar to HTMLInputElement.valueAsNumber.
|
|
65
|
+
* Returns NaN if the value cannot be converted to a number.
|
|
66
|
+
*/
|
|
67
|
+
get valueAsNumber(): number;
|
|
68
|
+
/**
|
|
69
|
+
* Sets the value from a number, similar to HTMLInputElement.valueAsNumber.
|
|
70
|
+
*/
|
|
71
|
+
set valueAsNumber(num: number);
|
|
72
|
+
}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { FormatOn, ThousandStyle } from "./types";
|
|
2
|
+
export declare const DEFAULT_DECIMAL_MAX_LENGTH = 2;
|
|
3
|
+
export declare const DEFAULT_DECIMAL_MIN_LENGTH = 0;
|
|
4
|
+
export declare const DEFAULT_FORMAT_ON = FormatOn.Blur;
|
|
5
|
+
export declare const DEFAULT_THOUSAND_SEPARATOR = ",";
|
|
6
|
+
export declare const DEFAULT_THOUSAND_STYLE = ThousandStyle.None;
|
|
7
|
+
export declare const DEFAULT_DECIMAL_SEPARATOR = ".";
|
|
8
|
+
export declare const DEFAULT_ENABLE_COMPACT_NOTATION = false;
|
|
9
|
+
export declare const DEFAULT_ENABLE_NEGATIVE = false;
|
|
10
|
+
export declare const DEFAULT_ENABLE_LEADING_ZEROS = false;
|
|
11
|
+
export declare const DEFAULT_RAW_VALUE_MODE = false;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Expands compact notation (k, m, b, M, T, Qa, Qi, Sx, Sp, O, N) to full numbers using string manipulation.
|
|
3
|
+
* Handles formats like: 1k, 1.5m, 2B, 1M, 2.5T, 3Qa (case-insensitive)
|
|
4
|
+
* Uses string arithmetic to avoid precision loss with large numbers.
|
|
5
|
+
*
|
|
6
|
+
* @param value - The string value that may contain compact notation
|
|
7
|
+
* @returns The expanded numeric string
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* expandCompactNotation("1k") // "1000"
|
|
11
|
+
* expandCompactNotation("1.5m") // "1500000"
|
|
12
|
+
* expandCompactNotation("2B") // "2000000000"
|
|
13
|
+
* expandCompactNotation("1M") // "1000000"
|
|
14
|
+
* expandCompactNotation("2.5T") // "2500000000000"
|
|
15
|
+
* expandCompactNotation("0.5k") // "500"
|
|
16
|
+
*/
|
|
17
|
+
export declare function expandCompactNotation(value: string): string;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { SeparatorOptions, Separators, FormattingOptions } from '@/types';
|
|
2
|
+
/**
|
|
3
|
+
* Normalizes separator configuration with defaults.
|
|
4
|
+
*
|
|
5
|
+
* @param options - Separator configuration options
|
|
6
|
+
* @returns Normalized separator configuration
|
|
7
|
+
*/
|
|
8
|
+
export declare function getSeparators(options: SeparatorOptions | FormattingOptions | undefined): Separators;
|
|
9
|
+
/**
|
|
10
|
+
* Converts comma or dot to the configured decimal separator when thousandStyle is None/undefined.
|
|
11
|
+
* This makes it easier for users to type decimal separators without knowing the exact separator character.
|
|
12
|
+
*
|
|
13
|
+
* @param e - The keyboard event
|
|
14
|
+
* @param inputElement - The input element
|
|
15
|
+
* @param formattingOptions - Optional formatting options
|
|
16
|
+
* @param separators - The separator configuration
|
|
17
|
+
* @returns True if the conversion was handled (event should be prevented), false otherwise
|
|
18
|
+
*/
|
|
19
|
+
export declare function convertCommaOrDotToDecimalSeparatorAndPreventMultimpleDecimalSeparators(e: KeyboardEvent, inputElement: HTMLInputElement, formattingOptions: FormattingOptions | undefined, decimalSeparator: string): boolean;
|
|
20
|
+
/**
|
|
21
|
+
* Trims a string representation of a number to a maximum number of decimal places.
|
|
22
|
+
*
|
|
23
|
+
* @param value - The string to trim.
|
|
24
|
+
* @param decimalMaxLength - The maximum number of decimal places to allow.
|
|
25
|
+
* @param decimalSeparator - The decimal separator character to use.
|
|
26
|
+
* @returns The trimmed string.
|
|
27
|
+
*/
|
|
28
|
+
export declare const trimToDecimalMaxLength: (value: string, decimalMaxLength: number, decimalSeparator?: string) => string;
|
|
29
|
+
/**
|
|
30
|
+
* Removes extra decimal separators, keeping only the first one.
|
|
31
|
+
*
|
|
32
|
+
* @param value - The string value
|
|
33
|
+
* @param decimalSeparator - The decimal separator character
|
|
34
|
+
* @returns The string with only the first decimal separator
|
|
35
|
+
*/
|
|
36
|
+
export declare const removeExtraDecimalSeparators: (value: string, decimalSeparator?: string) => string;
|
|
37
|
+
/**
|
|
38
|
+
* Ensures a numeric string has at least the specified minimum number of decimal places.
|
|
39
|
+
* Pads with zeros if needed, but does not truncate if more decimals exist.
|
|
40
|
+
*
|
|
41
|
+
* @param value - The string value to ensure minimum decimals for
|
|
42
|
+
* @param minDecimals - The minimum number of decimal places (default: 0, meaning no minimum)
|
|
43
|
+
* @param decimalSeparator - The decimal separator character (default: '.')
|
|
44
|
+
* @returns The string with at least minDecimals decimal places
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* ensureMinDecimals("1", 2, ".") // "1.00"
|
|
48
|
+
* ensureMinDecimals("1.5", 2, ".") // "1.50"
|
|
49
|
+
* ensureMinDecimals("1.123", 2, ".") // "1.123" (doesn't truncate)
|
|
50
|
+
* ensureMinDecimals("1", 0, ".") // "1" (no minimum)
|
|
51
|
+
*/
|
|
52
|
+
export declare const ensureMinDecimals: (value: string, minDecimals?: number, decimalSeparator?: string) => string;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utility functions for setting and managing caret position.
|
|
3
|
+
* Includes mobile browser workarounds and retry mechanisms.
|
|
4
|
+
*/
|
|
5
|
+
import type { FormattingOptions, CaretPositionInfo, Separators } from '@/types';
|
|
6
|
+
/**
|
|
7
|
+
* Sets the caret position in an input element.
|
|
8
|
+
* Includes workaround for Chrome/Safari mobile browser bugs.
|
|
9
|
+
*
|
|
10
|
+
* @param el - The input element
|
|
11
|
+
* @param caretPos - The desired caret position
|
|
12
|
+
* @returns True if successful, false otherwise
|
|
13
|
+
*/
|
|
14
|
+
export declare function setCaretPosition(el: HTMLInputElement, caretPos: number): boolean;
|
|
15
|
+
/**
|
|
16
|
+
* Sets caret position with retry mechanism for mobile browsers.
|
|
17
|
+
* Mobile Chrome sometimes resets the caret position after we set it,
|
|
18
|
+
* so we retry after a short timeout.
|
|
19
|
+
*
|
|
20
|
+
* @param el - The input element
|
|
21
|
+
* @param caretPos - The desired caret position
|
|
22
|
+
* @param currentValue - The current input value (for validation)
|
|
23
|
+
* @returns Timeout ID that can be cleared if needed
|
|
24
|
+
*/
|
|
25
|
+
export declare function setCaretPositionWithRetry(el: HTMLInputElement, caretPos: number, currentValue: string): ReturnType<typeof setTimeout> | null;
|
|
26
|
+
/**
|
|
27
|
+
* Gets the current caret position from an input element.
|
|
28
|
+
* Uses max of selectionStart and selectionEnd to handle mobile browser quirks.
|
|
29
|
+
*
|
|
30
|
+
* @param el - The input element
|
|
31
|
+
* @returns The current caret position
|
|
32
|
+
*/
|
|
33
|
+
export declare function getInputCaretPosition(el: HTMLInputElement): number;
|
|
34
|
+
/**
|
|
35
|
+
* Skips cursor over thousand separator when deleting/backspacing in 'change' mode.
|
|
36
|
+
* This prevents the cursor from stopping on the separator, making deletion smoother.
|
|
37
|
+
*
|
|
38
|
+
* @param e - The keyboard event
|
|
39
|
+
* @param inputElement - The input element
|
|
40
|
+
* @param formattingOptions - Optional formatting options
|
|
41
|
+
*/
|
|
42
|
+
export declare function skipOverThousandSeparatorOnDelete(e: KeyboardEvent, inputElement: HTMLInputElement, formattingOptions?: FormattingOptions): void;
|
|
43
|
+
/**
|
|
44
|
+
* Updates cursor position after value changes, handling both formatted and unformatted values.
|
|
45
|
+
*
|
|
46
|
+
* @param target - The input element
|
|
47
|
+
* @param oldValue - The value before the change
|
|
48
|
+
* @param newValue - The value after the change
|
|
49
|
+
* @param oldCursorPosition - The cursor position before the change
|
|
50
|
+
* @param caretPositionBeforeChange - Optional caret position info from keydown handler
|
|
51
|
+
* @param separators - Separator configuration
|
|
52
|
+
* @param formattingOptions - Optional formatting options
|
|
53
|
+
*/
|
|
54
|
+
export declare function updateCursorPosition(target: HTMLInputElement, oldValue: string, newValue: string, oldCursorPosition: number, caretPositionBeforeChange: CaretPositionInfo | undefined, separators: Separators, formattingOptions?: FormattingOptions): void;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utilities for detecting changes in input values.
|
|
3
|
+
* Used to distinguish between different editing operations (Delete vs Backspace, etc.)
|
|
4
|
+
*/
|
|
5
|
+
import type { ChangeRange } from './constants';
|
|
6
|
+
/**
|
|
7
|
+
* Determines what changed based on caret positions before and after the change.
|
|
8
|
+
* This is used to distinguish Delete (cursor stays) vs Backspace (cursor moves left).
|
|
9
|
+
*
|
|
10
|
+
* @param caretBefore - The caret position info before the change
|
|
11
|
+
* @param inputValueBefore - The input value before the change
|
|
12
|
+
* @param inputValueAfter - The input value after the change
|
|
13
|
+
* @returns Change range information, or undefined if unable to determine
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* // Delete key: cursor at 2, endOffset: 1
|
|
17
|
+
* findChangedRangeFromCaretPositions(
|
|
18
|
+
* { selectionStart: 2, selectionEnd: 2, endOffset: 1 },
|
|
19
|
+
* "1,234",
|
|
20
|
+
* "1,34"
|
|
21
|
+
* ) // Returns: { start: 2, end: 3, deletedLength: 1, isDelete: true }
|
|
22
|
+
*/
|
|
23
|
+
export declare function findChangedRangeFromCaretPositions(caretBefore: {
|
|
24
|
+
selectionStart: number;
|
|
25
|
+
selectionEnd: number;
|
|
26
|
+
endOffset?: number;
|
|
27
|
+
}, inputValueBefore: string, inputValueAfter: string): ChangeRange | undefined;
|
|
28
|
+
/**
|
|
29
|
+
* Finds the change range by comparing old and new values.
|
|
30
|
+
* This is a fallback when caret position info is not available.
|
|
31
|
+
*
|
|
32
|
+
* @param oldValue - The value before the change
|
|
33
|
+
* @param newValue - The value after the change
|
|
34
|
+
* @returns Change range information, or undefined if unable to determine
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* findChangeRange("1,234", "1,34")
|
|
38
|
+
* // Returns: { start: 2, end: 3, deletedLength: 1, isDelete: true }
|
|
39
|
+
*/
|
|
40
|
+
export declare function findChangeRange(oldValue: string, newValue: string): ChangeRange | undefined;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Character equivalence utilities for cursor position calculation.
|
|
3
|
+
*/
|
|
4
|
+
import type { IsCharacterEquivalent } from './cursor-position';
|
|
5
|
+
/**
|
|
6
|
+
* Default character equivalence function.
|
|
7
|
+
* Only considers identical characters as equivalent.
|
|
8
|
+
*/
|
|
9
|
+
export declare const defaultIsCharacterEquivalent: IsCharacterEquivalent;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration for different grouping styles used in number formatting.
|
|
3
|
+
*
|
|
4
|
+
* - thousand: Groups by 3 digits (Western style) - 1,234,567
|
|
5
|
+
* - lakh: First group of 3, then groups of 2 (Indian style) - 12,34,567
|
|
6
|
+
* - wan: Groups by 4 digits (Chinese style) - 123,4567
|
|
7
|
+
*/
|
|
8
|
+
export declare const GROUPING_CONFIG: {
|
|
9
|
+
readonly thousand: {
|
|
10
|
+
readonly size: 3;
|
|
11
|
+
};
|
|
12
|
+
readonly lakh: {
|
|
13
|
+
readonly firstGroup: 3;
|
|
14
|
+
readonly restGroup: 2;
|
|
15
|
+
};
|
|
16
|
+
readonly wan: {
|
|
17
|
+
readonly size: 4;
|
|
18
|
+
};
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Interface representing a change range in the input value.
|
|
22
|
+
* Used to distinguish between Delete and Backspace operations.
|
|
23
|
+
*/
|
|
24
|
+
export interface ChangeRange {
|
|
25
|
+
start: number;
|
|
26
|
+
end: number;
|
|
27
|
+
deletedLength: number;
|
|
28
|
+
isDelete?: boolean;
|
|
29
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Caret boundary system for defining editable positions in formatted numeric inputs.
|
|
3
|
+
* Prevents cursor from being placed in non-editable areas (separators, prefix, suffix).
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Determines which positions in a formatted value are editable.
|
|
7
|
+
* Returns a boolean array where true = editable position, false = non-editable.
|
|
8
|
+
*
|
|
9
|
+
* @param formattedValue - The formatted string value
|
|
10
|
+
* @param options - Configuration options
|
|
11
|
+
* @returns Boolean array indicating editable positions (length = formattedValue.length + 1)
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* getCaretBoundary("1,234.56", { thousandSeparator: ",", decimalSeparator: "." })
|
|
15
|
+
* // Returns: [true, true, false, true, true, true, false, true, true, ...]
|
|
16
|
+
* // (editable at positions 0,1,3,4,5,7,8,...)
|
|
17
|
+
*/
|
|
18
|
+
export declare function getCaretBoundary(formattedValue: string, options?: {
|
|
19
|
+
thousandSeparator?: string;
|
|
20
|
+
decimalSeparator?: string;
|
|
21
|
+
prefix?: string;
|
|
22
|
+
suffix?: string;
|
|
23
|
+
}): boolean[];
|
|
24
|
+
/**
|
|
25
|
+
* Corrects caret position to be within editable boundaries.
|
|
26
|
+
* Moves cursor to nearest editable position if current position is non-editable.
|
|
27
|
+
*
|
|
28
|
+
* @param value - The formatted string value
|
|
29
|
+
* @param caretPos - The current caret position
|
|
30
|
+
* @param boundary - The boundary array from getCaretBoundary()
|
|
31
|
+
* @param direction - Optional direction to search ('left' or 'right')
|
|
32
|
+
* @returns Corrected caret position within editable area
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* const boundary = getCaretBoundary("1,234", { thousandSeparator: "," });
|
|
36
|
+
* getCaretPosInBoundary("1,234", 1, boundary, 'right')
|
|
37
|
+
* // Returns: 2 (moves from separator position to next digit)
|
|
38
|
+
*/
|
|
39
|
+
export declare function getCaretPosInBoundary(value: string, caretPos: number, boundary: boolean[], direction?: 'left' | 'right'): number;
|