raqam 0.2.2 → 0.2.4

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 (46) hide show
  1. package/README.md +3 -4
  2. package/dist/{chunk-IG7CVIA2.js → chunk-4AMLNJEK.js} +3 -3
  3. package/dist/{chunk-IG7CVIA2.js.map → chunk-4AMLNJEK.js.map} +1 -1
  4. package/dist/{chunk-NTROGAES.js → chunk-7IEJOMYT.js} +3 -3
  5. package/dist/{chunk-NTROGAES.js.map → chunk-7IEJOMYT.js.map} +1 -1
  6. package/dist/{chunk-VOBTYII2.js → chunk-HPUEM7JB.js} +3 -3
  7. package/dist/{chunk-VOBTYII2.js.map → chunk-HPUEM7JB.js.map} +1 -1
  8. package/dist/{chunk-WTS5RY7S.js → chunk-OLLREWX6.js} +3 -3
  9. package/dist/{chunk-WTS5RY7S.js.map → chunk-OLLREWX6.js.map} +1 -1
  10. package/dist/{chunk-NSFX2EAT.js → chunk-RP3DVBGJ.js} +3 -3
  11. package/dist/{chunk-NSFX2EAT.js.map → chunk-RP3DVBGJ.js.map} +1 -1
  12. package/dist/{chunk-NBAZIJ5W.js → chunk-TISOJTKH.js} +2 -2
  13. package/dist/{chunk-NBAZIJ5W.js.map → chunk-TISOJTKH.js.map} +1 -1
  14. package/dist/core.cjs +1 -1
  15. package/dist/core.cjs.map +1 -1
  16. package/dist/core.d.cts +6 -2
  17. package/dist/core.d.ts +6 -2
  18. package/dist/core.js +1 -1
  19. package/dist/core.js.map +1 -1
  20. package/dist/{index-B8X3-9h1.d.cts → index-CJrJACqc.d.cts} +6 -2
  21. package/dist/{index-B8X3-9h1.d.ts → index-CJrJACqc.d.ts} +6 -2
  22. package/dist/index.cjs +1 -1
  23. package/dist/index.cjs.map +1 -1
  24. package/dist/index.d.cts +2 -2
  25. package/dist/index.d.ts +2 -2
  26. package/dist/index.js +1 -1
  27. package/dist/index.js.map +1 -1
  28. package/dist/locales/ar.cjs.map +1 -1
  29. package/dist/locales/ar.js +2 -2
  30. package/dist/locales/bn.cjs.map +1 -1
  31. package/dist/locales/bn.js +2 -2
  32. package/dist/locales/fa.cjs.map +1 -1
  33. package/dist/locales/fa.js +2 -2
  34. package/dist/locales/hi.cjs.map +1 -1
  35. package/dist/locales/hi.js +2 -2
  36. package/dist/locales/index.cjs.map +1 -1
  37. package/dist/locales/index.js +6 -6
  38. package/dist/locales/th.cjs.map +1 -1
  39. package/dist/locales/th.js +2 -2
  40. package/dist/react.cjs +1 -1
  41. package/dist/react.cjs.map +1 -1
  42. package/dist/react.d.cts +1 -1
  43. package/dist/react.d.ts +1 -1
  44. package/dist/react.js +1 -1
  45. package/dist/react.js.map +1 -1
  46. package/package.json +3 -2
package/README.md CHANGED
@@ -254,8 +254,8 @@ State management hook — returns `NumberFieldState`.
254
254
  | Prop | Type | Default | Description |
255
255
  |------|------|---------|-------------|
256
256
  | `value` | `number \| null` | — | Controlled value |
257
- | `defaultValue` | `number` | — | Uncontrolled default |
258
- | `onChange` | `(value: number \| null) => void` | — | Fires on every change |
257
+ | `defaultValue` | `number \| null` | — | Uncontrolled default |
258
+ | `onChange` | `(value: number \| null) => void` | — | Fires whenever the parsed numeric value changes |
259
259
  | `onRawChange` | `(raw: string \| null) => void` | — | Fires with raw unformatted string |
260
260
  | `locale` | `string` | browser | BCP 47 locale tag |
261
261
  | `formatOptions` | `Intl.NumberFormatOptions` | `{}` | Full Intl options |
@@ -288,14 +288,13 @@ Additional props beyond state options:
288
288
  | `stepHoldDelay` | `number` | `400` | Press-and-hold initial delay (ms) |
289
289
  | `stepHoldInterval` | `number` | `200` | Press-and-hold repeat interval (ms) |
290
290
  | `formatValue` | `(value: number) => string` | — | Custom format function |
291
- | `parseValue` | `(input: string) => ParseResult` | — | Custom parse function |
291
+ | `parseValue` | `(input: string) => { value: number \| null; isIntermediate: boolean }` | — | Custom parse function |
292
292
 
293
293
  ### `NumberField.Root` extra props
294
294
 
295
295
  | Prop | Type | Description |
296
296
  |------|------|-------------|
297
297
  | `onValueChange` | `(value, { reason, formattedValue }) => void` | Fires with change reason |
298
- | `onValueCommitted` | `(value, { reason }) => void` | Fires only on blur/Enter |
299
298
 
300
299
  ### `useNumberFieldFormat(value, options)`
301
300
 
@@ -1,4 +1,4 @@
1
- import { registerLocale } from './chunk-NBAZIJ5W.js';
1
+ import { registerLocale } from './chunk-TISOJTKH.js';
2
2
 
3
3
  // src/locales/ar.ts
4
4
  registerLocale({
@@ -10,5 +10,5 @@ registerLocale({
10
10
  var LOCALE_CODES = ["ar", "ar-EG", "ar-SA", "ar-MA", "ar-DZ", "ar-TN"];
11
11
 
12
12
  export { LOCALE_CODES };
13
- //# sourceMappingURL=chunk-IG7CVIA2.js.map
14
- //# sourceMappingURL=chunk-IG7CVIA2.js.map
13
+ //# sourceMappingURL=chunk-4AMLNJEK.js.map
14
+ //# sourceMappingURL=chunk-4AMLNJEK.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/locales/ar.ts"],"names":[],"mappings":";;;AAUA,cAAA,CAAe;AAAA,EACb,WAAA,EAAa;AAAA,IACX,CAAC,MAAQ,IAAM;AAAA;AAAA;AAEnB,CAAC,CAAA;AAGM,IAAM,eAAe,CAAC,IAAA,EAAM,SAAS,OAAA,EAAS,OAAA,EAAS,SAAS,OAAO","file":"chunk-IG7CVIA2.js","sourcesContent":["/**\n * Arabic (ar) locale plugin.\n *\n * Registers Arabic-Indic digit block (U+0660–U+0669).\n *\n * Usage:\n * import 'raqam/locales/ar'\n */\nimport { registerLocale } from \"../core/normalizer.js\";\n\nregisterLocale({\n digitBlocks: [\n [0x0660, 0x0669], // Arabic-Indic ٠–٩\n ],\n});\n\n/** BCP 47 locale tags that this plugin covers. */\nexport const LOCALE_CODES = [\"ar\", \"ar-EG\", \"ar-SA\", \"ar-MA\", \"ar-DZ\", \"ar-TN\"] as const;\n"]}
1
+ {"version":3,"sources":["../src/locales/ar.ts"],"names":[],"mappings":";;;AAUA,cAAA,CAAe;AAAA,EACb,WAAA,EAAa;AAAA,IACX,CAAC,MAAQ,IAAM;AAAA;AAAA;AAEnB,CAAC,CAAA;AAGM,IAAM,eAAe,CAAC,IAAA,EAAM,SAAS,OAAA,EAAS,OAAA,EAAS,SAAS,OAAO","file":"chunk-4AMLNJEK.js","sourcesContent":["/**\n * Arabic (ar) locale plugin.\n *\n * Registers Arabic-Indic digit block (U+0660–U+0669).\n *\n * Usage:\n * import 'raqam/locales/ar'\n */\nimport { registerLocale } from \"../core/normalizer.js\";\n\nregisterLocale({\n digitBlocks: [\n [0x0660, 0x0669], // Arabic-Indic ٠–٩\n ],\n});\n\n/** BCP 47 locale tags that this plugin covers. */\nexport const LOCALE_CODES = [\"ar\", \"ar-EG\", \"ar-SA\", \"ar-MA\", \"ar-DZ\", \"ar-TN\"] as const;\n"]}
@@ -1,4 +1,4 @@
1
- import { registerLocale } from './chunk-NBAZIJ5W.js';
1
+ import { registerLocale } from './chunk-TISOJTKH.js';
2
2
 
3
3
  // src/locales/bn.ts
4
4
  registerLocale({
@@ -10,5 +10,5 @@ registerLocale({
10
10
  var LOCALE_CODES = ["bn", "bn-BD", "bn-IN"];
11
11
 
12
12
  export { LOCALE_CODES };
13
- //# sourceMappingURL=chunk-NTROGAES.js.map
14
- //# sourceMappingURL=chunk-NTROGAES.js.map
13
+ //# sourceMappingURL=chunk-7IEJOMYT.js.map
14
+ //# sourceMappingURL=chunk-7IEJOMYT.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/locales/bn.ts"],"names":[],"mappings":";;;AAUA,cAAA,CAAe;AAAA,EACb,WAAA,EAAa;AAAA,IACX,CAAC,MAAQ,IAAM;AAAA;AAAA;AAEnB,CAAC,CAAA;AAGM,IAAM,YAAA,GAAe,CAAC,IAAA,EAAM,OAAA,EAAS,OAAO","file":"chunk-NTROGAES.js","sourcesContent":["/**\n * Bengali (bn-BD / bn-IN) locale plugin.\n *\n * Registers Bengali digit block (U+09E6–U+09EF).\n *\n * Usage:\n * import 'raqam/locales/bn'\n */\nimport { registerLocale } from \"../core/normalizer.js\";\n\nregisterLocale({\n digitBlocks: [\n [0x09e6, 0x09ef], // Bengali ০–৯\n ],\n});\n\n/** BCP 47 locale tags that this plugin covers. */\nexport const LOCALE_CODES = [\"bn\", \"bn-BD\", \"bn-IN\"] as const;\n"]}
1
+ {"version":3,"sources":["../src/locales/bn.ts"],"names":[],"mappings":";;;AAUA,cAAA,CAAe;AAAA,EACb,WAAA,EAAa;AAAA,IACX,CAAC,MAAQ,IAAM;AAAA;AAAA;AAEnB,CAAC,CAAA;AAGM,IAAM,YAAA,GAAe,CAAC,IAAA,EAAM,OAAA,EAAS,OAAO","file":"chunk-7IEJOMYT.js","sourcesContent":["/**\n * Bengali (bn-BD / bn-IN) locale plugin.\n *\n * Registers Bengali digit block (U+09E6–U+09EF).\n *\n * Usage:\n * import 'raqam/locales/bn'\n */\nimport { registerLocale } from \"../core/normalizer.js\";\n\nregisterLocale({\n digitBlocks: [\n [0x09e6, 0x09ef], // Bengali ০–৯\n ],\n});\n\n/** BCP 47 locale tags that this plugin covers. */\nexport const LOCALE_CODES = [\"bn\", \"bn-BD\", \"bn-IN\"] as const;\n"]}
@@ -1,4 +1,4 @@
1
- import { registerLocale } from './chunk-NBAZIJ5W.js';
1
+ import { registerLocale } from './chunk-TISOJTKH.js';
2
2
 
3
3
  // src/locales/th.ts
4
4
  registerLocale({
@@ -10,5 +10,5 @@ registerLocale({
10
10
  var LOCALE_CODES = ["th", "th-TH"];
11
11
 
12
12
  export { LOCALE_CODES };
13
- //# sourceMappingURL=chunk-VOBTYII2.js.map
14
- //# sourceMappingURL=chunk-VOBTYII2.js.map
13
+ //# sourceMappingURL=chunk-HPUEM7JB.js.map
14
+ //# sourceMappingURL=chunk-HPUEM7JB.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/locales/th.ts"],"names":[],"mappings":";;;AAUA,cAAA,CAAe;AAAA,EACb,WAAA,EAAa;AAAA,IACX,CAAC,MAAQ,IAAM;AAAA;AAAA;AAEnB,CAAC,CAAA;AAGM,IAAM,YAAA,GAAe,CAAC,IAAA,EAAM,OAAO","file":"chunk-VOBTYII2.js","sourcesContent":["/**\n * Thai (th-TH) locale plugin.\n *\n * Registers Thai digit block (U+0E50–U+0E59).\n *\n * Usage:\n * import 'raqam/locales/th'\n */\nimport { registerLocale } from \"../core/normalizer.js\";\n\nregisterLocale({\n digitBlocks: [\n [0x0e50, 0x0e59], // Thai ๐–๙\n ],\n});\n\n/** BCP 47 locale tags that this plugin covers. */\nexport const LOCALE_CODES = [\"th\", \"th-TH\"] as const;\n"]}
1
+ {"version":3,"sources":["../src/locales/th.ts"],"names":[],"mappings":";;;AAUA,cAAA,CAAe;AAAA,EACb,WAAA,EAAa;AAAA,IACX,CAAC,MAAQ,IAAM;AAAA;AAAA;AAEnB,CAAC,CAAA;AAGM,IAAM,YAAA,GAAe,CAAC,IAAA,EAAM,OAAO","file":"chunk-HPUEM7JB.js","sourcesContent":["/**\n * Thai (th-TH) locale plugin.\n *\n * Registers Thai digit block (U+0E50–U+0E59).\n *\n * Usage:\n * import 'raqam/locales/th'\n */\nimport { registerLocale } from \"../core/normalizer.js\";\n\nregisterLocale({\n digitBlocks: [\n [0x0e50, 0x0e59], // Thai ๐–๙\n ],\n});\n\n/** BCP 47 locale tags that this plugin covers. */\nexport const LOCALE_CODES = [\"th\", \"th-TH\"] as const;\n"]}
@@ -1,4 +1,4 @@
1
- import { registerLocale } from './chunk-NBAZIJ5W.js';
1
+ import { registerLocale } from './chunk-TISOJTKH.js';
2
2
 
3
3
  // src/locales/fa.ts
4
4
  registerLocale({
@@ -12,5 +12,5 @@ registerLocale({
12
12
  var LOCALE_CODES = ["fa", "fa-IR", "fa-AF"];
13
13
 
14
14
  export { LOCALE_CODES };
15
- //# sourceMappingURL=chunk-WTS5RY7S.js.map
16
- //# sourceMappingURL=chunk-WTS5RY7S.js.map
15
+ //# sourceMappingURL=chunk-OLLREWX6.js.map
16
+ //# sourceMappingURL=chunk-OLLREWX6.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/locales/fa.ts"],"names":[],"mappings":";;;AAeA,cAAA,CAAe;AAAA,EACb,WAAA,EAAa;AAAA,IACX,CAAC,MAAQ,IAAM,CAAA;AAAA;AAAA,IACf,CAAC,MAAQ,IAAM;AAAA;AAAA;AAEnB,CAAC,CAAA;AAGM,IAAM,YAAA,GAAe,CAAC,IAAA,EAAM,OAAA,EAAS,OAAO","file":"chunk-WTS5RY7S.js","sourcesContent":["/**\n * Persian (fa-IR) locale plugin.\n *\n * Registers Extended Arabic-Indic digit block (U+06F0–U+06F9).\n * The ISIRI 9147 keyboard produces these codepoints when number keys are\n * pressed on a Persian layout. This side-effect import enables their\n * normalization to ASCII 0–9 before parsing.\n *\n * Also registers the Arabic-Indic block for mixed-input scenarios.\n *\n * Usage:\n * import 'raqam/locales/fa'\n */\nimport { registerLocale } from \"../core/normalizer.js\";\n\nregisterLocale({\n digitBlocks: [\n [0x06f0, 0x06f9], // Extended Arabic-Indic (Persian) ۰–۹\n [0x0660, 0x0669], // Arabic-Indic ٠–٩\n ],\n});\n\n/** BCP 47 locale tags that this plugin covers. */\nexport const LOCALE_CODES = [\"fa\", \"fa-IR\", \"fa-AF\"] as const;\n"]}
1
+ {"version":3,"sources":["../src/locales/fa.ts"],"names":[],"mappings":";;;AAeA,cAAA,CAAe;AAAA,EACb,WAAA,EAAa;AAAA,IACX,CAAC,MAAQ,IAAM,CAAA;AAAA;AAAA,IACf,CAAC,MAAQ,IAAM;AAAA;AAAA;AAEnB,CAAC,CAAA;AAGM,IAAM,YAAA,GAAe,CAAC,IAAA,EAAM,OAAA,EAAS,OAAO","file":"chunk-OLLREWX6.js","sourcesContent":["/**\n * Persian (fa-IR) locale plugin.\n *\n * Registers Extended Arabic-Indic digit block (U+06F0–U+06F9).\n * The ISIRI 9147 keyboard produces these codepoints when number keys are\n * pressed on a Persian layout. This side-effect import enables their\n * normalization to ASCII 0–9 before parsing.\n *\n * Also registers the Arabic-Indic block for mixed-input scenarios.\n *\n * Usage:\n * import 'raqam/locales/fa'\n */\nimport { registerLocale } from \"../core/normalizer.js\";\n\nregisterLocale({\n digitBlocks: [\n [0x06f0, 0x06f9], // Extended Arabic-Indic (Persian) ۰–۹\n [0x0660, 0x0669], // Arabic-Indic ٠–٩\n ],\n});\n\n/** BCP 47 locale tags that this plugin covers. */\nexport const LOCALE_CODES = [\"fa\", \"fa-IR\", \"fa-AF\"] as const;\n"]}
@@ -1,4 +1,4 @@
1
- import { registerLocale } from './chunk-NBAZIJ5W.js';
1
+ import { registerLocale } from './chunk-TISOJTKH.js';
2
2
 
3
3
  // src/locales/hi.ts
4
4
  registerLocale({
@@ -10,5 +10,5 @@ registerLocale({
10
10
  var LOCALE_CODES = ["hi", "hi-IN", "mr", "mr-IN", "ne", "ne-NP"];
11
11
 
12
12
  export { LOCALE_CODES };
13
- //# sourceMappingURL=chunk-NSFX2EAT.js.map
14
- //# sourceMappingURL=chunk-NSFX2EAT.js.map
13
+ //# sourceMappingURL=chunk-RP3DVBGJ.js.map
14
+ //# sourceMappingURL=chunk-RP3DVBGJ.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/locales/hi.ts"],"names":[],"mappings":";;;AAUA,cAAA,CAAe;AAAA,EACb,WAAA,EAAa;AAAA,IACX,CAAC,MAAQ,IAAM;AAAA;AAAA;AAEnB,CAAC,CAAA;AAGM,IAAM,eAAe,CAAC,IAAA,EAAM,SAAS,IAAA,EAAM,OAAA,EAAS,MAAM,OAAO","file":"chunk-NSFX2EAT.js","sourcesContent":["/**\n * Hindi / Devanagari (hi-IN) locale plugin.\n *\n * Registers Devanagari digit block (U+0966–U+096F).\n *\n * Usage:\n * import 'raqam/locales/hi'\n */\nimport { registerLocale } from \"../core/normalizer.js\";\n\nregisterLocale({\n digitBlocks: [\n [0x0966, 0x096f], // Devanagari ०–९\n ],\n});\n\n/** BCP 47 locale tags that this plugin covers. */\nexport const LOCALE_CODES = [\"hi\", \"hi-IN\", \"mr\", \"mr-IN\", \"ne\", \"ne-NP\"] as const;\n"]}
1
+ {"version":3,"sources":["../src/locales/hi.ts"],"names":[],"mappings":";;;AAUA,cAAA,CAAe;AAAA,EACb,WAAA,EAAa;AAAA,IACX,CAAC,MAAQ,IAAM;AAAA;AAAA;AAEnB,CAAC,CAAA;AAGM,IAAM,eAAe,CAAC,IAAA,EAAM,SAAS,IAAA,EAAM,OAAA,EAAS,MAAM,OAAO","file":"chunk-RP3DVBGJ.js","sourcesContent":["/**\n * Hindi / Devanagari (hi-IN) locale plugin.\n *\n * Registers Devanagari digit block (U+0966–U+096F).\n *\n * Usage:\n * import 'raqam/locales/hi'\n */\nimport { registerLocale } from \"../core/normalizer.js\";\n\nregisterLocale({\n digitBlocks: [\n [0x0966, 0x096f], // Devanagari ०–९\n ],\n});\n\n/** BCP 47 locale tags that this plugin covers. */\nexport const LOCALE_CODES = [\"hi\", \"hi-IN\", \"mr\", \"mr-IN\", \"ne\", \"ne-NP\"] as const;\n"]}
@@ -21,5 +21,5 @@ function registerLocale(config) {
21
21
  }
22
22
 
23
23
  export { registerLocale };
24
- //# sourceMappingURL=chunk-NBAZIJ5W.js.map
25
- //# sourceMappingURL=chunk-NBAZIJ5W.js.map
24
+ //# sourceMappingURL=chunk-TISOJTKH.js.map
25
+ //# sourceMappingURL=chunk-TISOJTKH.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/core/normalizer.ts"],"names":[],"mappings":";AAMA,IAAM,oBAAA,GAAqC;AAAA,EACzC,CAAC,MAAQ,IAAM,CAAA;AAAA;AAAA,EACf,CAAC,MAAQ,IAAM,CAAA;AAAA;AAAA,EACf,CAAC,MAAQ,IAAM,CAAA;AAAA;AAAA,EACf,CAAC,MAAQ,IAAM,CAAA;AAAA;AAAA,EACf,CAAC,MAAQ,IAAM;AAAA;AACjB,CAAA;AAGA,IAAM,gBAAA,GAAiC,CAAC,GAAG,oBAAoB,CAAA;AAaxD,SAAS,eAAe,MAAA,EAA4B;AACzD,EAAA,IAAI,CAAC,OAAO,WAAA,EAAa;AACzB,EAAA,KAAA,MAAW,KAAA,IAAS,OAAO,WAAA,EAAa;AACtC,IAAA,MAAM,OAAA,GAAU,gBAAA,CAAiB,IAAA,CAAK,CAAC,CAAC,CAAC,CAAA,KAAM,CAAA,KAAM,KAAA,CAAM,CAAC,CAAC,CAAA;AAC7D,IAAA,IAAI,CAAC,OAAA,EAAS,gBAAA,CAAiB,IAAA,CAAK,KAAK,CAAA;AAAA,EAC3C;AACF","file":"chunk-NBAZIJ5W.js","sourcesContent":["import type { DigitBlock } from \"./types.js\";\n\n// ── Built-in digit blocks ────────────────────────────────────────────────────\n// These cover the digit systems required by the spec.\n// Additional blocks can be registered via registerLocale().\n\nconst BUILTIN_DIGIT_BLOCKS: DigitBlock[] = [\n [0x0660, 0x0669], // Arabic-Indic (arab)\n [0x06f0, 0x06f9], // Extended Arabic-Indic / Persian (arabext)\n [0x0966, 0x096f], // Devanagari / Hindi (deva)\n [0x09e6, 0x09ef], // Bengali (beng)\n [0x0e50, 0x0e59], // Thai (thai)\n];\n\n// Mutable registry — locale plugins can add blocks here\nconst registeredBlocks: DigitBlock[] = [...BUILTIN_DIGIT_BLOCKS];\n\n// ── Public API ────────────────────────────────────────────────────────────────\n\nexport interface LocaleConfig {\n /** Extra digit block ranges to register */\n digitBlocks?: DigitBlock[];\n}\n\n/**\n * Register additional digit blocks (called by locale plugins as a side effect).\n * Duplicate ranges are silently ignored.\n */\nexport function registerLocale(config: LocaleConfig): void {\n if (!config.digitBlocks) return;\n for (const block of config.digitBlocks) {\n const already = registeredBlocks.some(([s]) => s === block[0]);\n if (!already) registeredBlocks.push(block);\n }\n}\n\n/**\n * Normalise any Unicode decimal digit in `input` to its ASCII equivalent (0–9).\n * Non-digit characters pass through unchanged.\n */\nexport function normalizeDigits(input: string): string {\n // Fast path: if there are no non-ASCII chars, return as-is\n if (!/[^\\u0020-\\u007e]/.test(input)) return input;\n\n return input.replace(/\\p{Nd}/gu, (ch) => {\n const code = ch.codePointAt(0)!;\n for (const [start, end] of registeredBlocks) {\n if (code >= start && code <= end) {\n return String(code - start);\n }\n }\n // Fallback: let JS try to parse it as a decimal digit\n const digit = Number.parseInt(ch, 10);\n return Number.isNaN(digit) ? ch : String(digit);\n });\n}\n\n/**\n * Returns true if the character is a non-Latin Unicode decimal digit\n * (i.e. would need normalization).\n */\nexport function isNonLatinDigit(ch: string): boolean {\n const code = ch.codePointAt(0);\n if (code === undefined) return false;\n if (code >= 0x30 && code <= 0x39) return false; // ASCII 0-9\n for (const [start, end] of registeredBlocks) {\n if (code >= start && code <= end) return true;\n }\n return false;\n}\n"]}
1
+ {"version":3,"sources":["../src/core/normalizer.ts"],"names":[],"mappings":";AAMA,IAAM,oBAAA,GAAqC;AAAA,EACzC,CAAC,MAAQ,IAAM,CAAA;AAAA;AAAA,EACf,CAAC,MAAQ,IAAM,CAAA;AAAA;AAAA,EACf,CAAC,MAAQ,IAAM,CAAA;AAAA;AAAA,EACf,CAAC,MAAQ,IAAM,CAAA;AAAA;AAAA,EACf,CAAC,MAAQ,IAAM;AAAA;AACjB,CAAA;AAGA,IAAM,gBAAA,GAAiC,CAAC,GAAG,oBAAoB,CAAA;AAaxD,SAAS,eAAe,MAAA,EAA4B;AACzD,EAAA,IAAI,CAAC,OAAO,WAAA,EAAa;AACzB,EAAA,KAAA,MAAW,KAAA,IAAS,OAAO,WAAA,EAAa;AACtC,IAAA,MAAM,OAAA,GAAU,gBAAA,CAAiB,IAAA,CAAK,CAAC,CAAC,CAAC,CAAA,KAAM,CAAA,KAAM,KAAA,CAAM,CAAC,CAAC,CAAA;AAC7D,IAAA,IAAI,CAAC,OAAA,EAAS,gBAAA,CAAiB,IAAA,CAAK,KAAK,CAAA;AAAA,EAC3C;AACF","file":"chunk-TISOJTKH.js","sourcesContent":["import type { DigitBlock } from \"./types.js\";\n\n// ── Built-in digit blocks ────────────────────────────────────────────────────\n// These cover the digit systems required by the spec.\n// Additional blocks can be registered via registerLocale().\n\nconst BUILTIN_DIGIT_BLOCKS: DigitBlock[] = [\n [0x0660, 0x0669], // Arabic-Indic (arab)\n [0x06f0, 0x06f9], // Extended Arabic-Indic / Persian (arabext)\n [0x0966, 0x096f], // Devanagari / Hindi (deva)\n [0x09e6, 0x09ef], // Bengali (beng)\n [0x0e50, 0x0e59], // Thai (thai)\n];\n\n// Mutable registry — locale plugins can add blocks here\nconst registeredBlocks: DigitBlock[] = [...BUILTIN_DIGIT_BLOCKS];\n\n// ── Public API ────────────────────────────────────────────────────────────────\n\nexport interface LocaleConfig {\n /** Extra digit block ranges to register */\n digitBlocks?: DigitBlock[];\n}\n\n/**\n * Register additional digit blocks (called by locale plugins as a side effect).\n * Duplicate ranges are silently ignored.\n */\nexport function registerLocale(config: LocaleConfig): void {\n if (!config.digitBlocks) return;\n for (const block of config.digitBlocks) {\n const already = registeredBlocks.some(([s]) => s === block[0]);\n if (!already) registeredBlocks.push(block);\n }\n}\n\n/**\n * Normalise any Unicode decimal digit in `input` to its ASCII equivalent (0–9).\n * Non-digit characters pass through unchanged.\n */\nexport function normalizeDigits(input: string): string {\n // Fast path: if there are no non-ASCII chars, return as-is\n if (!/[^\\u0020-\\u007e]/.test(input)) return input;\n\n return input.replace(/\\p{Nd}/gu, (ch) => {\n const code = ch.codePointAt(0)!;\n for (const [start, end] of registeredBlocks) {\n if (code >= start && code <= end) {\n return String(code - start);\n }\n }\n // Fallback: let JS try to parse it as a decimal digit\n const digit = Number.parseInt(ch, 10);\n return Number.isNaN(digit) ? ch : String(digit);\n });\n}\n\n/**\n * Inverse of {@link normalizeDigits}: map ASCII digits 0–9 to the locale's\n * native digit block (identified by its \"zero\" character). No-op when the\n * locale already uses ASCII digits. Used to keep intermediate (still-typing)\n * display strings in the user's native script.\n */\nexport function localizeDigits(input: string, zero: string): string {\n if (!zero || zero === \"0\") return input;\n const base = zero.codePointAt(0);\n if (base === undefined) return input;\n return input.replace(/[0-9]/g, (d) => String.fromCodePoint(base + (d.charCodeAt(0) - 48)));\n}\n\n/**\n * Returns true if the character is a non-Latin Unicode decimal digit\n * (i.e. would need normalization).\n */\nexport function isNonLatinDigit(ch: string): boolean {\n const code = ch.codePointAt(0);\n if (code === undefined) return false;\n if (code >= 0x30 && code <= 0x39) return false; // ASCII 0-9\n for (const [start, end] of registeredBlocks) {\n if (code >= start && code <= end) return true;\n }\n return false;\n}\n"]}
package/dist/core.cjs CHANGED
@@ -1,2 +1,2 @@
1
- 'use strict';var L={currency:e=>({style:"currency",currency:e}),accounting:e=>({style:"currency",currency:e,currencySign:"accounting"}),percent:{style:"percent"},compact:{notation:"compact"},compactLong:{notation:"compact",compactDisplay:"long"},scientific:{notation:"scientific"},engineering:{notation:"engineering"},integer:{maximumFractionDigits:0},financial:{minimumFractionDigits:2,maximumFractionDigits:2},unit:e=>({style:"unit",unit:e})};var O=[[1632,1641],[1776,1785],[2406,2415],[2534,2543],[3664,3673]],p=[...O];function D(e){if(e.digitBlocks)for(let t of e.digitBlocks)p.some(([i])=>i===t[0])||p.push(t);}function g(e){return /[^\u0020-\u007e]/.test(e)?e.replace(/\p{Nd}/gu,t=>{let r=t.codePointAt(0);for(let[o,s]of p)if(r>=o&&r<=s)return String(r-o);let i=Number.parseInt(t,10);return Number.isNaN(i)?t:String(i)}):e}var x=new Map;function F(e,t){let r=`${e??""}::${JSON.stringify(t??{})}`,i=x.get(r);return i||(i=new Intl.NumberFormat(e,t),x.set(r,i)),i}function P(e,t){let r=F(e,t),i=r.formatToParts(12345.6),o=".",s=",",f="-",c="0";for(let m of i)m.type==="decimal"&&(o=m.value),m.type==="group"&&(s=m.value),m.type==="minusSign"&&(f=m.value);let u=r.formatToParts(0);for(let m of u)if(m.type==="integer"){c=m.value;break}let a=/^(ar|he|fa|ur|syc|nqo|ug|yi)/i,n=r.resolvedOptions().locale,l=a.test(n);return {decimalSeparator:o,groupingSeparator:s,minusSign:f,zero:c,isRTL:l}}function d(e){let t={...e.formatOptions};e.minimumFractionDigits!==void 0&&(t.minimumFractionDigits=e.minimumFractionDigits),e.maximumFractionDigits!==void 0&&(t.maximumFractionDigits=e.maximumFractionDigits),e.fixedDecimalScale&&e.maximumFractionDigits!==void 0&&(t.minimumFractionDigits=e.maximumFractionDigits,t.maximumFractionDigits=e.maximumFractionDigits);let r=F(e.locale,t),i=null;function o(){return i||(i=P(e.locale,t)),i}function s(u){let a=r.formatToParts(u);if(!e.prefix&&!e.suffix)return a;let n=[];return e.prefix&&n.push({type:"literal",value:e.prefix}),n.push(...a),e.suffix&&n.push({type:"literal",value:e.suffix}),n}function f(u){if(!Number.isFinite(u))return "";let a=r.format(u);return (e.prefix??"")+a+(e.suffix??"")}function c(u){let a=s(u);return {formatted:a.map(l=>l.value).join(""),parts:a}}return {format:f,formatToParts:s,getLocaleInfo:o,formatResult:c}}function I(e){return e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}function b(e,t,r,i){let o=I(t.decimalSeparator),s=I(t.minusSign);return !!(r&&(e==="-"||e===t.minusSign)||i&&new RegExp(`^${s}?\\d+${o}$`).test(e)||i&&new RegExp(`^${s}?\\d+${o}\\d*0+$`).test(e))}function S(e={}){let t=e.allowNegative??true,r=e.allowDecimal??true,i=d({locale:e.locale,formatOptions:e.formatOptions,prefix:e.prefix,suffix:e.suffix});function o(){return i.getLocaleInfo()}function s(u){let a=o(),n=g(u),l=n.match(/^\((.+)\)$/);return l&&(n=`-${l[1]}`),e.prefix&&n.startsWith(e.prefix)&&(n=n.slice(e.prefix.length)),e.suffix&&n.endsWith(e.suffix)&&(n=n.slice(0,-e.suffix.length)),a.groupingSeparator&&(n=n.split(a.groupingSeparator).join("")),a.decimalSeparator!=="."&&(n=n.split(a.decimalSeparator).join(".")),a.minusSign!=="-"&&(n=n.split(a.minusSign).join("-")),n=n.replace(/[^\d.\-]/g,"").trim(),n}function f(u){if(!u||u.trim()==="")return {value:null,isValid:false,isIntermediate:false};let a=o();if(b(u,a,t,r))return {value:null,isValid:false,isIntermediate:true};let n=s(u);if(n==="")return {value:null,isValid:false,isIntermediate:false};if(n==="-")return {value:null,isValid:false,isIntermediate:t};if(!/^-?\d+\.?\d*$/.test(n))return {value:null,isValid:false,isIntermediate:false};if(!t&&n.startsWith("-"))return {value:null,isValid:false,isIntermediate:false};if(!r&&n.includes("."))return {value:null,isValid:false,isIntermediate:false};let l=Number.parseFloat(n);return Number.isFinite(l)?{value:l,isValid:true,isIntermediate:false}:{value:null,isValid:false,isIntermediate:false}}function c(u){let a=o();return b(u,a,t,r)}return {parse:f,isIntermediate:c,getLocaleInfo:o}}function y(e,t){return e>="0"&&e<="9"||e===t.decimalSeparator||e===t.minusSign||e==="-"}function v(e,t,r){let i=g(e),o=0;for(let s=0;s<t&&s<i.length;s++)y(i[s],r)&&o++;return o}function N(e,t){let r=e.length,i=new Array(r+1).fill(true);for(let o=0;o<r;o++)e[o]===t.groupingSeparator&&(i[o+1]=false);return i[0]=true,i[r]=true,i}function B(e,t,r,i,o){let s=g(e),f=v(s,t,i);o==="deleteContentBackward"&&t>0&&e[t-1]===i.groupingSeparator&&(f=Math.max(0,f-1));let c=N(r,i),u=g(r),a=0,n=0;for(let l=0;l<u.length;l++){if(a===f){n=l;break}y(u[l],i)&&a++,n=l+1;}return f>0&&a<f&&(n=r.length),n=R(n,c),n}function R(e,t){if(t[e])return e;for(let r=e+1;r<t.length;r++)if(t[r])return r;for(let r=e-1;r>=0;r--)if(t[r])return r;return 0}exports.computeNewCursorPosition=B;exports.createFormatter=d;exports.createParser=S;exports.getCaretBoundary=N;exports.normalizeDigits=g;exports.presets=L;exports.registerLocale=D;//# sourceMappingURL=core.cjs.map
1
+ 'use strict';var D={currency:e=>({style:"currency",currency:e}),accounting:e=>({style:"currency",currency:e,currencySign:"accounting"}),percent:{style:"percent"},compact:{notation:"compact"},compactLong:{notation:"compact",compactDisplay:"long"},scientific:{notation:"scientific"},engineering:{notation:"engineering"},integer:{maximumFractionDigits:0},financial:{minimumFractionDigits:2,maximumFractionDigits:2},unit:e=>({style:"unit",unit:e})};var O=[[1632,1641],[1776,1785],[2406,2415],[2534,2543],[3664,3673]],x=[...O];function L(e){if(e.digitBlocks)for(let r of e.digitBlocks)x.some(([n])=>n===r[0])||x.push(r);}function d(e){return /[^\u0020-\u007e]/.test(e)?e.replace(/\p{Nd}/gu,r=>{let i=r.codePointAt(0);for(let[u,s]of x)if(i>=u&&i<=s)return String(i-u);let n=Number.parseInt(r,10);return Number.isNaN(n)?r:String(n)}):e}var I=new Map;function b(e,r){let i=`${e??""}::${JSON.stringify(r??{})}`,n=I.get(i);return n||(n=new Intl.NumberFormat(e,r),I.set(i,n)),n}function P(e,r){let i=b(e,r),n=b(e,{minimumFractionDigits:1,maximumFractionDigits:1,numberingSystem:r?.numberingSystem}),u=n.formatToParts(-12345.6),s=".",l=",",c="-",f="0";for(let t of u)t.type==="decimal"&&(s=t.value),t.type==="group"&&(l=t.value),t.type==="minusSign"&&(c=t.value);for(let t of n.formatToParts(0))if(t.type==="integer"){f=t.value;break}let m=/^(ar|he|fa|ur|syc|nqo|ug|yi)/i,a=i.resolvedOptions().locale,o=m.test(a);return {decimalSeparator:s,groupingSeparator:l,minusSign:c,zero:f,isRTL:o}}function F(e){let r={...e.formatOptions};e.minimumFractionDigits!==void 0&&(r.minimumFractionDigits=e.minimumFractionDigits),e.maximumFractionDigits!==void 0&&(r.maximumFractionDigits=e.maximumFractionDigits),e.fixedDecimalScale&&e.maximumFractionDigits!==void 0&&(r.minimumFractionDigits=e.maximumFractionDigits,r.maximumFractionDigits=e.maximumFractionDigits);let i=b(e.locale,r),n=null;function u(){return n||(n=P(e.locale,r)),n}function s(f){let m=i.formatToParts(f);if(!e.prefix&&!e.suffix)return m;let a=[];return e.prefix&&a.push({type:"literal",value:e.prefix}),a.push(...m),e.suffix&&a.push({type:"literal",value:e.suffix}),a}function l(f){if(!Number.isFinite(f))return "";let m=i.format(f);return (e.prefix??"")+m+(e.suffix??"")}function c(f){let m=s(f);return {formatted:m.map(o=>o.value).join(""),parts:m}}return {format:l,formatToParts:s,getLocaleInfo:u,formatResult:c}}function S(e,r){return r?!!(/^-0+$/.test(e)||/\.$/.test(e)||/\.\d*0$/.test(e)||/^-?\.\d+$/.test(e)):false}function v(e={}){let r=e.allowNegative??true,i=e.allowDecimal??true,n=e.formatOptions?.style==="percent",u=F({locale:e.locale,formatOptions:e.formatOptions,prefix:e.prefix,suffix:e.suffix}),s="";if(e.formatOptions?.style==="currency")try{s=u.formatToParts(1).filter(a=>a.type==="currency").map(a=>a.value).join("");}catch{s="";}function l(){return u.getLocaleInfo()}function c(a){let o=l(),t=d(a);s&&(t=t.split(s).join(""));let g=t.match(/^\((.+)\)$/);if(g&&(t=`-${g[1]}`),e.prefix&&t.startsWith(e.prefix)&&(t=t.slice(e.prefix.length)),e.suffix&&t.endsWith(e.suffix)&&(t=t.slice(0,-e.suffix.length)),o.groupingSeparator&&(t=t.split(o.groupingSeparator).join("")),o.decimalSeparator!=="."&&(t=t.split(o.decimalSeparator).join(".")),o.minusSign!=="-"&&(t=t.split(o.minusSign).join("-")),t.includes("\u2212")&&(t=t.split("\u2212").join("-")),t=t.replace(/[^\d.\-]/g,"").trim(),t.includes("-")){let p=t.startsWith("-");t=t.replace(/-/g,""),p&&(t=`-${t}`);}return t}function f(a){if(!a||a.trim()==="")return {value:null,isValid:false,isIntermediate:false};let o=c(a);if(o==="")return {value:null,isValid:false,isIntermediate:false};if(o==="-")return {value:null,isValid:false,isIntermediate:r};if(o==="."||o==="-.")return {value:null,isValid:false,isIntermediate:i&&(o==="."||r)};if(!r&&o.startsWith("-"))return {value:null,isValid:false,isIntermediate:false};if(!i&&o.includes("."))return {value:null,isValid:false,isIntermediate:false};if(!/^-?(?:\d+\.?\d*|\.\d+)$/.test(o))return {value:null,isValid:false,isIntermediate:false};let t=Number.parseFloat(o);return Number.isFinite(t)?(Object.is(t,-0)&&(t=0),n&&t!==0&&(t=Number((t/100).toPrecision(15))),{value:t,isValid:true,isIntermediate:S(o,i)}):{value:null,isValid:false,isIntermediate:false}}function m(a){return f(a).isIntermediate}return {parse:f,isIntermediate:m,getLocaleInfo:l}}function y(e,r){return e>="0"&&e<="9"||e===r.decimalSeparator||e===r.minusSign||e==="-"||e==="."&&r.decimalSeparator!=="."&&r.groupingSeparator!=="."}function B(e,r,i){let n=d(e),u=0;for(let s=0;s<r&&s<n.length;s++)y(n[s],i)&&u++;return u}function N(e,r){let i=e.length,n=new Array(i+1).fill(true);for(let u=0;u<i;u++)e[u]===r.groupingSeparator&&(n[u+1]=false);return n[0]=true,n[i]=true,n}function h(e,r,i,n,u){let s=d(e),l=B(s,r,n),c=e.slice(0,r),f=c.includes("-")||c.includes(n.minusSign),m=i.includes("-")||i.includes(n.minusSign);f&&!m&&(l=Math.max(0,l-1)),u==="deleteContentBackward"&&r>0&&e[r-1]===n.groupingSeparator&&(l=Math.max(0,l-1));let a=N(i,n),o=d(i),t=0,g=0;for(let p=0;p<o.length;p++){if(t===l){g=p;break}y(o[p],n)&&t++,g=p+1;}return l>0&&t<l&&(g=i.length),g=C(g,a),g}function C(e,r){if(r[e])return e;for(let i=e+1;i<r.length;i++)if(r[i])return i;for(let i=e-1;i>=0;i--)if(r[i])return i;return 0}exports.computeNewCursorPosition=h;exports.createFormatter=F;exports.createParser=v;exports.getCaretBoundary=N;exports.normalizeDigits=d;exports.presets=D;exports.registerLocale=L;//# sourceMappingURL=core.cjs.map
2
2
  //# sourceMappingURL=core.cjs.map
package/dist/core.cjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/core/presets.ts","../src/core/normalizer.ts","../src/core/formatter.ts","../src/core/parser.ts","../src/core/cursor.ts"],"names":["presets","code","unit","BUILTIN_DIGIT_BLOCKS","registeredBlocks","registerLocale","config","block","s","normalizeDigits","input","ch","start","end","digit","formatterCache","getFormatter","locale","options","key","fmt","extractLocaleInfo","parts","decimalSeparator","groupingSeparator","minusSign","zero","part","zeroParts","rtlLocales","resolvedLocale","isRTL","createFormatter","opts","intlOptions","intlFmt","cachedLocaleInfo","getLocaleInfo","formatToParts","value","result","format","formatted","formatResult","p","escapeRegex","checkIntermediate","normalized","info","allowNegative","allowDecimal","dec","minus","createParser","stripAffordances","raw","accountingMatch","parse","stripped","n","isIntermediate","isAccepted","countAcceptedBefore","str","cursor","normalised","count","i","getCaretBoundary","formattedValue","len","boundary","computeNewCursorPosition","oldInput","oldCursor","newFormatted","inputType","acceptedCount","normNew","pos","snapToBoundary"],"mappings":"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","file":"core.cjs","sourcesContent":["/**\n * Format presets — named Intl.NumberFormatOptions configurations for common\n * number input patterns. Use these as the `formatOptions` prop value.\n *\n * @example\n * import { presets } from 'raqam'\n * <NumberField.Root formatOptions={presets.currency('USD')} />\n * <NumberField.Root formatOptions={presets.percent} />\n * <NumberField.Root formatOptions={presets.compact} />\n */\n\nexport const presets = {\n /** Currency with standard sign display. Shorthand for `{ style:'currency', currency:code }`. */\n currency: (code: string): Intl.NumberFormatOptions => ({\n style: \"currency\",\n currency: code,\n }),\n\n /**\n * Accounting currency — negatives shown as `(1,234.56)` instead of `-$1,234.56`.\n * Requires the accounting format parser fix (built-in to raqam).\n */\n accounting: (code: string): Intl.NumberFormatOptions => ({\n style: \"currency\",\n currency: code,\n currencySign: \"accounting\",\n }),\n\n /** Percentage — formats 0.42 as \"42%\" */\n percent: { style: \"percent\" } as Intl.NumberFormatOptions,\n\n /** Compact short — \"1.2K\", \"3.4M\" */\n compact: { notation: \"compact\" } as Intl.NumberFormatOptions,\n\n /** Compact long — \"1.2 thousand\", \"3.4 million\" */\n compactLong: {\n notation: \"compact\",\n compactDisplay: \"long\",\n } as Intl.NumberFormatOptions,\n\n /** Scientific notation — \"1.234E3\" */\n scientific: { notation: \"scientific\" } as Intl.NumberFormatOptions,\n\n /** Engineering notation — exponents always multiples of 3 */\n engineering: { notation: \"engineering\" } as Intl.NumberFormatOptions,\n\n /** Integer — no decimal places */\n integer: { maximumFractionDigits: 0 } as Intl.NumberFormatOptions,\n\n /**\n * Financial — always exactly 2 decimal places (fixed scale).\n * Combine with `fixedDecimalScale` prop for strict display.\n */\n financial: {\n minimumFractionDigits: 2,\n maximumFractionDigits: 2,\n } as Intl.NumberFormatOptions,\n\n /**\n * Unit — shorthand for `{ style:'unit', unit:unitCode }`.\n * @example presets.unit('kilometer-per-hour') → { style:'unit', unit:'kilometer-per-hour' }\n */\n unit: (unit: string): Intl.NumberFormatOptions => ({\n style: \"unit\",\n unit,\n }),\n} as const;\n","import type { DigitBlock } from \"./types.js\";\n\n// ── Built-in digit blocks ────────────────────────────────────────────────────\n// These cover the digit systems required by the spec.\n// Additional blocks can be registered via registerLocale().\n\nconst BUILTIN_DIGIT_BLOCKS: DigitBlock[] = [\n [0x0660, 0x0669], // Arabic-Indic (arab)\n [0x06f0, 0x06f9], // Extended Arabic-Indic / Persian (arabext)\n [0x0966, 0x096f], // Devanagari / Hindi (deva)\n [0x09e6, 0x09ef], // Bengali (beng)\n [0x0e50, 0x0e59], // Thai (thai)\n];\n\n// Mutable registry — locale plugins can add blocks here\nconst registeredBlocks: DigitBlock[] = [...BUILTIN_DIGIT_BLOCKS];\n\n// ── Public API ────────────────────────────────────────────────────────────────\n\nexport interface LocaleConfig {\n /** Extra digit block ranges to register */\n digitBlocks?: DigitBlock[];\n}\n\n/**\n * Register additional digit blocks (called by locale plugins as a side effect).\n * Duplicate ranges are silently ignored.\n */\nexport function registerLocale(config: LocaleConfig): void {\n if (!config.digitBlocks) return;\n for (const block of config.digitBlocks) {\n const already = registeredBlocks.some(([s]) => s === block[0]);\n if (!already) registeredBlocks.push(block);\n }\n}\n\n/**\n * Normalise any Unicode decimal digit in `input` to its ASCII equivalent (0–9).\n * Non-digit characters pass through unchanged.\n */\nexport function normalizeDigits(input: string): string {\n // Fast path: if there are no non-ASCII chars, return as-is\n if (!/[^\\u0020-\\u007e]/.test(input)) return input;\n\n return input.replace(/\\p{Nd}/gu, (ch) => {\n const code = ch.codePointAt(0)!;\n for (const [start, end] of registeredBlocks) {\n if (code >= start && code <= end) {\n return String(code - start);\n }\n }\n // Fallback: let JS try to parse it as a decimal digit\n const digit = Number.parseInt(ch, 10);\n return Number.isNaN(digit) ? ch : String(digit);\n });\n}\n\n/**\n * Returns true if the character is a non-Latin Unicode decimal digit\n * (i.e. would need normalization).\n */\nexport function isNonLatinDigit(ch: string): boolean {\n const code = ch.codePointAt(0);\n if (code === undefined) return false;\n if (code >= 0x30 && code <= 0x39) return false; // ASCII 0-9\n for (const [start, end] of registeredBlocks) {\n if (code >= start && code <= end) return true;\n }\n return false;\n}\n","import type { FormatResult, LocaleInfo } from \"./types.js\";\n\n// ── Internal ──────────────────────────────────────────────────────────────────\n\n/** Probe value that will surface decimal AND grouping parts */\nconst PROBE_VALUE = 12345.6;\n\n/** Cache key = locale + JSON.stringify(options) */\nconst formatterCache = new Map<string, Intl.NumberFormat>();\n\nfunction getFormatter(\n locale: string | undefined,\n options: Intl.NumberFormatOptions | undefined\n): Intl.NumberFormat {\n const key = `${locale ?? \"\"}::${JSON.stringify(options ?? {})}`;\n let fmt = formatterCache.get(key);\n if (!fmt) {\n fmt = new Intl.NumberFormat(locale, options);\n formatterCache.set(key, fmt);\n }\n return fmt;\n}\n\n/** Extract locale meta from formatToParts — never hardcoded. */\nfunction extractLocaleInfo(\n locale: string | undefined,\n options: Intl.NumberFormatOptions | undefined\n): LocaleInfo {\n const fmt = getFormatter(locale, options);\n // Use a simple decimal number — we only need the separators\n const parts = fmt.formatToParts(PROBE_VALUE);\n\n let decimalSeparator = \".\";\n let groupingSeparator = \",\";\n let minusSign = \"-\";\n let zero = \"0\";\n\n for (const part of parts) {\n if (part.type === \"decimal\") decimalSeparator = part.value;\n if (part.type === \"group\") groupingSeparator = part.value;\n if (part.type === \"minusSign\") minusSign = part.value;\n }\n\n // Detect locale zero digit\n const zeroParts = fmt.formatToParts(0);\n for (const part of zeroParts) {\n if (part.type === \"integer\") {\n zero = part.value;\n break;\n }\n }\n\n // RTL locales: Arabic / Hebrew / Persian / Urdu / Syriac etc.\n const rtlLocales = /^(ar|he|fa|ur|syc|nqo|ug|yi)/i;\n const resolvedLocale = fmt.resolvedOptions().locale;\n const isRTL = rtlLocales.test(resolvedLocale);\n\n return { decimalSeparator, groupingSeparator, minusSign, zero, isRTL };\n}\n\n// ── Factory ───────────────────────────────────────────────────────────────────\n\nexport interface FormatterOptions {\n locale?: string;\n formatOptions?: Intl.NumberFormatOptions;\n prefix?: string;\n suffix?: string;\n minimumFractionDigits?: number;\n maximumFractionDigits?: number;\n fixedDecimalScale?: boolean;\n}\n\nexport interface Formatter {\n format(value: number): string;\n formatToParts(value: number): Intl.NumberFormatPart[];\n getLocaleInfo(): LocaleInfo;\n formatResult(value: number): FormatResult;\n}\n\n/**\n * Create a formatter instance. Intl.NumberFormat is cached — safe to call\n * on every render.\n */\nexport function createFormatter(opts: FormatterOptions): Formatter {\n // Merge fraction digit overrides into formatOptions\n const intlOptions: Intl.NumberFormatOptions = { ...opts.formatOptions };\n\n if (opts.minimumFractionDigits !== undefined) {\n intlOptions.minimumFractionDigits = opts.minimumFractionDigits;\n }\n if (opts.maximumFractionDigits !== undefined) {\n intlOptions.maximumFractionDigits = opts.maximumFractionDigits;\n }\n if (opts.fixedDecimalScale && opts.maximumFractionDigits !== undefined) {\n intlOptions.minimumFractionDigits = opts.maximumFractionDigits;\n intlOptions.maximumFractionDigits = opts.maximumFractionDigits;\n }\n\n const intlFmt = getFormatter(opts.locale, intlOptions);\n // Lazy — computed once on first call\n let cachedLocaleInfo: LocaleInfo | null = null;\n\n function getLocaleInfo(): LocaleInfo {\n if (!cachedLocaleInfo) {\n cachedLocaleInfo = extractLocaleInfo(opts.locale, intlOptions);\n }\n return cachedLocaleInfo;\n }\n\n function formatToParts(value: number): Intl.NumberFormatPart[] {\n const parts = intlFmt.formatToParts(value);\n if (!opts.prefix && !opts.suffix) return parts;\n\n const result: Intl.NumberFormatPart[] = [];\n if (opts.prefix) result.push({ type: \"literal\", value: opts.prefix });\n result.push(...parts);\n if (opts.suffix) result.push({ type: \"literal\", value: opts.suffix });\n return result;\n }\n\n function format(value: number): string {\n if (!Number.isFinite(value)) return \"\";\n const formatted = intlFmt.format(value);\n return (opts.prefix ?? \"\") + formatted + (opts.suffix ?? \"\");\n }\n\n function formatResult(value: number): FormatResult {\n const parts = formatToParts(value);\n const formatted = parts.map((p) => p.value).join(\"\");\n return { formatted, parts };\n }\n\n return { format, formatToParts, getLocaleInfo, formatResult };\n}\n","import { createFormatter } from \"./formatter.js\";\nimport { normalizeDigits } from \"./normalizer.js\";\nimport type { LocaleInfo, ParseResult } from \"./types.js\";\n\n// ── Helpers ───────────────────────────────────────────────────────────────────\n\n/**\n * Escape a string so it can be used literally inside a RegExp.\n */\nfunction escapeRegex(s: string): string {\n return s.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n\n/**\n * Returns true if `s` represents a valid-but-incomplete number:\n * \"-\" lone minus\n * \"1.\" trailing decimal separator (any locale)\n * \"1.0\" trailing zero after decimal\n * \"1.00\" etc.\n *\n * These should NOT be reformatted while the user is still typing.\n */\nfunction checkIntermediate(\n normalized: string,\n info: LocaleInfo,\n allowNegative: boolean,\n allowDecimal: boolean\n): boolean {\n const dec = escapeRegex(info.decimalSeparator);\n const minus = escapeRegex(info.minusSign);\n\n // Lone minus sign\n if (allowNegative && (normalized === \"-\" || normalized === info.minusSign)) {\n return true;\n }\n // Trailing decimal separator\n if (allowDecimal && new RegExp(`^${minus}?\\\\d+${dec}$`).test(normalized)) {\n return true;\n }\n // Trailing zeros after decimal (e.g. \"1.0\", \"1.00\", \"-1.0\")\n if (allowDecimal && new RegExp(`^${minus}?\\\\d+${dec}\\\\d*0+$`).test(normalized)) {\n return true;\n }\n return false;\n}\n\n// ── Factory ───────────────────────────────────────────────────────────────────\n\nexport interface ParserOptions {\n locale?: string;\n formatOptions?: Intl.NumberFormatOptions;\n allowNegative?: boolean;\n allowDecimal?: boolean;\n prefix?: string;\n suffix?: string;\n}\n\nexport interface Parser {\n parse(input: string): ParseResult;\n isIntermediate(input: string): boolean;\n getLocaleInfo(): LocaleInfo;\n}\n\n/**\n * Create a locale-aware parser. Separator characters are extracted from\n * Intl.NumberFormat — never hardcoded.\n */\nexport function createParser(opts: ParserOptions = {}): Parser {\n const allowNegative = opts.allowNegative ?? true;\n const allowDecimal = opts.allowDecimal ?? true;\n\n // Re-use the formatter to get locale info\n const fmt = createFormatter({\n locale: opts.locale,\n formatOptions: opts.formatOptions,\n prefix: opts.prefix,\n suffix: opts.suffix,\n });\n\n function getLocaleInfo(): LocaleInfo {\n return fmt.getLocaleInfo();\n }\n\n function stripAffordances(raw: string): string {\n const info = getLocaleInfo();\n\n // 1. Normalise non-Latin digits to ASCII\n let s = normalizeDigits(raw);\n\n // 2. Accounting format: \"(1,234.56)\" or \"($1,234.56)\" → negative\n // Intl.NumberFormat with currencySign:\"accounting\" wraps negatives in parens\n const accountingMatch = s.match(/^\\((.+)\\)$/);\n if (accountingMatch) {\n s = `-${accountingMatch[1]}`;\n }\n\n // 3. Strip prefix / suffix\n if (opts.prefix && s.startsWith(opts.prefix)) {\n s = s.slice(opts.prefix.length);\n }\n if (opts.suffix && s.endsWith(opts.suffix)) {\n s = s.slice(0, -opts.suffix.length);\n }\n\n // 4. Strip grouping separators (escape special chars)\n if (info.groupingSeparator) {\n s = s.split(info.groupingSeparator).join(\"\");\n }\n\n // 5. Replace locale decimal separator with ASCII \".\"\n if (info.decimalSeparator !== \".\") {\n s = s.split(info.decimalSeparator).join(\".\");\n }\n\n // 6. Replace locale minus sign with ASCII \"-\"\n if (info.minusSign !== \"-\") {\n s = s.split(info.minusSign).join(\"-\");\n }\n\n // 7. Strip currency symbol, percent sign, spaces that Intl might prepend/append\n // Strip any remaining non-numeric chars except digits, \".\", \"-\"\n // (handles currency prefixes/suffixes from Intl)\n s = s.replace(/[^\\d.\\-]/g, \"\").trim();\n\n return s;\n }\n\n function parse(input: string): ParseResult {\n if (!input || input.trim() === \"\") {\n return { value: null, isValid: false, isIntermediate: false };\n }\n\n const info = getLocaleInfo();\n\n // Check for intermediate state before stripping\n if (checkIntermediate(input, info, allowNegative, allowDecimal)) {\n return { value: null, isValid: false, isIntermediate: true };\n }\n\n const stripped = stripAffordances(input);\n\n if (stripped === \"\") {\n return { value: null, isValid: false, isIntermediate: false };\n }\n\n if (stripped === \"-\") {\n // Lone minus after stripping — only intermediate if negatives are allowed\n return { value: null, isValid: false, isIntermediate: allowNegative };\n }\n\n // Reject if not a valid numeric string\n if (!/^-?\\d+\\.?\\d*$/.test(stripped)) {\n return { value: null, isValid: false, isIntermediate: false };\n }\n\n if (!allowNegative && stripped.startsWith(\"-\")) {\n return { value: null, isValid: false, isIntermediate: false };\n }\n\n if (!allowDecimal && stripped.includes(\".\")) {\n return { value: null, isValid: false, isIntermediate: false };\n }\n\n const n = Number.parseFloat(stripped);\n if (!Number.isFinite(n)) {\n return { value: null, isValid: false, isIntermediate: false };\n }\n\n return { value: n, isValid: true, isIntermediate: false };\n }\n\n function isIntermediate(input: string): boolean {\n const info = getLocaleInfo();\n return checkIntermediate(input, info, allowNegative, allowDecimal);\n }\n\n return { parse, isIntermediate, getLocaleInfo };\n}\n","import { normalizeDigits } from \"./normalizer.js\";\nimport type { CaretBoundary, LocaleInfo } from \"./types.js\";\n\n// ── Accepted-character helpers ────────────────────────────────────────────────\n\n/**\n * Returns true if `ch` is an \"accepted\" character — one that the user typed\n * intentionally and that contributes to the numeric value:\n * - ASCII digit 0-9\n * - The locale decimal separator\n * - The locale minus sign\n */\nfunction isAccepted(ch: string, info: LocaleInfo): boolean {\n if (ch >= \"0\" && ch <= \"9\") return true;\n if (ch === info.decimalSeparator) return true;\n if (ch === info.minusSign || ch === \"-\") return true;\n return false;\n}\n\n/**\n * Count how many \"accepted\" characters appear before position `cursor`\n * in `str` (after normalising non-Latin digits to ASCII).\n */\nfunction countAcceptedBefore(str: string, cursor: number, info: LocaleInfo): number {\n const normalised = normalizeDigits(str);\n let count = 0;\n for (let i = 0; i < cursor && i < normalised.length; i++) {\n if (isAccepted(normalised[i]!, info)) count++;\n }\n return count;\n}\n\n// ── Caret boundary ────────────────────────────────────────────────────────────\n\n/**\n * Build a boolean array of length `formattedValue.length + 1`.\n * `true` → cursor may rest at this position.\n * `false` → cursor must snap away (sits inside a formatting-only character\n * such as a grouping separator or a currency prefix).\n *\n * Rules:\n * - Start and end positions are always valid.\n * - A position immediately AFTER a grouping separator is invalid (the\n * cursor would look like it's between two digits but moving left would\n * skip the comma).\n * - A position immediately BEFORE a grouping separator is valid.\n */\nexport function getCaretBoundary(formattedValue: string, info: LocaleInfo): CaretBoundary {\n const len = formattedValue.length;\n const boundary: CaretBoundary = new Array(len + 1).fill(true) as boolean[];\n\n for (let i = 0; i < len; i++) {\n const ch = formattedValue[i]!;\n if (ch === info.groupingSeparator) {\n // Position immediately after the grouping separator is invalid\n boundary[i + 1] = false;\n }\n }\n\n // First and last are always valid\n boundary[0] = true;\n boundary[len] = true;\n\n return boundary;\n}\n\n// ── Cursor computation ────────────────────────────────────────────────────────\n\n/**\n * Compute the new cursor position in `newFormatted` that corresponds\n * semantically to `oldCursor` in `oldInput`.\n *\n * Algorithm (3 stages from the spec):\n *\n * 1. Count accepted characters before `oldCursor` in `oldInput`.\n * 2. Adjust for backspace-over-separator edge case.\n * 3. Walk `newFormatted` to find the position where the same count of\n * accepted chars precede it; snap to the nearest valid boundary.\n *\n * @param oldInput The raw string the user just typed into (pre-format)\n * @param oldCursor selectionStart captured from the native event\n * @param newFormatted The formatted string we're about to display\n * @param info Locale separators\n * @param inputType e.nativeEvent.inputType (optional — for backspace detection)\n */\nexport function computeNewCursorPosition(\n oldInput: string,\n oldCursor: number,\n newFormatted: string,\n info: LocaleInfo,\n inputType?: string\n): number {\n const normalised = normalizeDigits(oldInput);\n\n // Stage 1: count accepted chars before cursor in old input\n let acceptedCount = countAcceptedBefore(normalised, oldCursor, info);\n\n // Stage 2: backspace on grouping separator — also delete the preceding digit\n if (\n inputType === \"deleteContentBackward\" &&\n oldCursor > 0 &&\n oldInput[oldCursor - 1] === info.groupingSeparator\n ) {\n // The separator was deleted but we want to remove the preceding digit too\n acceptedCount = Math.max(0, acceptedCount - 1);\n }\n\n // Stage 3: build boundary and walk new formatted string\n const boundary = getCaretBoundary(newFormatted, info);\n const normNew = normalizeDigits(newFormatted);\n let count = 0;\n let pos = 0;\n\n for (let i = 0; i < normNew.length; i++) {\n if (count === acceptedCount) {\n pos = i;\n break;\n }\n if (isAccepted(normNew[i]!, info)) count++;\n pos = i + 1;\n }\n\n // If we ran through the entire string without reaching the target count,\n // place the cursor at the end. Note: acceptedCount === 0 means we correctly\n // want pos = 0 (set in the loop), so we must NOT override that case.\n if (acceptedCount > 0 && count < acceptedCount) {\n pos = newFormatted.length;\n }\n\n // Snap to nearest valid boundary\n pos = snapToBoundary(pos, boundary);\n\n return pos;\n}\n\n/**\n * If `pos` is at a false (invalid) boundary position, find the nearest\n * true position. Prefers moving forward; falls back to backward.\n */\nfunction snapToBoundary(pos: number, boundary: CaretBoundary): number {\n if (boundary[pos]) return pos;\n\n // Try forward first\n for (let i = pos + 1; i < boundary.length; i++) {\n if (boundary[i]) return i;\n }\n // Fall back to backward\n for (let i = pos - 1; i >= 0; i--) {\n if (boundary[i]) return i;\n }\n return 0;\n}\n"]}
1
+ {"version":3,"sources":["../src/core/presets.ts","../src/core/normalizer.ts","../src/core/formatter.ts","../src/core/parser.ts","../src/core/cursor.ts"],"names":["presets","code","unit","BUILTIN_DIGIT_BLOCKS","registeredBlocks","registerLocale","config","block","s","normalizeDigits","input","ch","start","end","digit","formatterCache","getFormatter","locale","options","key","fmt","extractLocaleInfo","styledFmt","probeFmt","parts","decimalSeparator","groupingSeparator","minusSign","zero","part","rtlLocales","resolvedLocale","isRTL","createFormatter","opts","intlOptions","intlFmt","cachedLocaleInfo","getLocaleInfo","formatToParts","value","result","format","formatted","formatResult","p","isIntermediateStripped","stripped","allowDecimal","createParser","allowNegative","isPercent","currencySymbol","stripAffordances","raw","info","accountingMatch","negative","parse","n","isIntermediate","isAccepted","countAcceptedBefore","str","cursor","normalised","count","i","getCaretBoundary","formattedValue","len","boundary","computeNewCursorPosition","oldInput","oldCursor","newFormatted","inputType","acceptedCount","before","minusBeforeCaret","newHasMinus","normNew","pos","snapToBoundary"],"mappings":"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","file":"core.cjs","sourcesContent":["/**\n * Format presets — named Intl.NumberFormatOptions configurations for common\n * number input patterns. Use these as the `formatOptions` prop value.\n *\n * @example\n * import { presets } from 'raqam'\n * <NumberField.Root formatOptions={presets.currency('USD')} />\n * <NumberField.Root formatOptions={presets.percent} />\n * <NumberField.Root formatOptions={presets.compact} />\n */\n\nexport const presets = {\n /** Currency with standard sign display. Shorthand for `{ style:'currency', currency:code }`. */\n currency: (code: string): Intl.NumberFormatOptions => ({\n style: \"currency\",\n currency: code,\n }),\n\n /**\n * Accounting currency — negatives shown as `(1,234.56)` instead of `-$1,234.56`.\n * Requires the accounting format parser fix (built-in to raqam).\n */\n accounting: (code: string): Intl.NumberFormatOptions => ({\n style: \"currency\",\n currency: code,\n currencySign: \"accounting\",\n }),\n\n /** Percentage — formats 0.42 as \"42%\" */\n percent: { style: \"percent\" } as Intl.NumberFormatOptions,\n\n /** Compact short — \"1.2K\", \"3.4M\" */\n compact: { notation: \"compact\" } as Intl.NumberFormatOptions,\n\n /** Compact long — \"1.2 thousand\", \"3.4 million\" */\n compactLong: {\n notation: \"compact\",\n compactDisplay: \"long\",\n } as Intl.NumberFormatOptions,\n\n /** Scientific notation — \"1.234E3\" */\n scientific: { notation: \"scientific\" } as Intl.NumberFormatOptions,\n\n /** Engineering notation — exponents always multiples of 3 */\n engineering: { notation: \"engineering\" } as Intl.NumberFormatOptions,\n\n /** Integer — no decimal places */\n integer: { maximumFractionDigits: 0 } as Intl.NumberFormatOptions,\n\n /**\n * Financial — always exactly 2 decimal places (fixed scale).\n * Combine with `fixedDecimalScale` prop for strict display.\n */\n financial: {\n minimumFractionDigits: 2,\n maximumFractionDigits: 2,\n } as Intl.NumberFormatOptions,\n\n /**\n * Unit — shorthand for `{ style:'unit', unit:unitCode }`.\n * @example presets.unit('kilometer-per-hour') → { style:'unit', unit:'kilometer-per-hour' }\n */\n unit: (unit: string): Intl.NumberFormatOptions => ({\n style: \"unit\",\n unit,\n }),\n} as const;\n","import type { DigitBlock } from \"./types.js\";\n\n// ── Built-in digit blocks ────────────────────────────────────────────────────\n// These cover the digit systems required by the spec.\n// Additional blocks can be registered via registerLocale().\n\nconst BUILTIN_DIGIT_BLOCKS: DigitBlock[] = [\n [0x0660, 0x0669], // Arabic-Indic (arab)\n [0x06f0, 0x06f9], // Extended Arabic-Indic / Persian (arabext)\n [0x0966, 0x096f], // Devanagari / Hindi (deva)\n [0x09e6, 0x09ef], // Bengali (beng)\n [0x0e50, 0x0e59], // Thai (thai)\n];\n\n// Mutable registry — locale plugins can add blocks here\nconst registeredBlocks: DigitBlock[] = [...BUILTIN_DIGIT_BLOCKS];\n\n// ── Public API ────────────────────────────────────────────────────────────────\n\nexport interface LocaleConfig {\n /** Extra digit block ranges to register */\n digitBlocks?: DigitBlock[];\n}\n\n/**\n * Register additional digit blocks (called by locale plugins as a side effect).\n * Duplicate ranges are silently ignored.\n */\nexport function registerLocale(config: LocaleConfig): void {\n if (!config.digitBlocks) return;\n for (const block of config.digitBlocks) {\n const already = registeredBlocks.some(([s]) => s === block[0]);\n if (!already) registeredBlocks.push(block);\n }\n}\n\n/**\n * Normalise any Unicode decimal digit in `input` to its ASCII equivalent (0–9).\n * Non-digit characters pass through unchanged.\n */\nexport function normalizeDigits(input: string): string {\n // Fast path: if there are no non-ASCII chars, return as-is\n if (!/[^\\u0020-\\u007e]/.test(input)) return input;\n\n return input.replace(/\\p{Nd}/gu, (ch) => {\n const code = ch.codePointAt(0)!;\n for (const [start, end] of registeredBlocks) {\n if (code >= start && code <= end) {\n return String(code - start);\n }\n }\n // Fallback: let JS try to parse it as a decimal digit\n const digit = Number.parseInt(ch, 10);\n return Number.isNaN(digit) ? ch : String(digit);\n });\n}\n\n/**\n * Inverse of {@link normalizeDigits}: map ASCII digits 0–9 to the locale's\n * native digit block (identified by its \"zero\" character). No-op when the\n * locale already uses ASCII digits. Used to keep intermediate (still-typing)\n * display strings in the user's native script.\n */\nexport function localizeDigits(input: string, zero: string): string {\n if (!zero || zero === \"0\") return input;\n const base = zero.codePointAt(0);\n if (base === undefined) return input;\n return input.replace(/[0-9]/g, (d) => String.fromCodePoint(base + (d.charCodeAt(0) - 48)));\n}\n\n/**\n * Returns true if the character is a non-Latin Unicode decimal digit\n * (i.e. would need normalization).\n */\nexport function isNonLatinDigit(ch: string): boolean {\n const code = ch.codePointAt(0);\n if (code === undefined) return false;\n if (code >= 0x30 && code <= 0x39) return false; // ASCII 0-9\n for (const [start, end] of registeredBlocks) {\n if (code >= start && code <= end) return true;\n }\n return false;\n}\n","import type { FormatResult, LocaleInfo } from \"./types.js\";\n\n// ── Internal ──────────────────────────────────────────────────────────────────\n\n/** Probe value that will surface decimal AND grouping parts */\nconst PROBE_VALUE = 12345.6;\n\n/** Cache key = locale + JSON.stringify(options) */\nconst formatterCache = new Map<string, Intl.NumberFormat>();\n\nfunction getFormatter(\n locale: string | undefined,\n options: Intl.NumberFormatOptions | undefined\n): Intl.NumberFormat {\n const key = `${locale ?? \"\"}::${JSON.stringify(options ?? {})}`;\n let fmt = formatterCache.get(key);\n if (!fmt) {\n fmt = new Intl.NumberFormat(locale, options);\n formatterCache.set(key, fmt);\n }\n return fmt;\n}\n\n/** Extract locale meta from formatToParts — never hardcoded. */\nfunction extractLocaleInfo(\n locale: string | undefined,\n options: Intl.NumberFormatOptions | undefined\n): LocaleInfo {\n const styledFmt = getFormatter(locale, options);\n\n // Probe the separators with a NEUTRAL decimal formatter (same locale +\n // numbering system, but plain decimal style with a forced fraction). The\n // styled formatter can hide them — e.g. percent scales the probe so it has no\n // fraction (decimal separator invisible) and a positive value never yields a\n // minusSign. A negative, fractional decimal probe always surfaces all four.\n const probeFmt = getFormatter(locale, {\n minimumFractionDigits: 1,\n maximumFractionDigits: 1,\n numberingSystem: options?.numberingSystem,\n });\n const parts = probeFmt.formatToParts(-PROBE_VALUE);\n\n let decimalSeparator = \".\";\n let groupingSeparator = \",\";\n let minusSign = \"-\";\n let zero = \"0\";\n\n for (const part of parts) {\n if (part.type === \"decimal\") decimalSeparator = part.value;\n if (part.type === \"group\") groupingSeparator = part.value;\n if (part.type === \"minusSign\") minusSign = part.value;\n }\n\n // Detect locale zero digit\n for (const part of probeFmt.formatToParts(0)) {\n if (part.type === \"integer\") {\n zero = part.value;\n break;\n }\n }\n\n // RTL locales: Arabic / Hebrew / Persian / Urdu / Syriac etc.\n const rtlLocales = /^(ar|he|fa|ur|syc|nqo|ug|yi)/i;\n const resolvedLocale = styledFmt.resolvedOptions().locale;\n const isRTL = rtlLocales.test(resolvedLocale);\n\n return { decimalSeparator, groupingSeparator, minusSign, zero, isRTL };\n}\n\n// ── Factory ───────────────────────────────────────────────────────────────────\n\nexport interface FormatterOptions {\n locale?: string;\n formatOptions?: Intl.NumberFormatOptions;\n prefix?: string;\n suffix?: string;\n minimumFractionDigits?: number;\n maximumFractionDigits?: number;\n fixedDecimalScale?: boolean;\n}\n\nexport interface Formatter {\n format(value: number): string;\n formatToParts(value: number): Intl.NumberFormatPart[];\n getLocaleInfo(): LocaleInfo;\n formatResult(value: number): FormatResult;\n}\n\n/**\n * Create a formatter instance. Intl.NumberFormat is cached — safe to call\n * on every render.\n */\nexport function createFormatter(opts: FormatterOptions): Formatter {\n // Merge fraction digit overrides into formatOptions\n const intlOptions: Intl.NumberFormatOptions = { ...opts.formatOptions };\n\n if (opts.minimumFractionDigits !== undefined) {\n intlOptions.minimumFractionDigits = opts.minimumFractionDigits;\n }\n if (opts.maximumFractionDigits !== undefined) {\n intlOptions.maximumFractionDigits = opts.maximumFractionDigits;\n }\n if (opts.fixedDecimalScale && opts.maximumFractionDigits !== undefined) {\n intlOptions.minimumFractionDigits = opts.maximumFractionDigits;\n intlOptions.maximumFractionDigits = opts.maximumFractionDigits;\n }\n\n const intlFmt = getFormatter(opts.locale, intlOptions);\n // Lazy — computed once on first call\n let cachedLocaleInfo: LocaleInfo | null = null;\n\n function getLocaleInfo(): LocaleInfo {\n if (!cachedLocaleInfo) {\n cachedLocaleInfo = extractLocaleInfo(opts.locale, intlOptions);\n }\n return cachedLocaleInfo;\n }\n\n function formatToParts(value: number): Intl.NumberFormatPart[] {\n const parts = intlFmt.formatToParts(value);\n if (!opts.prefix && !opts.suffix) return parts;\n\n const result: Intl.NumberFormatPart[] = [];\n if (opts.prefix) result.push({ type: \"literal\", value: opts.prefix });\n result.push(...parts);\n if (opts.suffix) result.push({ type: \"literal\", value: opts.suffix });\n return result;\n }\n\n function format(value: number): string {\n if (!Number.isFinite(value)) return \"\";\n const formatted = intlFmt.format(value);\n return (opts.prefix ?? \"\") + formatted + (opts.suffix ?? \"\");\n }\n\n function formatResult(value: number): FormatResult {\n const parts = formatToParts(value);\n const formatted = parts.map((p) => p.value).join(\"\");\n return { formatted, parts };\n }\n\n return { format, formatToParts, getLocaleInfo, formatResult };\n}\n","import { createFormatter } from \"./formatter.js\";\nimport { normalizeDigits } from \"./normalizer.js\";\nimport type { LocaleInfo, ParseResult } from \"./types.js\";\n\n// ── Helpers ───────────────────────────────────────────────────────────────────\n\n/**\n * Given an ASCII-normalised numeric string (`stripped` — digits, an optional\n * leading \"-\", and at most one \".\"), report whether it is a valid-but-incomplete\n * value that must NOT be reformatted while the user is still typing:\n * \"1.\" trailing decimal separator\n * \"1.0\" trailing zero after decimal\n * \"1.50\" trailing zeros after decimal\n * \".5\" leading decimal point (normalised to \"0.5\" only on blur)\n *\n * This runs on the *stripped* string, so grouping separators, prefixes,\n * suffixes and currency symbols never break the detection.\n */\nfunction isIntermediateStripped(stripped: string, allowDecimal: boolean): boolean {\n if (!allowDecimal) return false;\n if (/^-0+$/.test(stripped)) return true; // \"-0\" — user may still type \"-0.5\"\n if (/\\.$/.test(stripped)) return true; // \"1.\", \"-1.\"\n if (/\\.\\d*0$/.test(stripped)) return true; // \"1.0\", \"1.50\", \"1.230\"\n if (/^-?\\.\\d+$/.test(stripped)) return true; // \".5\", \"-.5\"\n return false;\n}\n\n// ── Factory ───────────────────────────────────────────────────────────────────\n\nexport interface ParserOptions {\n locale?: string;\n formatOptions?: Intl.NumberFormatOptions;\n allowNegative?: boolean;\n allowDecimal?: boolean;\n prefix?: string;\n suffix?: string;\n}\n\nexport interface Parser {\n parse(input: string): ParseResult;\n isIntermediate(input: string): boolean;\n getLocaleInfo(): LocaleInfo;\n}\n\n/**\n * Create a locale-aware parser. Separator characters are extracted from\n * Intl.NumberFormat — never hardcoded.\n */\nexport function createParser(opts: ParserOptions = {}): Parser {\n const allowNegative = opts.allowNegative ?? true;\n const allowDecimal = opts.allowDecimal ?? true;\n // Percent fields hold the *fraction* (Intl multiplies by 100 on display), so\n // typed digits must be divided by 100: typing \"50\" means 50% i.e. value 0.5.\n const isPercent = opts.formatOptions?.style === \"percent\";\n\n // Re-use the formatter to get locale info\n const fmt = createFormatter({\n locale: opts.locale,\n formatOptions: opts.formatOptions,\n prefix: opts.prefix,\n suffix: opts.suffix,\n });\n\n // The literal currency symbol (e.g. \"$\", \"€\", or Arabic \"ج.م.\" which embeds\n // ASCII dots). Captured so it can be removed wholesale before separator\n // handling — otherwise its dots would be mistaken for a decimal point.\n let currencySymbol = \"\";\n if (opts.formatOptions?.style === \"currency\") {\n try {\n currencySymbol = fmt\n .formatToParts(1)\n .filter((p) => p.type === \"currency\")\n .map((p) => p.value)\n .join(\"\");\n } catch {\n currencySymbol = \"\";\n }\n }\n\n function getLocaleInfo(): LocaleInfo {\n return fmt.getLocaleInfo();\n }\n\n function stripAffordances(raw: string): string {\n const info = getLocaleInfo();\n\n // 1. Normalise non-Latin digits to ASCII\n let s = normalizeDigits(raw);\n\n // 1b. Remove the currency symbol wholesale (before separators are touched),\n // so a symbol containing ASCII dots — e.g. Arabic \"ج.م.\" — does not leave\n // stray \".\" that the numeric validation would reject.\n if (currencySymbol) {\n s = s.split(currencySymbol).join(\"\");\n }\n\n // 2. Accounting format: \"(1,234.56)\" or \"($1,234.56)\" → negative\n // Intl.NumberFormat with currencySign:\"accounting\" wraps negatives in parens\n const accountingMatch = s.match(/^\\((.+)\\)$/);\n if (accountingMatch) {\n s = `-${accountingMatch[1]}`;\n }\n\n // 3. Strip prefix / suffix\n if (opts.prefix && s.startsWith(opts.prefix)) {\n s = s.slice(opts.prefix.length);\n }\n if (opts.suffix && s.endsWith(opts.suffix)) {\n s = s.slice(0, -opts.suffix.length);\n }\n\n // 4. Strip grouping separators (escape special chars)\n if (info.groupingSeparator) {\n s = s.split(info.groupingSeparator).join(\"\");\n }\n\n // 5. Replace locale decimal separator with ASCII \".\"\n if (info.decimalSeparator !== \".\") {\n s = s.split(info.decimalSeparator).join(\".\");\n }\n\n // 6. Replace locale minus sign with ASCII \"-\"\n if (info.minusSign !== \"-\") {\n s = s.split(info.minusSign).join(\"-\");\n }\n // Also normalise the Unicode minus U+2212 (used by fa/sv/fi/nb… and often\n // pasted) so it is never stripped as a non-numeric character below.\n if (s.includes(\"−\")) {\n s = s.split(\"−\").join(\"-\");\n }\n\n // 7. Strip currency symbol, percent sign, spaces that Intl might prepend/append\n // Strip any remaining non-numeric chars except digits, \".\", \"-\"\n // (handles currency prefixes/suffixes from Intl)\n s = s.replace(/[^\\d.\\-]/g, \"\").trim();\n\n // 8. Collapse minus signs to a single leading one, so stray minuses typed in\n // the middle (\"1-23\") or doubled (\"--5\") never invalidate the whole value.\n if (s.includes(\"-\")) {\n const negative = s.startsWith(\"-\");\n s = s.replace(/-/g, \"\");\n if (negative) s = `-${s}`;\n }\n\n return s;\n }\n\n function parse(input: string): ParseResult {\n if (!input || input.trim() === \"\") {\n return { value: null, isValid: false, isIntermediate: false };\n }\n\n // Strip *first*, so intermediate detection never trips over grouping\n // separators, prefixes, suffixes or currency symbols.\n const stripped = stripAffordances(input);\n\n if (stripped === \"\") {\n return { value: null, isValid: false, isIntermediate: false };\n }\n\n // Lone minus — intermediate only if negatives are allowed.\n if (stripped === \"-\") {\n return { value: null, isValid: false, isIntermediate: allowNegative };\n }\n\n // Lone decimal point (\".\" or \"-.\") — intermediate only if decimals allowed.\n if (stripped === \".\" || stripped === \"-.\") {\n return {\n value: null,\n isValid: false,\n isIntermediate: allowDecimal && (stripped === \".\" || allowNegative),\n };\n }\n\n if (!allowNegative && stripped.startsWith(\"-\")) {\n return { value: null, isValid: false, isIntermediate: false };\n }\n\n if (!allowDecimal && stripped.includes(\".\")) {\n return { value: null, isValid: false, isIntermediate: false };\n }\n\n // Accept integers and decimals with the dot either trailing (\"1.\") or\n // leading (\".5\") so partially-typed values still yield a numeric value.\n if (!/^-?(?:\\d+\\.?\\d*|\\.\\d+)$/.test(stripped)) {\n return { value: null, isValid: false, isIntermediate: false };\n }\n\n let n = Number.parseFloat(stripped);\n if (!Number.isFinite(n)) {\n return { value: null, isValid: false, isIntermediate: false };\n }\n // Normalise negative zero so consumers never receive -0 from onChange.\n if (Object.is(n, -0)) n = 0;\n // Percent: divide by 100, snapping to canonical precision so the IEEE-754\n // division artifact (12.99/100 = 0.12990000000000002) never surfaces.\n if (isPercent && n !== 0) n = Number((n / 100).toPrecision(15));\n\n // A trailing-zero / trailing-dot / leading-dot value is still a real number\n // (so it is never wiped on blur) but must not be reformatted mid-typing.\n return {\n value: n,\n isValid: true,\n isIntermediate: isIntermediateStripped(stripped, allowDecimal),\n };\n }\n\n function isIntermediate(input: string): boolean {\n return parse(input).isIntermediate;\n }\n\n return { parse, isIntermediate, getLocaleInfo };\n}\n","import { normalizeDigits } from \"./normalizer.js\";\nimport type { CaretBoundary, LocaleInfo } from \"./types.js\";\n\n// ── Accepted-character helpers ────────────────────────────────────────────────\n\n/**\n * Returns true if `ch` is an \"accepted\" character — one that the user typed\n * intentionally and that contributes to the numeric value:\n * - ASCII digit 0-9\n * - The locale decimal separator\n * - The locale minus sign\n */\nfunction isAccepted(ch: string, info: LocaleInfo): boolean {\n if (ch >= \"0\" && ch <= \"9\") return true;\n if (ch === info.decimalSeparator) return true;\n if (ch === info.minusSign || ch === \"-\") return true;\n // ASCII \".\" stands in for the decimal point in locales whose separator is\n // non-ASCII (ar/fa: ٫) — but not when \".\" is the grouping separator (de-DE).\n if (ch === \".\" && info.decimalSeparator !== \".\" && info.groupingSeparator !== \".\") return true;\n return false;\n}\n\n/**\n * Count how many \"accepted\" characters appear before position `cursor`\n * in `str` (after normalising non-Latin digits to ASCII).\n */\nfunction countAcceptedBefore(str: string, cursor: number, info: LocaleInfo): number {\n const normalised = normalizeDigits(str);\n let count = 0;\n for (let i = 0; i < cursor && i < normalised.length; i++) {\n if (isAccepted(normalised[i]!, info)) count++;\n }\n return count;\n}\n\n// ── Caret boundary ────────────────────────────────────────────────────────────\n\n/**\n * Build a boolean array of length `formattedValue.length + 1`.\n * `true` → cursor may rest at this position.\n * `false` → cursor must snap away (sits inside a formatting-only character\n * such as a grouping separator or a currency prefix).\n *\n * Rules:\n * - Start and end positions are always valid.\n * - A position immediately AFTER a grouping separator is invalid (the\n * cursor would look like it's between two digits but moving left would\n * skip the comma).\n * - A position immediately BEFORE a grouping separator is valid.\n */\nexport function getCaretBoundary(formattedValue: string, info: LocaleInfo): CaretBoundary {\n const len = formattedValue.length;\n const boundary: CaretBoundary = new Array(len + 1).fill(true) as boolean[];\n\n for (let i = 0; i < len; i++) {\n const ch = formattedValue[i]!;\n if (ch === info.groupingSeparator) {\n // Position immediately after the grouping separator is invalid\n boundary[i + 1] = false;\n }\n }\n\n // First and last are always valid\n boundary[0] = true;\n boundary[len] = true;\n\n return boundary;\n}\n\n// ── Cursor computation ────────────────────────────────────────────────────────\n\n/**\n * Compute the new cursor position in `newFormatted` that corresponds\n * semantically to `oldCursor` in `oldInput`.\n *\n * Algorithm (3 stages from the spec):\n *\n * 1. Count accepted characters before `oldCursor` in `oldInput`.\n * 2. Adjust for backspace-over-separator edge case.\n * 3. Walk `newFormatted` to find the position where the same count of\n * accepted chars precede it; snap to the nearest valid boundary.\n *\n * @param oldInput The raw string the user just typed into (pre-format)\n * @param oldCursor selectionStart captured from the native event\n * @param newFormatted The formatted string we're about to display\n * @param info Locale separators\n * @param inputType e.nativeEvent.inputType (optional — for backspace detection)\n */\nexport function computeNewCursorPosition(\n oldInput: string,\n oldCursor: number,\n newFormatted: string,\n info: LocaleInfo,\n inputType?: string\n): number {\n const normalised = normalizeDigits(oldInput);\n\n // Stage 1: count accepted chars before cursor in old input\n let acceptedCount = countAcceptedBefore(normalised, oldCursor, info);\n\n // Stage 1b: accounting/parens negatives drop the minus sign. If a minus was\n // counted before the caret but the reformatted output represents the sign\n // without one (e.g. \"($1.00)\"), the accepted-char counts are off by one —\n // correct for it so integer digits don't land inside the fraction.\n const before = oldInput.slice(0, oldCursor);\n const minusBeforeCaret = before.includes(\"-\") || before.includes(info.minusSign);\n const newHasMinus = newFormatted.includes(\"-\") || newFormatted.includes(info.minusSign);\n if (minusBeforeCaret && !newHasMinus) {\n acceptedCount = Math.max(0, acceptedCount - 1);\n }\n\n // Stage 2: backspace on grouping separator — also delete the preceding digit\n if (\n inputType === \"deleteContentBackward\" &&\n oldCursor > 0 &&\n oldInput[oldCursor - 1] === info.groupingSeparator\n ) {\n // The separator was deleted but we want to remove the preceding digit too\n acceptedCount = Math.max(0, acceptedCount - 1);\n }\n\n // Stage 3: build boundary and walk new formatted string\n const boundary = getCaretBoundary(newFormatted, info);\n const normNew = normalizeDigits(newFormatted);\n let count = 0;\n let pos = 0;\n\n for (let i = 0; i < normNew.length; i++) {\n if (count === acceptedCount) {\n pos = i;\n break;\n }\n if (isAccepted(normNew[i]!, info)) count++;\n pos = i + 1;\n }\n\n // If we ran through the entire string without reaching the target count,\n // place the cursor at the end. Note: acceptedCount === 0 means we correctly\n // want pos = 0 (set in the loop), so we must NOT override that case.\n if (acceptedCount > 0 && count < acceptedCount) {\n pos = newFormatted.length;\n }\n\n // Snap to nearest valid boundary\n pos = snapToBoundary(pos, boundary);\n\n return pos;\n}\n\n/**\n * If `pos` is at a false (invalid) boundary position, find the nearest\n * true position. Prefers moving forward; falls back to backward.\n */\nfunction snapToBoundary(pos: number, boundary: CaretBoundary): number {\n if (boundary[pos]) return pos;\n\n // Try forward first\n for (let i = pos + 1; i < boundary.length; i++) {\n if (boundary[i]) return i;\n }\n // Fall back to backward\n for (let i = pos - 1; i >= 0; i--) {\n if (boundary[i]) return i;\n }\n return 0;\n}\n"]}
package/dist/core.d.cts CHANGED
@@ -143,8 +143,12 @@ interface NumberFieldState {
143
143
  _setLastChangeReason: (reason: ChangeReason) => void;
144
144
  /** Internal: read the current change reason (used by NumberField.Root) */
145
145
  _getLastChangeReason: () => ChangeReason;
146
- /** Update display string (triggers parse + onChange) */
147
- setInputValue: (val: string) => void;
146
+ /**
147
+ * Update display string (triggers parse + onChange). `knownValue` overrides
148
+ * the parsed value when `val` is a formatted string that cannot be reversed
149
+ * (compact "2.5K", scientific, unit notation).
150
+ */
151
+ setInputValue: (val: string, knownValue?: number | null) => void;
148
152
  /** Directly set the numeric value (triggers format + onChange) */
149
153
  setNumberValue: (val: number | null) => void;
150
154
  /** Format + clamp on blur — call from onBlur */
package/dist/core.d.ts CHANGED
@@ -143,8 +143,12 @@ interface NumberFieldState {
143
143
  _setLastChangeReason: (reason: ChangeReason) => void;
144
144
  /** Internal: read the current change reason (used by NumberField.Root) */
145
145
  _getLastChangeReason: () => ChangeReason;
146
- /** Update display string (triggers parse + onChange) */
147
- setInputValue: (val: string) => void;
146
+ /**
147
+ * Update display string (triggers parse + onChange). `knownValue` overrides
148
+ * the parsed value when `val` is a formatted string that cannot be reversed
149
+ * (compact "2.5K", scientific, unit notation).
150
+ */
151
+ setInputValue: (val: string, knownValue?: number | null) => void;
148
152
  /** Directly set the numeric value (triggers format + onChange) */
149
153
  setNumberValue: (val: number | null) => void;
150
154
  /** Format + clamp on blur — call from onBlur */
package/dist/core.js CHANGED
@@ -1,2 +1,2 @@
1
- var L={currency:e=>({style:"currency",currency:e}),accounting:e=>({style:"currency",currency:e,currencySign:"accounting"}),percent:{style:"percent"},compact:{notation:"compact"},compactLong:{notation:"compact",compactDisplay:"long"},scientific:{notation:"scientific"},engineering:{notation:"engineering"},integer:{maximumFractionDigits:0},financial:{minimumFractionDigits:2,maximumFractionDigits:2},unit:e=>({style:"unit",unit:e})};var O=[[1632,1641],[1776,1785],[2406,2415],[2534,2543],[3664,3673]],p=[...O];function D(e){if(e.digitBlocks)for(let t of e.digitBlocks)p.some(([i])=>i===t[0])||p.push(t);}function g(e){return /[^\u0020-\u007e]/.test(e)?e.replace(/\p{Nd}/gu,t=>{let r=t.codePointAt(0);for(let[o,s]of p)if(r>=o&&r<=s)return String(r-o);let i=Number.parseInt(t,10);return Number.isNaN(i)?t:String(i)}):e}var x=new Map;function F(e,t){let r=`${e??""}::${JSON.stringify(t??{})}`,i=x.get(r);return i||(i=new Intl.NumberFormat(e,t),x.set(r,i)),i}function P(e,t){let r=F(e,t),i=r.formatToParts(12345.6),o=".",s=",",f="-",c="0";for(let m of i)m.type==="decimal"&&(o=m.value),m.type==="group"&&(s=m.value),m.type==="minusSign"&&(f=m.value);let u=r.formatToParts(0);for(let m of u)if(m.type==="integer"){c=m.value;break}let a=/^(ar|he|fa|ur|syc|nqo|ug|yi)/i,n=r.resolvedOptions().locale,l=a.test(n);return {decimalSeparator:o,groupingSeparator:s,minusSign:f,zero:c,isRTL:l}}function d(e){let t={...e.formatOptions};e.minimumFractionDigits!==void 0&&(t.minimumFractionDigits=e.minimumFractionDigits),e.maximumFractionDigits!==void 0&&(t.maximumFractionDigits=e.maximumFractionDigits),e.fixedDecimalScale&&e.maximumFractionDigits!==void 0&&(t.minimumFractionDigits=e.maximumFractionDigits,t.maximumFractionDigits=e.maximumFractionDigits);let r=F(e.locale,t),i=null;function o(){return i||(i=P(e.locale,t)),i}function s(u){let a=r.formatToParts(u);if(!e.prefix&&!e.suffix)return a;let n=[];return e.prefix&&n.push({type:"literal",value:e.prefix}),n.push(...a),e.suffix&&n.push({type:"literal",value:e.suffix}),n}function f(u){if(!Number.isFinite(u))return "";let a=r.format(u);return (e.prefix??"")+a+(e.suffix??"")}function c(u){let a=s(u);return {formatted:a.map(l=>l.value).join(""),parts:a}}return {format:f,formatToParts:s,getLocaleInfo:o,formatResult:c}}function I(e){return e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}function b(e,t,r,i){let o=I(t.decimalSeparator),s=I(t.minusSign);return !!(r&&(e==="-"||e===t.minusSign)||i&&new RegExp(`^${s}?\\d+${o}$`).test(e)||i&&new RegExp(`^${s}?\\d+${o}\\d*0+$`).test(e))}function S(e={}){let t=e.allowNegative??true,r=e.allowDecimal??true,i=d({locale:e.locale,formatOptions:e.formatOptions,prefix:e.prefix,suffix:e.suffix});function o(){return i.getLocaleInfo()}function s(u){let a=o(),n=g(u),l=n.match(/^\((.+)\)$/);return l&&(n=`-${l[1]}`),e.prefix&&n.startsWith(e.prefix)&&(n=n.slice(e.prefix.length)),e.suffix&&n.endsWith(e.suffix)&&(n=n.slice(0,-e.suffix.length)),a.groupingSeparator&&(n=n.split(a.groupingSeparator).join("")),a.decimalSeparator!=="."&&(n=n.split(a.decimalSeparator).join(".")),a.minusSign!=="-"&&(n=n.split(a.minusSign).join("-")),n=n.replace(/[^\d.\-]/g,"").trim(),n}function f(u){if(!u||u.trim()==="")return {value:null,isValid:false,isIntermediate:false};let a=o();if(b(u,a,t,r))return {value:null,isValid:false,isIntermediate:true};let n=s(u);if(n==="")return {value:null,isValid:false,isIntermediate:false};if(n==="-")return {value:null,isValid:false,isIntermediate:t};if(!/^-?\d+\.?\d*$/.test(n))return {value:null,isValid:false,isIntermediate:false};if(!t&&n.startsWith("-"))return {value:null,isValid:false,isIntermediate:false};if(!r&&n.includes("."))return {value:null,isValid:false,isIntermediate:false};let l=Number.parseFloat(n);return Number.isFinite(l)?{value:l,isValid:true,isIntermediate:false}:{value:null,isValid:false,isIntermediate:false}}function c(u){let a=o();return b(u,a,t,r)}return {parse:f,isIntermediate:c,getLocaleInfo:o}}function y(e,t){return e>="0"&&e<="9"||e===t.decimalSeparator||e===t.minusSign||e==="-"}function v(e,t,r){let i=g(e),o=0;for(let s=0;s<t&&s<i.length;s++)y(i[s],r)&&o++;return o}function N(e,t){let r=e.length,i=new Array(r+1).fill(true);for(let o=0;o<r;o++)e[o]===t.groupingSeparator&&(i[o+1]=false);return i[0]=true,i[r]=true,i}function B(e,t,r,i,o){let s=g(e),f=v(s,t,i);o==="deleteContentBackward"&&t>0&&e[t-1]===i.groupingSeparator&&(f=Math.max(0,f-1));let c=N(r,i),u=g(r),a=0,n=0;for(let l=0;l<u.length;l++){if(a===f){n=l;break}y(u[l],i)&&a++,n=l+1;}return f>0&&a<f&&(n=r.length),n=R(n,c),n}function R(e,t){if(t[e])return e;for(let r=e+1;r<t.length;r++)if(t[r])return r;for(let r=e-1;r>=0;r--)if(t[r])return r;return 0}export{B as computeNewCursorPosition,d as createFormatter,S as createParser,N as getCaretBoundary,g as normalizeDigits,L as presets,D as registerLocale};//# sourceMappingURL=core.js.map
1
+ var D={currency:e=>({style:"currency",currency:e}),accounting:e=>({style:"currency",currency:e,currencySign:"accounting"}),percent:{style:"percent"},compact:{notation:"compact"},compactLong:{notation:"compact",compactDisplay:"long"},scientific:{notation:"scientific"},engineering:{notation:"engineering"},integer:{maximumFractionDigits:0},financial:{minimumFractionDigits:2,maximumFractionDigits:2},unit:e=>({style:"unit",unit:e})};var O=[[1632,1641],[1776,1785],[2406,2415],[2534,2543],[3664,3673]],x=[...O];function L(e){if(e.digitBlocks)for(let r of e.digitBlocks)x.some(([n])=>n===r[0])||x.push(r);}function d(e){return /[^\u0020-\u007e]/.test(e)?e.replace(/\p{Nd}/gu,r=>{let i=r.codePointAt(0);for(let[u,s]of x)if(i>=u&&i<=s)return String(i-u);let n=Number.parseInt(r,10);return Number.isNaN(n)?r:String(n)}):e}var I=new Map;function b(e,r){let i=`${e??""}::${JSON.stringify(r??{})}`,n=I.get(i);return n||(n=new Intl.NumberFormat(e,r),I.set(i,n)),n}function P(e,r){let i=b(e,r),n=b(e,{minimumFractionDigits:1,maximumFractionDigits:1,numberingSystem:r?.numberingSystem}),u=n.formatToParts(-12345.6),s=".",l=",",c="-",f="0";for(let t of u)t.type==="decimal"&&(s=t.value),t.type==="group"&&(l=t.value),t.type==="minusSign"&&(c=t.value);for(let t of n.formatToParts(0))if(t.type==="integer"){f=t.value;break}let m=/^(ar|he|fa|ur|syc|nqo|ug|yi)/i,a=i.resolvedOptions().locale,o=m.test(a);return {decimalSeparator:s,groupingSeparator:l,minusSign:c,zero:f,isRTL:o}}function F(e){let r={...e.formatOptions};e.minimumFractionDigits!==void 0&&(r.minimumFractionDigits=e.minimumFractionDigits),e.maximumFractionDigits!==void 0&&(r.maximumFractionDigits=e.maximumFractionDigits),e.fixedDecimalScale&&e.maximumFractionDigits!==void 0&&(r.minimumFractionDigits=e.maximumFractionDigits,r.maximumFractionDigits=e.maximumFractionDigits);let i=b(e.locale,r),n=null;function u(){return n||(n=P(e.locale,r)),n}function s(f){let m=i.formatToParts(f);if(!e.prefix&&!e.suffix)return m;let a=[];return e.prefix&&a.push({type:"literal",value:e.prefix}),a.push(...m),e.suffix&&a.push({type:"literal",value:e.suffix}),a}function l(f){if(!Number.isFinite(f))return "";let m=i.format(f);return (e.prefix??"")+m+(e.suffix??"")}function c(f){let m=s(f);return {formatted:m.map(o=>o.value).join(""),parts:m}}return {format:l,formatToParts:s,getLocaleInfo:u,formatResult:c}}function S(e,r){return r?!!(/^-0+$/.test(e)||/\.$/.test(e)||/\.\d*0$/.test(e)||/^-?\.\d+$/.test(e)):false}function v(e={}){let r=e.allowNegative??true,i=e.allowDecimal??true,n=e.formatOptions?.style==="percent",u=F({locale:e.locale,formatOptions:e.formatOptions,prefix:e.prefix,suffix:e.suffix}),s="";if(e.formatOptions?.style==="currency")try{s=u.formatToParts(1).filter(a=>a.type==="currency").map(a=>a.value).join("");}catch{s="";}function l(){return u.getLocaleInfo()}function c(a){let o=l(),t=d(a);s&&(t=t.split(s).join(""));let g=t.match(/^\((.+)\)$/);if(g&&(t=`-${g[1]}`),e.prefix&&t.startsWith(e.prefix)&&(t=t.slice(e.prefix.length)),e.suffix&&t.endsWith(e.suffix)&&(t=t.slice(0,-e.suffix.length)),o.groupingSeparator&&(t=t.split(o.groupingSeparator).join("")),o.decimalSeparator!=="."&&(t=t.split(o.decimalSeparator).join(".")),o.minusSign!=="-"&&(t=t.split(o.minusSign).join("-")),t.includes("\u2212")&&(t=t.split("\u2212").join("-")),t=t.replace(/[^\d.\-]/g,"").trim(),t.includes("-")){let p=t.startsWith("-");t=t.replace(/-/g,""),p&&(t=`-${t}`);}return t}function f(a){if(!a||a.trim()==="")return {value:null,isValid:false,isIntermediate:false};let o=c(a);if(o==="")return {value:null,isValid:false,isIntermediate:false};if(o==="-")return {value:null,isValid:false,isIntermediate:r};if(o==="."||o==="-.")return {value:null,isValid:false,isIntermediate:i&&(o==="."||r)};if(!r&&o.startsWith("-"))return {value:null,isValid:false,isIntermediate:false};if(!i&&o.includes("."))return {value:null,isValid:false,isIntermediate:false};if(!/^-?(?:\d+\.?\d*|\.\d+)$/.test(o))return {value:null,isValid:false,isIntermediate:false};let t=Number.parseFloat(o);return Number.isFinite(t)?(Object.is(t,-0)&&(t=0),n&&t!==0&&(t=Number((t/100).toPrecision(15))),{value:t,isValid:true,isIntermediate:S(o,i)}):{value:null,isValid:false,isIntermediate:false}}function m(a){return f(a).isIntermediate}return {parse:f,isIntermediate:m,getLocaleInfo:l}}function y(e,r){return e>="0"&&e<="9"||e===r.decimalSeparator||e===r.minusSign||e==="-"||e==="."&&r.decimalSeparator!=="."&&r.groupingSeparator!=="."}function B(e,r,i){let n=d(e),u=0;for(let s=0;s<r&&s<n.length;s++)y(n[s],i)&&u++;return u}function N(e,r){let i=e.length,n=new Array(i+1).fill(true);for(let u=0;u<i;u++)e[u]===r.groupingSeparator&&(n[u+1]=false);return n[0]=true,n[i]=true,n}function h(e,r,i,n,u){let s=d(e),l=B(s,r,n),c=e.slice(0,r),f=c.includes("-")||c.includes(n.minusSign),m=i.includes("-")||i.includes(n.minusSign);f&&!m&&(l=Math.max(0,l-1)),u==="deleteContentBackward"&&r>0&&e[r-1]===n.groupingSeparator&&(l=Math.max(0,l-1));let a=N(i,n),o=d(i),t=0,g=0;for(let p=0;p<o.length;p++){if(t===l){g=p;break}y(o[p],n)&&t++,g=p+1;}return l>0&&t<l&&(g=i.length),g=C(g,a),g}function C(e,r){if(r[e])return e;for(let i=e+1;i<r.length;i++)if(r[i])return i;for(let i=e-1;i>=0;i--)if(r[i])return i;return 0}export{h as computeNewCursorPosition,F as createFormatter,v as createParser,N as getCaretBoundary,d as normalizeDigits,D as presets,L as registerLocale};//# sourceMappingURL=core.js.map
2
2
  //# sourceMappingURL=core.js.map