ngx-digits-only 1.0.2 → 1.0.3

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 CHANGED
@@ -28,15 +28,15 @@ Import the directive directly into your standalone component:
28
28
  ```typescript
29
29
  import { Component } from '@angular/core';
30
30
  import { ReactiveFormsModule } from '@angular/forms';
31
- import { NgxDigitsOnlyDirective } from 'ngx-digits-only';
31
+ import { DigitsOnlyDirective } from 'digits-only';
32
32
 
33
33
  @Component({
34
34
  selector: 'app-example',
35
35
  standalone: true,
36
- imports: [ReactiveFormsModule, NgxDigitsOnlyDirective],
36
+ imports: [ReactiveFormsModule, DigitsOnlyDirective],
37
37
  template: `
38
38
  <input
39
- ngxDigitsOnly
39
+ digitsOnly
40
40
  [allowDecimal]="true"
41
41
  [decimalPlaces]="2"
42
42
  [allowNegative]="false"
@@ -59,7 +59,7 @@ Standalone directives can still be used inside NgModule-based components — jus
59
59
  ```typescript
60
60
  @Component({
61
61
  standalone: true,
62
- imports: [NgxDigitsOnlyDirective],
62
+ imports: [DigitsOnlyDirective],
63
63
  ...
64
64
  })
65
65
  ```
@@ -918,7 +918,7 @@ function isNamedPattern(value) {
918
918
  // ─────────────────────────────────────────────────────────────────────────────
919
919
  // Directive
920
920
  // ─────────────────────────────────────────────────────────────────────────────
921
- class digitsOnlyDirective {
921
+ class DigitsOnlyDirective {
922
922
  el;
923
923
  renderer;
924
924
  document;
@@ -1759,21 +1759,21 @@ class digitsOnlyDirective {
1759
1759
  ].includes(event.key);
1760
1760
  return isModifierHeld || isNavigationOrEditKey;
1761
1761
  }
1762
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: digitsOnlyDirective, deps: [{ token: i0.ElementRef }, { token: i0.Renderer2 }, { token: DOCUMENT }], target: i0.ɵɵFactoryTarget.Directive });
1763
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.6", type: digitsOnlyDirective, isStandalone: true, selector: "[digitsOnly]", inputs: { decimalPlaces: "decimalPlaces", thousandSeparator: "thousandSeparator", prefix: "prefix", suffix: "suffix", allowNegative: "allowNegative", leadingZeros: "leadingZeros", maxLength: "maxLength", min: "min", max: "max", outputType: "outputType", convertEasternNumerals: "convertEasternNumerals", pattern: "pattern" }, host: { listeners: { "keydown": "onKeyDown($event)", "input": "onInput()", "blur": "onBlur()", "paste": "onPaste($event)", "drop": "onDrop($event)" } }, providers: [
1762
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: DigitsOnlyDirective, deps: [{ token: i0.ElementRef }, { token: i0.Renderer2 }, { token: DOCUMENT }], target: i0.ɵɵFactoryTarget.Directive });
1763
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.6", type: DigitsOnlyDirective, isStandalone: true, selector: "[digitsOnly]", inputs: { decimalPlaces: "decimalPlaces", thousandSeparator: "thousandSeparator", prefix: "prefix", suffix: "suffix", allowNegative: "allowNegative", leadingZeros: "leadingZeros", maxLength: "maxLength", min: "min", max: "max", outputType: "outputType", convertEasternNumerals: "convertEasternNumerals", pattern: "pattern" }, host: { listeners: { "keydown": "onKeyDown($event)", "input": "onInput()", "blur": "onBlur()", "paste": "onPaste($event)", "drop": "onDrop($event)" } }, providers: [
1764
1764
  {
1765
1765
  provide: NG_VALUE_ACCESSOR,
1766
- useExisting: forwardRef(() => digitsOnlyDirective),
1766
+ useExisting: forwardRef(() => DigitsOnlyDirective),
1767
1767
  multi: true,
1768
1768
  },
1769
1769
  {
1770
1770
  provide: NG_VALIDATORS,
1771
- useExisting: forwardRef(() => digitsOnlyDirective),
1771
+ useExisting: forwardRef(() => DigitsOnlyDirective),
1772
1772
  multi: true,
1773
1773
  },
1774
1774
  ], usesOnChanges: true, ngImport: i0 });
1775
1775
  }
1776
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: digitsOnlyDirective, decorators: [{
1776
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.6", ngImport: i0, type: DigitsOnlyDirective, decorators: [{
1777
1777
  type: Directive,
1778
1778
  args: [{
1779
1779
  selector: '[digitsOnly]',
@@ -1781,12 +1781,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.6", ngImpor
1781
1781
  providers: [
1782
1782
  {
1783
1783
  provide: NG_VALUE_ACCESSOR,
1784
- useExisting: forwardRef(() => digitsOnlyDirective),
1784
+ useExisting: forwardRef(() => DigitsOnlyDirective),
1785
1785
  multi: true,
1786
1786
  },
1787
1787
  {
1788
1788
  provide: NG_VALIDATORS,
1789
- useExisting: forwardRef(() => digitsOnlyDirective),
1789
+ useExisting: forwardRef(() => DigitsOnlyDirective),
1790
1790
  multi: true,
1791
1791
  },
1792
1792
  ],
@@ -1843,5 +1843,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.6", ngImpor
1843
1843
  * Generated bundle index. Do not edit.
1844
1844
  */
1845
1845
 
1846
- export { EASTERN_DIGIT_MAP, EASTERN_NUMERAL_REGEX, convertEasternDigits, digitsOnlyDirective, getDigitScript, hasEasternDigits, toWesternNumber };
1846
+ export { DigitsOnlyDirective, EASTERN_DIGIT_MAP, EASTERN_NUMERAL_REGEX, convertEasternDigits, getDigitScript, hasEasternDigits, toWesternNumber };
1847
1847
  //# sourceMappingURL=ngx-digits-only.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"ngx-digits-only.mjs","sources":["../../../projects/digits-only/src/lib/digits-only.regex.ts","../../../projects/digits-only/src/lib/eastern-numerals.ts","../../../projects/digits-only/src/lib/digits-only.directive.ts","../../../projects/digits-only/src/public-api.ts","../../../projects/digits-only/src/ngx-digits-only.ts"],"sourcesContent":["/**\r\n * digits-only.regex.ts\r\n *\r\n * Every regular expression used by the digitsOnly directive lives here.\r\n * Each constant has a plain-English name, a detailed breakdown, and live examples\r\n * so you never have to decode cryptic patterns inside the directive itself.\r\n *\r\n * Import what you need:\r\n * import { REGEX } from './digits-only.regex';\r\n * REGEX.SINGLE_DIGIT.test('5') // true\r\n */\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// HOW TO READ REGEX ANATOMY\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n//\r\n// /pattern/flags\r\n// │ │\r\n// │ └── modifiers: g = global (find ALL matches, not just first)\r\n// │ i = case-insensitive\r\n// │ m = multiline\r\n// │\r\n// └── special characters:\r\n// ^ start of string (or line with m flag)\r\n// $ end of string (or line with m flag)\r\n// . any single character except newline\r\n// \\d any digit [0-9]\r\n// \\D any NON-digit\r\n// \\w word character [a-zA-Z0-9_]\r\n// \\s whitespace\r\n// \\B NON-word boundary position\r\n// [abc] character class – matches a, b, or c\r\n// [^abc] negated class – matches anything EXCEPT a, b, c\r\n// (abc) capture group\r\n// (?=x) positive lookahead – \"followed by x\" (zero-width, no consume)\r\n// (?!x) negative lookahead – \"NOT followed by x\"\r\n// {n} exactly n repetitions\r\n// {n,} n or more repetitions\r\n// + one or more (greedy)\r\n// * zero or more (greedy)\r\n// ? zero or one (optional)\r\n// \\. escaped dot – a literal '.' character\r\n// \\\\ escaped backslash – a literal '\\'\r\n// \\- escaped hyphen inside [] – a literal '-'\r\n// $& in replacement strings: the entire matched text\r\n\r\nexport const REGEX = {\r\n\r\n // ──────────────────────────────────────────────────────────────────────────\r\n // 1. SINGLE_DIGIT\r\n // Is this one keyboard character exactly a digit and nothing else?\r\n // ──────────────────────────────────────────────────────────────────────────\r\n //\r\n // Pattern breakdown:\r\n // ^ – anchor: string must START here\r\n // \\d – one digit character (0, 1, 2 … 9)\r\n // $ – anchor: string must END here\r\n //\r\n // The ^ and $ together mean the whole string must be exactly one digit.\r\n // Without them, /\\d/ would match '5' inside 'abc5xyz' too.\r\n //\r\n // Used in: onKeyDown() — to decide if a pressed key is a digit\r\n //\r\n // ✔ matches: '0' '5' '9'\r\n // ✖ no match: 'a' '-' '.' 'Enter' '12' ''\r\n //\r\n SINGLE_DIGIT: /^\\d$/,\r\n\r\n // ──────────────────────────────────────────────────────────────────────────\r\n // 2. NON_DIGIT_CHARACTERS\r\n // Find (and remove) every character that is NOT a digit.\r\n // ──────────────────────────────────────────────────────────────────────────\r\n //\r\n // Pattern breakdown:\r\n // [^0-9] – negated character class\r\n // ^ inside [] = NOT\r\n // 0-9 = any digit\r\n // → \"any character that is not 0 through 9\"\r\n // g – global flag: match ALL occurrences, not just the first\r\n //\r\n // Used in: onKeyDown (count digits), validate (count digits), onPaste\r\n //\r\n // Purpose: Calling .replace(NON_DIGIT_CHARACTERS, '') strips everything\r\n // except digits so we can measure the pure digit length,\r\n // ignoring separators, dots, minus signs, prefix/suffix.\r\n //\r\n // Examples:\r\n // '$1,234.56'.replace(REGEX.NON_DIGIT_CHARACTERS, '') → '123456'\r\n // '-99_000'.replace(REGEX.NON_DIGIT_CHARACTERS, '') → '99000'\r\n // '007'.replace(REGEX.NON_DIGIT_CHARACTERS, '') → '007'\r\n //\r\n NON_DIGIT_CHARACTERS: /[^0-9]/g,\r\n\r\n // ──────────────────────────────────────────────────────────────────────────\r\n // 3. ONLY_DIGITS_AND_MINUS\r\n // Remove everything except digits and the minus sign.\r\n // Used for integer-mode paste sanitization.\r\n // ──────────────────────────────────────────────────────────────────────────\r\n //\r\n // Pattern breakdown:\r\n // [^0-9\\-] – negated class that keeps digits and the literal hyphen\r\n // \\- = escaped hyphen (a bare '-' in the middle of [] is\r\n // treated as a range operator, e.g. [a-z];\r\n // escaping with \\ makes it a literal minus character)\r\n // g – global\r\n //\r\n // Used in: sanitizePastedText() when decimalPlaces === 0\r\n //\r\n // Examples (integers only, negatives allowed):\r\n // '1,234.56'.replace(REGEX.ONLY_DIGITS_AND_MINUS, '') → '123456'\r\n // '-99.9%'.replace(REGEX.ONLY_DIGITS_AND_MINUS, '') → '-999'\r\n // '007'.replace(REGEX.ONLY_DIGITS_AND_MINUS, '') → '007'\r\n //\r\n ONLY_DIGITS_AND_MINUS: /[^0-9\\-]/g,\r\n\r\n // ──────────────────────────────────────────────────────────────────────────\r\n // 4. ONLY_DIGITS_DOT_AND_MINUS\r\n // Remove everything except digits, a decimal point, and the minus sign.\r\n // Used for decimal-number paste sanitization.\r\n // ──────────────────────────────────────────────────────────────────────────\r\n //\r\n // Pattern breakdown:\r\n // [^0-9.\\-] – negated class\r\n // . inside [] is a literal dot (no escape needed inside [])\r\n // \\- is the literal minus\r\n // g – global\r\n //\r\n // Difference from ONLY_DIGITS_AND_MINUS:\r\n // This one also KEEPS the dot '.' so decimal numbers like '12.5' survive.\r\n //\r\n // Used in: sanitizePastedText() when decimalPlaces > 0\r\n //\r\n // Examples:\r\n // '$-1,234.56 USD'.replace(REGEX.ONLY_DIGITS_DOT_AND_MINUS, '') → '-1234.56'\r\n // '€ 99.9%'.replace(REGEX.ONLY_DIGITS_DOT_AND_MINUS, '') → '99.9'\r\n // 'abc123def'.replace(REGEX.ONLY_DIGITS_DOT_AND_MINUS, '') → '123'\r\n //\r\n ONLY_DIGITS_DOT_AND_MINUS: /[^0-9.\\-]/g,\r\n\r\n // ──────────────────────────────────────────────────────────────────────────\r\n // 5. ONLY_DIGITS\r\n // Remove everything except bare digits.\r\n // Used for string-mode (identifiers like card numbers) where dots and\r\n // minus signs are also illegal.\r\n // ──────────────────────────────────────────────────────────────────────────\r\n //\r\n // Pattern breakdown:\r\n // [^0-9] – keep only digit characters\r\n // g – global\r\n //\r\n // Used in: sanitizePastedText() in string / pattern mode,\r\n // onPaste() in pattern mode,\r\n // _applyPattern() to extract digit string before formatting\r\n //\r\n // Examples:\r\n // '4111-1111-1111-1111'.replace(REGEX.ONLY_DIGITS, '') → '4111111111111111'\r\n // '(555) 867-5309'.replace(REGEX.ONLY_DIGITS, '') → '5558675309'\r\n // '01/01/1990'.replace(REGEX.ONLY_DIGITS, '') → '01011990'\r\n //\r\n ONLY_DIGITS: /[^0-9]/g,\r\n\r\n // ──────────────────────────────────────────────────────────────────────────\r\n // 6. ALL_DOTS\r\n // Remove every dot/period from a string.\r\n // ──────────────────────────────────────────────────────────────────────────\r\n //\r\n // Pattern breakdown:\r\n // \\. – escaped dot.\r\n // In regex, a bare '.' means \"any character except newline\".\r\n // The backslash \\ escapes it to mean a LITERAL period character.\r\n // Without the backslash, /./g would erase EVERY character.\r\n // g – global: remove all dots, not just the first\r\n //\r\n // Used in: onInput() and onPaste() when decimalPlaces === 0 or pattern is set.\r\n // If integers are expected, any dot that slips through (e.g. from paste)\r\n // must be stripped.\r\n //\r\n // Examples:\r\n // '12.34'.replace(REGEX.ALL_DOTS, '') → '1234'\r\n // '1.2.3'.replace(REGEX.ALL_DOTS, '') → '123'\r\n // '100'.replace(REGEX.ALL_DOTS, '') → '100' (no dot, unchanged)\r\n //\r\n ALL_DOTS: /\\./g,\r\n\r\n // ──────────────────────────────────────────────────────────────────────────\r\n // 7. ALL_MINUS_SIGNS\r\n // Remove every minus / hyphen character from a string.\r\n // ──────────────────────────────────────────────────────────────────────────\r\n //\r\n // Pattern breakdown:\r\n // - – a literal hyphen/minus character\r\n // (safe outside of a character class [] without escaping)\r\n // g – global\r\n //\r\n // Used in: sanitizePastedText() to strip minus signs when allowNegative is false\r\n // or when outputType is 'string' (identifiers can't be negative).\r\n //\r\n // Examples:\r\n // '-99'.replace(REGEX.ALL_MINUS_SIGNS, '') → '99'\r\n // '1-2-3'.replace(REGEX.ALL_MINUS_SIGNS, '') → '123'\r\n //\r\n ALL_MINUS_SIGNS: /-/g,\r\n\r\n // ──────────────────────────────────────────────────────────────────────────\r\n // 8. THOUSANDS_SEPARATOR_POSITIONS\r\n // Find every position in the INTEGER PART of a number where a thousands\r\n // separator (comma, dot, space, underscore) should be inserted.\r\n // This is the most sophisticated regex in the codebase.\r\n // ──────────────────────────────────────────────────────────────────────────\r\n //\r\n // Full pattern: /\\B(?=(\\d{3})+(?!\\d))/g\r\n //\r\n // Token-by-token breakdown:\r\n //\r\n // \\B – NON-word-boundary position.\r\n // Matches a position that is NOT at the very start of the\r\n // string and NOT right after a non-word character.\r\n // This prevents a separator being inserted before the first\r\n // digit (e.g. \",1,234\" would be wrong).\r\n //\r\n // (?= – POSITIVE LOOKAHEAD — zero-width assertion.\r\n // \"The following characters must match, but don't consume them.\"\r\n // The regex engine peeks ahead without advancing its position.\r\n //\r\n // (\\d{3})+ – one or more groups of EXACTLY THREE digits.\r\n // \\d = any digit\r\n // {3} = exactly 3 of them\r\n // + = one or more such groups (so 3, 6, 9, 12 … digits ahead)\r\n //\r\n // (?!\\d) – NEGATIVE LOOKAHEAD.\r\n // \"What follows the groups of 3 is NOT another digit.\"\r\n // This anchors the groups to the END of the number so the\r\n // separator only lands at multiples of 3 from the RIGHT.\r\n //\r\n // ) – close the positive lookahead\r\n // g – global: insert at every valid position\r\n //\r\n // How the engine works step-by-step on '1234567':\r\n //\r\n // Position between '1' and '2' → lookahead sees '234567' → 6 digits → 6=3×2 ✔ → INSERT\r\n // Position between '2' and '3' → lookahead sees '34567' → 5 digits → not ÷3 ✖\r\n // Position between '3' and '4' → lookahead sees '4567' → 4 digits → not ÷3 ✖\r\n // Position between '4' and '5' → lookahead sees '567' → 3 digits → 3=3×1 ✔ → INSERT\r\n // … and so on\r\n // Result → '1,234,567'\r\n //\r\n // Works with ANY separator — just change the replacement string:\r\n // '1234567'.replace(REGEX.THOUSANDS_SEPARATOR_POSITIONS, ',') → '1,234,567'\r\n // '1234567'.replace(REGEX.THOUSANDS_SEPARATOR_POSITIONS, '.') → '1.234.567'\r\n // '1234567'.replace(REGEX.THOUSANDS_SEPARATOR_POSITIONS, ' ') → '1 234 567'\r\n // '1234567'.replace(REGEX.THOUSANDS_SEPARATOR_POSITIONS, '_') → '1_234_567'\r\n //\r\n // More examples:\r\n // '1234' → '1,234'\r\n // '100' → '100' (fewer than 4 digits — no separator)\r\n // '1000000' → '1,000,000'\r\n //\r\n THOUSANDS_SEPARATOR_POSITIONS: /\\B(?=(\\d{3})+(?!\\d))/g,\r\n\r\n // ──────────────────────────────────────────────────────────────────────────\r\n // 9. REGEX_SPECIAL_CHARACTERS\r\n // Escape any character that has a special meaning inside a RegExp pattern.\r\n // Used before passing a user-supplied string into new RegExp(…).\r\n // ──────────────────────────────────────────────────────────────────────────\r\n //\r\n // Why this is needed:\r\n // The thousandSeparator input can be '.', which inside a regex means\r\n // \"any character\", not a literal dot. Before building\r\n // new RegExp(thousandSeparator, 'g') we must escape it to '\\.'\r\n // so it matches only a literal period.\r\n //\r\n // The replacement string '\\\\$&' means:\r\n // \\\\ – insert a literal backslash\r\n // $& – followed by the matched character itself\r\n // So '.' becomes '\\.' and '(' becomes '\\(' etc.\r\n //\r\n // Pattern breakdown (character class contents):\r\n // . – dot\r\n // * – asterisk\r\n // + – plus\r\n // ? – question mark\r\n // ^ – caret\r\n // $ – dollar sign\r\n // { } – curly braces\r\n // ( ) – parentheses\r\n // | – pipe\r\n // [ – open bracket\r\n // \\] – close bracket (must be escaped to close the class correctly)\r\n // \\\\ – backslash (represents a single \\)\r\n // g – global\r\n //\r\n // Used in: _stripDecoration(), _stripPatternSeparators()\r\n // Anywhere a dynamic string is inserted into new RegExp(…)\r\n //\r\n // Examples:\r\n // '.'.replace(REGEX.REGEX_SPECIAL_CHARACTERS, '\\\\$&') → '\\\\.'\r\n // ','.replace(REGEX.REGEX_SPECIAL_CHARACTERS, '\\\\$&') → ',' (no change)\r\n // '('.replace(REGEX.REGEX_SPECIAL_CHARACTERS, '\\\\$&') → '\\\\('\r\n // ' '.replace(REGEX.REGEX_SPECIAL_CHARACTERS, '\\\\$&') → ' ' (no change)\r\n //\r\n REGEX_SPECIAL_CHARACTERS: /[.*+?^${}()|[\\]\\\\]/g,\r\n\r\n // ──────────────────────────────────────────────────────────────────────────\r\n // 10. LEADING_ZEROS\r\n // Remove leading zeros from the integer part, keeping at least one digit.\r\n // ──────────────────────────────────────────────────────────────────────────\r\n //\r\n // Pattern breakdown:\r\n // ^ – anchor at the start of the string\r\n // 0+ – one or more '0' characters (the leading zeros to remove)\r\n // ( – open capture group 1\r\n // \\d – one digit (the first SIGNIFICANT digit after the zeros)\r\n // ) – close capture group 1\r\n //\r\n // No 'g' flag — we only care about the beginning of the string.\r\n //\r\n // Replacement '$1':\r\n // $1 = capture group 1 = the first significant digit.\r\n // The entire match (zeros + that digit) is replaced by just that digit.\r\n // Any digits AFTER it in the string are left untouched.\r\n //\r\n // Used in: _stripLeadingZeros()\r\n //\r\n // Examples:\r\n // '007'.replace(REGEX.LEADING_ZEROS, '$1') → '7'\r\n // '0042'.replace(REGEX.LEADING_ZEROS, '$1') → '42'\r\n // '0.5'.replace(REGEX.LEADING_ZEROS, '$1') → '0.5' (no match — char after 0 is '.')\r\n // '100'.replace(REGEX.LEADING_ZEROS, '$1') → '100' (no match — first char is '1')\r\n // '0'.replace(REGEX.LEADING_ZEROS, '$1') → '0' (no match — no digit after the 0)\r\n //\r\n LEADING_ZEROS: /^0+(\\d)/,\r\n\r\n // ──────────────────────────────────────────────────────────────────────────\r\n // 11. ARABIC_DIGITS\r\n // Match Arabic-Indic digit characters (used across Middle East & South Asia).\r\n // ──────────────────────────────────────────────────────────────────────────\r\n //\r\n // Unicode range:\r\n // \\u0660 = Arabic-Indic digit zero ٠\r\n // \\u0669 = Arabic-Indic digit nine ٩\r\n // [\\u0660-\\u0669] matches any of the 10 Arabic-Indic digit characters.\r\n //\r\n // g flag: replace ALL occurrences in one pass.\r\n //\r\n // Used in: convertEasternDigits() in eastern-numerals.ts\r\n //\r\n // Background:\r\n // Arabic keyboards and some IMEs output these instead of 0–9.\r\n // Unicode offset from Western digit: charCode - 0x0660 + 48 ('0')\r\n // e.g. '٥'.charCodeAt(0) = 0x0665 → 0x0665 - 0x0660 = 5 → '5'\r\n //\r\n // Examples:\r\n // '١٢٣'.replace(REGEX.ARABIC_DIGITS, …) → '123'\r\n // '٠٩'.replace(REGEX.ARABIC_DIGITS, …) → '09'\r\n //\r\n ARABIC_DIGITS: /[\\u0660-\\u0669]/g,\r\n\r\n // ──────────────────────────────────────────────────────────────────────────\r\n // 12. PERSIAN_DIGITS\r\n // Match Extended Arabic-Indic digit characters used in Persian / Farsi.\r\n // ──────────────────────────────────────────────────────────────────────────\r\n //\r\n // Unicode range:\r\n // \\u06F0 = Extended Arabic-Indic digit zero ۰\r\n // \\u06F9 = Extended Arabic-Indic digit nine ۹\r\n //\r\n // Persian digits are a DIFFERENT Unicode block from Arabic digits even though\r\n // they look similar:\r\n // Arabic zero ٠ = U+0660\r\n // Persian zero ۰ = U+06F0 ← different code point!\r\n //\r\n // Both blocks need separate patterns.\r\n //\r\n // g flag: replace ALL occurrences.\r\n //\r\n // Used in: convertEasternDigits() in eastern-numerals.ts\r\n //\r\n // Examples:\r\n // '۱۲۳'.replace(REGEX.PERSIAN_DIGITS, …) → '123'\r\n // '۰۹'.replace(REGEX.PERSIAN_DIGITS, …) → '09'\r\n //\r\n PERSIAN_DIGITS: /[\\u06F0-\\u06F9]/g,\r\n\r\n // ──────────────────────────────────────────────────────────────────────────\r\n // 13. ARABIC_DECIMAL_COMMA\r\n // Match the Arabic decimal comma character ٫ (U+066B).\r\n // ──────────────────────────────────────────────────────────────────────────\r\n //\r\n // In Arabic locale, the decimal separator is ٫ (Arabic Decimal Separator)\r\n // not the Western period '.'. We convert it to '.' so the rest of the\r\n // directive can process it uniformly.\r\n //\r\n // Used in: convertEasternDigits() in eastern-numerals.ts\r\n //\r\n // Example:\r\n // '١٢٫٥'.replace(REGEX.ARABIC_DECIMAL_COMMA, '.') → '١٢.٥'\r\n // (then after digit conversion) → '12.5'\r\n //\r\n ARABIC_DECIMAL_COMMA: /\\u066B/g,\r\n\r\n // ──────────────────────────────────────────────────────────────────────────\r\n // 14. ARABIC_THOUSANDS_SEPARATOR\r\n // Match the Arabic thousands separator ٬ (U+066C).\r\n // ──────────────────────────────────────────────────────────────────────────\r\n //\r\n // Similar to the decimal comma above: the Arabic locale uses U+066C as its\r\n // thousands grouping character. We strip it so the directive does not\r\n // mistake it for a digit.\r\n //\r\n // Used in: convertEasternDigits() in eastern-numerals.ts\r\n //\r\n // Example:\r\n // '١٬٢٣٤'.replace(REGEX.ARABIC_THOUSANDS_SEPARATOR, '') → '١٢٣٤'\r\n //\r\n ARABIC_THOUSANDS_SEPARATOR: /\\u066C/g,\r\n\r\n // ──────────────────────────────────────────────────────────────────────────\r\n // 15. ARABIC_DIGIT_RANGE\r\n // The Unicode code-point boundaries for Arabic-Indic digit characters.\r\n // ──────────────────────────────────────────────────────────────────────────\r\n //\r\n // Unlike ARABIC_DIGITS (which is a /g regex used with .replace()), these\r\n // are plain string constants used for a boundary COMPARISON in onKeyDown():\r\n //\r\n // pressedKey >= REGEX.ARABIC_DIGIT_RANGE.FIRST &&\r\n // pressedKey <= REGEX.ARABIC_DIGIT_RANGE.LAST\r\n //\r\n // Why not reuse ARABIC_DIGITS here?\r\n // ARABIC_DIGITS has the global 'g' flag and is designed for .replace()\r\n // across an entire string. A single-character range check via >= / <=\r\n // is simpler, has no flag state to worry about, and is O(1).\r\n // JavaScript compares strings by Unicode code point, so this works\r\n // correctly for single characters.\r\n //\r\n // Used in: onKeyDown() — to allow Eastern keystrokes through before\r\n // onInput() converts them to Western digits.\r\n //\r\n // Examples:\r\n // '١' >= REGEX.ARABIC_DIGIT_RANGE.FIRST → true (U+0661 >= U+0660)\r\n // '١' <= REGEX.ARABIC_DIGIT_RANGE.LAST → true (U+0661 <= U+0669)\r\n // 'a' >= REGEX.ARABIC_DIGIT_RANGE.FIRST → false (U+0061 < U+0660)\r\n //\r\n ARABIC_DIGIT_RANGE: {\r\n FIRST: '\\u0660', // ٠ Arabic-Indic digit ZERO\r\n LAST: '\\u0669', // ٩ Arabic-Indic digit NINE\r\n },\r\n\r\n // ──────────────────────────────────────────────────────────────────────────\r\n // 16. PERSIAN_DIGIT_RANGE\r\n // The Unicode code-point boundaries for Persian digit characters.\r\n // ──────────────────────────────────────────────────────────────────────────\r\n //\r\n // Same idea as ARABIC_DIGIT_RANGE above, but for the Persian/Extended\r\n // Arabic-Indic block (U+06F0–U+06F9).\r\n //\r\n // Persian digits live in a DIFFERENT Unicode block from Arabic digits\r\n // even though they look similar:\r\n // Arabic zero ٠ = U+0660\r\n // Persian zero ۰ = U+06F0 ← different block, needs its own range\r\n //\r\n // Used in: onKeyDown() — same purpose as ARABIC_DIGIT_RANGE.\r\n //\r\n // Examples:\r\n // '۶' >= REGEX.PERSIAN_DIGIT_RANGE.FIRST → true (U+06F6 >= U+06F0)\r\n // '۶' <= REGEX.PERSIAN_DIGIT_RANGE.LAST → true (U+06F6 <= U+06F9)\r\n // '١' >= REGEX.PERSIAN_DIGIT_RANGE.FIRST → false (U+0661 < U+06F0)\r\n //\r\n PERSIAN_DIGIT_RANGE: {\r\n FIRST: '\\u06F0', // ۰ Persian digit ZERO\r\n LAST: '\\u06F9', // ۹ Persian digit NINE\r\n },\r\n\r\n} as const;\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// QUICK-REFERENCE TABLE\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n//\r\n// Key Pattern Purpose\r\n// ────────────────────────── ───────────────────────────── ────────────────────────────────────────\r\n// SINGLE_DIGIT /^\\d$/ Is a keypress exactly one digit?\r\n// NON_DIGIT_CHARACTERS /[^0-9]/g Strip everything except digits\r\n// ONLY_DIGITS_AND_MINUS /[^0-9\\-]/g Keep digits + minus (integer paste)\r\n// ONLY_DIGITS_DOT_AND_MINUS /[^0-9.\\-]/g Keep digits + dot + minus (decimal paste)\r\n// ONLY_DIGITS /[^0-9]/g Keep digits only (string/pattern paste)\r\n// ALL_DOTS /\\./g Remove all literal dot characters\r\n// ALL_MINUS_SIGNS /-/g Remove all minus/hyphen characters\r\n// THOUSANDS_SEPARATOR_POSITIONS /\\B(?=(\\d{3})+(?!\\d))/g Find positions for thousands separator\r\n// REGEX_SPECIAL_CHARACTERS /[.*+?^${}()|[\\]\\\\]/g Escape chars before new RegExp(…)\r\n// LEADING_ZEROS /^0+(\\d)/ Strip leading zeros, keep ≥ 1 digit\r\n// ARABIC_DIGITS /[\\u0660-\\u0669]/g Match Arabic-Indic digits ٠-٩\r\n// PERSIAN_DIGITS /[\\u06F0-\\u06F9]/g Match Persian digits ۰-۹\r\n// ARABIC_DECIMAL_COMMA /\\u066B/g Arabic decimal separator ٫ → '.'\r\n// ARABIC_THOUSANDS_SEPARATOR /\\u066C/g Arabic grouping separator ٬ → strip\r\n// ARABIC_DIGIT_RANGE { FIRST: '\\u0660', LAST: '\\u0669' } Boundary check for Arabic digits in keydown\r\n// PERSIAN_DIGIT_RANGE { FIRST: '\\u06F0', LAST: '\\u06F9' } Boundary check for Persian digits in keydown\r\n","/**\r\n * eastern-numerals.ts\r\n *\r\n * A fully standalone, zero-dependency TypeScript utility that converts\r\n * Arabic-Indic and Persian/Farsi digit characters to their Western (ASCII)\r\n * equivalents.\r\n *\r\n * ─── Zero dependencies ────────────────────────────────────────────────────────\r\n *\r\n * This file has NO imports. Copy it into ANY TypeScript or JavaScript project\r\n * and use it immediately — Angular, React, Vue, Node.js, plain TS, or a browser\r\n * script. Nothing else is required.\r\n *\r\n * ─── Why this exists ──────────────────────────────────────────────────────────\r\n *\r\n * Arabic and Persian keyboards — and many mobile IMEs across the Middle East\r\n * and South Asia — produce digit characters from a DIFFERENT Unicode block\r\n * than the ASCII digits 0–9 most Western software expects:\r\n *\r\n * Western / ASCII 0 1 2 3 4 5 6 7 8 9\r\n * Code points U+0030 ── U+0039\r\n *\r\n * Arabic-Indic ٠ ١ ٢ ٣ ٤ ٥ ٦ ٧ ٨ ٩\r\n * Code points U+0660 ── U+0669\r\n *\r\n * Persian / Farsi ۰ ۱ ۲ ۳ ۴ ۵ ۶ ۷ ۸ ۹\r\n * Code points U+06F0 ── U+06F9\r\n *\r\n * When a user types ١٢٣ on an Arabic keyboard the browser sends those exact\r\n * Unicode code points, NOT '123'. Without conversion:\r\n * • /^\\d$/ does NOT match them (it only matches U+0030–U+0039)\r\n * • Number('١٢٣') returns NaN\r\n * • parseInt('١٢٣', 10) returns NaN\r\n *\r\n * ─── Safety guarantee ─────────────────────────────────────────────────────────\r\n *\r\n * This conversion is completely conflict-free because the Arabic and Persian\r\n * digit Unicode blocks have ZERO overlap with:\r\n * • ASCII digits (U+0030–U+0039)\r\n * • ASCII letters (U+0041–U+007A)\r\n * • ASCII punctuation (. , - + $ € % @ # etc.)\r\n *\r\n * Converting ٥ → '5' can NEVER accidentally transform a separator, currency\r\n * symbol, or letter into something else. The substitution is exact and\r\n * unambiguous.\r\n *\r\n * ─── What is NOT converted ────────────────────────────────────────────────────\r\n *\r\n * • Arabic letters ا ب ت ث … — no Western equivalent, left as-is\r\n * • Arabic diacritics (harakat) — no Western equivalent, left as-is\r\n * • Any character outside the two digit blocks described above\r\n *\r\n * ─── Exports ──────────────────────────────────────────────────────────────────\r\n *\r\n * convertEasternDigits(input) Main conversion — Eastern digits → 0-9\r\n * hasEasternDigits(input) Quick check — does the string contain any?\r\n * getDigitScript(input) Identify which script the digits are in\r\n * EASTERN_DIGIT_MAP Full lookup table (for testing / docs)\r\n * EASTERN_NUMERAL_REGEX The two RegExp patterns (for advanced use)\r\n *\r\n * ─── Quick usage ──────────────────────────────────────────────────────────────\r\n *\r\n * import { convertEasternDigits } from './eastern-numerals';\r\n *\r\n * convertEasternDigits('١٢٣٫٤٥') // '123.45'\r\n * convertEasternDigits('۶۷۸') // '678'\r\n * convertEasternDigits('price: ١٠٠') // 'price: 100' (non-digits untouched)\r\n * convertEasternDigits('123') // '123' (already Western)\r\n * convertEasternDigits(null) // ''\r\n */\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// Internal regex constants\r\n// (Inline here so the file has zero dependencies and can be used anywhere.)\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Regex patterns used internally by this module.\r\n * Also exported so callers can use them directly if needed.\r\n *\r\n * These are the same patterns documented in digits-only.regex.ts entries\r\n * ARABIC_DIGITS, PERSIAN_DIGITS, ARABIC_DECIMAL_COMMA, ARABIC_THOUSANDS_SEPARATOR\r\n * but duplicated here so this file remains self-contained.\r\n */\r\nexport const EASTERN_NUMERAL_REGEX = {\r\n\r\n /**\r\n * Matches any Arabic-Indic digit character (U+0660 – U+0669).\r\n * These are the digits used on Arabic keyboards: ٠١٢٣٤٥٦٧٨٩\r\n * The g flag ensures ALL occurrences in a string are replaced, not just the first.\r\n */\r\n ARABIC_DIGITS: /[\\u0660-\\u0669]/g,\r\n\r\n /**\r\n * Matches any Persian / Extended Arabic-Indic digit (U+06F0 – U+06F9).\r\n * These are the digits used on Persian / Farsi keyboards: ۰۱۲۳۴۵۶۷۸۹\r\n * Note: a DIFFERENT Unicode block from Arabic even though they look similar.\r\n * Arabic zero ٠ = U+0660\r\n * Persian zero ۰ = U+06F0 ← different!\r\n */\r\n PERSIAN_DIGITS: /[\\u06F0-\\u06F9]/g,\r\n\r\n /**\r\n * Matches the Arabic Decimal Separator ٫ (U+066B).\r\n * In Arabic locale this character plays the role of the Western period '.'\r\n * as a decimal point. We normalize it to '.' so parseFloat / Number work.\r\n */\r\n ARABIC_DECIMAL_COMMA: /\\u066B/g,\r\n\r\n /**\r\n * Matches the Arabic Thousands Separator ٬ (U+066C).\r\n * In Arabic locale this character plays the role of the Western comma ','\r\n * as a digit-group separator. We strip it so it doesn't pollute the number.\r\n */\r\n ARABIC_THOUSANDS_SEPARATOR: /\\u066C/g,\r\n\r\n} as const;\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// Full Unicode character map (exported for testing and documentation)\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Every Eastern digit character mapped to its Western ASCII equivalent.\r\n *\r\n * Key = Eastern character (as it arrives from the keyboard / clipboard)\r\n * Value = Western ASCII character it should become\r\n *\r\n * This map is exported for:\r\n * • Unit tests that want to iterate over every case\r\n * • Documentation / debugging\r\n * • Manual lookups without running a conversion\r\n */\r\nexport const EASTERN_DIGIT_MAP: Readonly<Record<string, string>> = {\r\n\r\n // ── Arabic-Indic digits (U+0660 – U+0669) ────────────────────────────────\r\n // Used in: Arabic, Urdu, Sindhi, and other Arabic-script languages\r\n '٠': '0', // U+0660 Arabic-Indic digit zero\r\n '١': '1', // U+0661 Arabic-Indic digit one\r\n '٢': '2', // U+0662 Arabic-Indic digit two\r\n '٣': '3', // U+0663 Arabic-Indic digit three\r\n '٤': '4', // U+0664 Arabic-Indic digit four\r\n '٥': '5', // U+0665 Arabic-Indic digit five\r\n '٦': '6', // U+0666 Arabic-Indic digit six\r\n '٧': '7', // U+0667 Arabic-Indic digit seven\r\n '٨': '8', // U+0668 Arabic-Indic digit eight\r\n '٩': '9', // U+0669 Arabic-Indic digit nine\r\n\r\n // ── Persian / Extended Arabic-Indic digits (U+06F0 – U+06F9) ─────────────\r\n // Used in: Persian (Farsi), Pashto, Kurdish, and related languages\r\n '۰': '0', // U+06F0 Extended Arabic-Indic digit zero\r\n '۱': '1', // U+06F1 Extended Arabic-Indic digit one\r\n '۲': '2', // U+06F2 Extended Arabic-Indic digit two\r\n '۳': '3', // U+06F3 Extended Arabic-Indic digit three\r\n '۴': '4', // U+06F4 Extended Arabic-Indic digit four\r\n '۵': '5', // U+06F5 Extended Arabic-Indic digit five\r\n '۶': '6', // U+06F6 Extended Arabic-Indic digit six\r\n '۷': '7', // U+06F7 Extended Arabic-Indic digit seven\r\n '۸': '8', // U+06F8 Extended Arabic-Indic digit eight\r\n '۹': '9', // U+06F9 Extended Arabic-Indic digit nine\r\n\r\n // ── Arabic punctuation normalized as part of number handling ─────────────\r\n '٫': '.', // U+066B Arabic Decimal Separator → Western decimal point\r\n '٬': '', // U+066C Arabic Thousands Separator → stripped (empty string)\r\n\r\n} as const;\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// Core conversion function\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Convert all Arabic-Indic and Persian digit characters in a string\r\n * to their Western ASCII equivalents (0–9).\r\n *\r\n * The function runs four passes in order:\r\n * 1. Strip Arabic thousands separator ٬ (U+066C)\r\n * 2. Convert Arabic decimal comma ٫ (U+066B) → '.'\r\n * 3. Convert Arabic-Indic digits ٠-٩ (U+0660–U+0669) → '0'-'9'\r\n * 4. Convert Persian digits ۰-۹ (U+06F0–U+06F9) → '0'-'9'\r\n *\r\n * The function is PURE — it never mutates its input and has no side effects.\r\n *\r\n * @param input Any string, number, null, or undefined.\r\n * Numbers are converted via String() first.\r\n * null / undefined / '' all return ''.\r\n * @returns A new string with every Eastern digit replaced by its Western\r\n * equivalent. Non-digit characters are passed through unchanged.\r\n *\r\n * @example\r\n * convertEasternDigits('١٢٣') // '123'\r\n * convertEasternDigits('۶۷۸') // '678'\r\n * convertEasternDigits('١٢٣٫٤٥') // '123.45' (decimal comma)\r\n * convertEasternDigits('١٬٢٣٤') // '1234' (thousands sep stripped)\r\n * convertEasternDigits('price: ٩٩') // 'price: 99' (non-digits untouched)\r\n * convertEasternDigits('123') // '123' (already Western)\r\n * convertEasternDigits('') // ''\r\n * convertEasternDigits(null) // ''\r\n * convertEasternDigits(undefined) // ''\r\n * convertEasternDigits(42) // '42' (number input)\r\n */\r\nexport function convertEasternDigits(\r\n input: string | number | null | undefined,\r\n): string {\r\n\r\n // ── Guard ─────────────────────────────────────────────────────────────────\r\n // Handle null, undefined, and empty-string early so the rest of the\r\n // function can assume it has a non-empty string to work with.\r\n if (input === null || input === undefined || input === '') {\r\n return '';\r\n }\r\n\r\n // Coerce numbers to strings so the function also works when called\r\n // directly on a numeric value (e.g. after a model value update).\r\n const text = typeof input === 'number' ? String(input) : input;\r\n\r\n return text\r\n\r\n // ── Step 1: Remove Arabic thousands separator ──────────────────────────\r\n //\r\n // ٬ (U+066C) looks like a comma but is a completely different code point.\r\n // It must be stripped FIRST — before digit conversion — so it cannot\r\n // accidentally become part of the number string.\r\n //\r\n // '١٬٢٣٤' → '١٢٣٤' (grouping character removed)\r\n .replace(EASTERN_NUMERAL_REGEX.ARABIC_THOUSANDS_SEPARATOR, '')\r\n\r\n // ── Step 2: Arabic decimal comma → Western decimal point ──────────────\r\n //\r\n // ٫ (U+066B) is the decimal separator in Arabic locale.\r\n // We convert it to '.' so parseFloat / Number can parse the result.\r\n //\r\n // '١٢٫٥' → '١٢.٥' (decimal comma replaced, digits still Arabic)\r\n .replace(EASTERN_NUMERAL_REGEX.ARABIC_DECIMAL_COMMA, '.')\r\n\r\n // ── Step 3: Arabic-Indic digits → Western digits ───────────────────────\r\n //\r\n // The conversion formula uses the Unicode offset of the Arabic zero (0x0660):\r\n //\r\n // Example with '٥':\r\n // '٥'.charCodeAt(0) = 0x0665 (1637 decimal)\r\n // 0x0665 - 0x0660 = 5 (the digit's numeric value)\r\n // 5 + 0x30 = 53 (ASCII code of '5')\r\n // String.fromCharCode(53) = '5'\r\n //\r\n // '٠١٢٣٤٥٦٧٨٩' → '0123456789'\r\n .replace(EASTERN_NUMERAL_REGEX.ARABIC_DIGITS, (arabicChar: string) => {\r\n const ARABIC_ZERO_CODE_POINT = 0x0660; // Unicode code point of Arabic ٠\r\n const WESTERN_ZERO_CODE_POINT = 0x0030; // Unicode code point of ASCII 0\r\n\r\n const digitValue = arabicChar.charCodeAt(0) - ARABIC_ZERO_CODE_POINT;\r\n return String.fromCharCode(digitValue + WESTERN_ZERO_CODE_POINT);\r\n })\r\n\r\n // ── Step 4: Persian digits → Western digits ────────────────────────────\r\n //\r\n // Identical math to step 3 but using the Persian zero offset (0x06F0).\r\n // Persian and Arabic digits look similar but are different code points —\r\n // both blocks need their own replacement pass.\r\n //\r\n // '۰۱۲۳۴۵۶۷۸۹' → '0123456789'\r\n .replace(EASTERN_NUMERAL_REGEX.PERSIAN_DIGITS, (persianChar: string) => {\r\n const PERSIAN_ZERO_CODE_POINT = 0x06F0; // Unicode code point of Persian ۰\r\n const WESTERN_ZERO_CODE_POINT = 0x0030; // Unicode code point of ASCII 0\r\n\r\n const digitValue = persianChar.charCodeAt(0) - PERSIAN_ZERO_CODE_POINT;\r\n return String.fromCharCode(digitValue + WESTERN_ZERO_CODE_POINT);\r\n });\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// Helper utilities\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Returns true if the string contains at least one Arabic-Indic or\r\n * Persian digit character.\r\n *\r\n * Useful as a cheap pre-check before calling convertEasternDigits — if this\r\n * returns false the string is already all-Western and no conversion is needed.\r\n *\r\n * @param input Any string to check.\r\n * @returns true if any Eastern digit is present, false otherwise.\r\n *\r\n * @example\r\n * hasEasternDigits('١٢٣') // true — Arabic\r\n * hasEasternDigits('۴۵۶') // true — Persian\r\n * hasEasternDigits('٥0') // true — mix of Arabic and Western\r\n * hasEasternDigits('123') // false — already Western\r\n * hasEasternDigits('hello') // false — no digits at all\r\n * hasEasternDigits('') // false\r\n */\r\nexport function hasEasternDigits(input: string): boolean {\r\n if (!input) return false;\r\n\r\n // We must reset lastIndex before using a /g regex with .test()\r\n // because stateful regex objects remember where they left off.\r\n // The simplest safe approach is to re-create a clean regex each time.\r\n return /[\\u0660-\\u0669\\u06F0-\\u06F9]/.test(input);\r\n}\r\n\r\n/**\r\n * Identify which numeral script(s) the string's digits are written in.\r\n * Returns one of four values so callers can branch on the exact script\r\n * rather than just knowing \"something eastern is present\".\r\n *\r\n * @param input Any string to inspect.\r\n * @returns\r\n * 'western' — only ASCII digits 0-9 found (or no digits at all)\r\n * 'arabic' — only Arabic-Indic digits ٠-٩ found\r\n * 'persian' — only Persian digits ۰-۹ found\r\n * 'mixed' — both Eastern script(s) AND/OR Western digits found together\r\n *\r\n * @example\r\n * getDigitScript('١٢٣') // 'arabic'\r\n * getDigitScript('۴۵۶') // 'persian'\r\n * getDigitScript('123') // 'western'\r\n * getDigitScript('١23') // 'mixed' (Arabic + Western)\r\n * getDigitScript('١۴') // 'mixed' (Arabic + Persian)\r\n * getDigitScript('hello') // 'western' (no digits → treated as Western)\r\n */\r\nexport type DigitScript = 'western' | 'arabic' | 'persian' | 'mixed';\r\n\r\nexport function getDigitScript(input: string): DigitScript {\r\n if (!input) return 'western';\r\n\r\n const containsArabic = /[\\u0660-\\u0669]/.test(input);\r\n const containsPersian = /[\\u06F0-\\u06F9]/.test(input);\r\n const containsWestern = /[0-9]/.test(input);\r\n\r\n // Mixed: more than one script present simultaneously\r\n const scriptCount = [containsArabic, containsPersian, containsWestern]\r\n .filter(Boolean).length;\r\n\r\n if (scriptCount > 1) return 'mixed';\r\n if (containsArabic) return 'arabic';\r\n if (containsPersian) return 'persian';\r\n return 'western';\r\n}\r\n\r\n/**\r\n * Convert a string that may contain Eastern digits to a JavaScript number.\r\n * A convenience wrapper around convertEasternDigits + parseFloat.\r\n *\r\n * Returns NaN for strings that don't represent a valid number\r\n * (same contract as parseFloat / Number).\r\n *\r\n * @param input A string possibly containing Eastern digits.\r\n * @returns A JavaScript number, or NaN if parsing fails.\r\n *\r\n * @example\r\n * toWesternNumber('١٢٣') // 123\r\n * toWesternNumber('١٢٣٫٤٥') // 123.45\r\n * toWesternNumber('۹٩') // 99 (mixed Arabic + Persian → still works)\r\n * toWesternNumber('abc') // NaN\r\n * toWesternNumber('') // NaN\r\n */\r\nexport function toWesternNumber(input: string | null | undefined): number {\r\n const converted = convertEasternDigits(input);\r\n return parseFloat(converted);\r\n}\r\n","/**\r\n * digits-only.directive.ts\r\n *\r\n * A self-contained Angular directive that replicates the key numeric features\r\n * of ngx-mask without any external dependency.\r\n *\r\n * ─── Files in this module ─────────────────────────────────────────────────────\r\n *\r\n * digits-only.directive.ts ← you are here — the Angular directive\r\n * digits-only.regex.ts ← every RegExp constant with detailed explanations\r\n * digits-only.module.ts ← NgModule that declares and exports the directive\r\n * eastern-numerals.ts ← standalone Arabic / Persian → Western digit converter\r\n *\r\n * ─── Features ─────────────────────────────────────────────────────────────────\r\n *\r\n * [digitsOnly] Selector — add to any <input> element\r\n * [decimalPlaces] Number of decimal places allowed (default: 0 = integers)\r\n * [thousandSeparator] Visual grouping character: ',' | '.' | ' ' | '_' | ''\r\n * [prefix] Visual prefix shown in input (e.g. '$') — stripped from model\r\n * [suffix] Visual suffix shown in input (e.g. '%') — stripped from model\r\n * [allowNegative] Allow a leading minus sign (default: false)\r\n * [leadingZeros] Preserve leading zeros like '007' (default: false)\r\n * [maxLength] Maximum number of raw digits allowed\r\n * [min] Minimum numeric value (validation, number mode only)\r\n * [max] Maximum numeric value (validation, number mode only)\r\n * [outputType] 'number' (default) | 'string' — type emitted to the model\r\n * [pattern] Display-format mask: '0000 0000 0000 0000' or named alias\r\n * [convertEasternNumerals] Convert Arabic/Persian digits to Western on input (default: true)\r\n *\r\n * ─── outputType ───────────────────────────────────────────────────────────────\r\n *\r\n * 'number' (default)\r\n * Model receives: number | null\r\n * Use for: prices, quantities, temperatures — values used in arithmetic\r\n * Note: Leading zeros are lost (007 → 7); values beyond\r\n * Number.MAX_SAFE_INTEGER may lose precision\r\n *\r\n * 'string'\r\n * Model receives: string | null\r\n * Use for: card numbers, phone numbers, postal codes, SSNs, OTPs —\r\n * digit strings that are identifiers, not quantities\r\n * Note: Leading zeros are always preserved; no precision limit\r\n *\r\n * ─── pattern ──────────────────────────────────────────────────────────────────\r\n *\r\n * A mask string made of '0' (digit slot) and any other character (separator).\r\n * Separators are inserted in the display automatically but NEVER reach the model.\r\n * Setting a pattern forces outputType='string' and derives maxLength automatically.\r\n *\r\n * Named aliases:\r\n * 'card' → '0000 0000 0000 0000' Visa / Mastercard (16 digits)\r\n *\r\n * ─── convertEasternNumerals ───────────────────────────────────────────────────\r\n *\r\n * When true (default), Arabic-Indic (٠١٢٣٤٥٦٧٨٩) and Persian (۰۱۲۳۴۵۶۷۸۹)\r\n * digit characters are silently converted to their Western equivalents before\r\n * any processing. This allows users on Arabic / Persian keyboards to type\r\n * naturally into any digitsOnly field.\r\n * Set to false only if you specifically need to block Eastern numerals.\r\n *\r\n * ─── Usage examples ───────────────────────────────────────────────────────────\r\n *\r\n * <!-- Integer amount, $ prefix, comma thousands -->\r\n * <input digitsOnly thousandSeparator=\",\" prefix=\"$\" formControlName=\"salary\" />\r\n *\r\n * <!-- Decimal price, 2 places -->\r\n * <input digitsOnly [decimalPlaces]=\"2\" thousandSeparator=\",\" formControlName=\"price\" />\r\n *\r\n * <!-- Card number with automatic space grouping -->\r\n * <input digitsOnly pattern=\"card\" formControlName=\"cardNumber\" />\r\n *\r\n * <!-- Custom pattern -->\r\n * <input digitsOnly pattern=\"000-00-0000\" formControlName=\"ssn\" />\r\n *\r\n * <!-- Opt out of Eastern numeral conversion -->\r\n * <input digitsOnly [convertEasternNumerals]=\"false\" formControlName=\"code\" />\r\n */\r\n\r\nimport {\r\n Directive,\r\n ElementRef,\r\n HostListener,\r\n Inject,\r\n Input,\r\n OnChanges,\r\n OnInit,\r\n Renderer2,\r\n SimpleChanges,\r\n forwardRef,\r\n} from '@angular/core';\r\nimport { DOCUMENT } from '@angular/common';\r\nimport {\r\n AbstractControl,\r\n ControlValueAccessor,\r\n NG_VALIDATORS,\r\n NG_VALUE_ACCESSOR,\r\n ValidationErrors,\r\n Validator,\r\n} from '@angular/forms';\r\n\r\nimport { REGEX } from './digits-only.regex';\r\nimport { convertEasternDigits } from './eastern-numerals';\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// Unicode directional constants\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Left-to-Right Mark (U+200E) — an invisible zero-width Unicode character.\r\n *\r\n * WHY IT IS NEEDED:\r\n * In RTL documents (dir=\"rtl\"), the browser's Unicode Bidirectional Algorithm\r\n * (UBA) may try to mirror numeric content — reordering digits or misplacing\r\n * separators like commas. For example, \"1,234\" can display as \"234,1\" in\r\n * some bidi contexts.\r\n *\r\n * Prepending LRM to the input value tells the bidi algorithm:\r\n * \"treat everything that follows as left-to-right text\".\r\n * Numbers, thousands separators, and decimal points always read correctly.\r\n *\r\n * ZERO IMPACT ON MODEL:\r\n * LRM is stripped inside stripDecorationsFrom() before any value reaches\r\n * the model, so your component never sees this character.\r\n *\r\n * BROWSER SUPPORT:\r\n * All modern browsers respect U+200E in input values.\r\n */\r\nconst LRM = '\\u200E'; // Left-to-Right Mark — invisible, prevents bidi digit mirroring\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// Named pattern registry + type\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// '0' = digit slot, any other character = literal separator inserted in display.\r\n\r\nexport type NamedPattern = 'card';\r\n\r\n\r\n/**\r\n * Maps every NamedPattern alias to its full mask string.\r\n * Typed as Record<NamedPattern, string> so TypeScript catches missing entries —\r\n * add a new entry here and the type forces you to add it to NamedPattern too.\r\n */\r\nconst NAMED_PATTERNS: Record<NamedPattern, string> = {\r\n 'card': '0000 0000 0000 0000',\r\n};\r\n\r\n/**\r\n * Type guard that narrows an arbitrary string to NamedPattern.\r\n *\r\n * Using the `in` operator against NAMED_PATTERNS tells TypeScript:\r\n * \"if this key exists in the object, treat it as NamedPattern from here on\".\r\n * This is the only way to safely index Record<NamedPattern, string> with a\r\n * value that might be a NamedPattern OR a plain custom pattern string.\r\n *\r\n * @example\r\n * isNamedPattern('card') // true → safe to use as NAMED_PATTERNS key\r\n * isNamedPattern('000-0000') // false → treat as raw custom pattern\r\n */\r\nfunction isNamedPattern(value: string): value is NamedPattern {\r\n return value in NAMED_PATTERNS;\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// Directive\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\n@Directive({\r\n selector: '[digitsOnly]',\r\n standalone: true,\r\n providers: [\r\n {\r\n provide: NG_VALUE_ACCESSOR,\r\n useExisting: forwardRef(() => digitsOnlyDirective),\r\n multi: true,\r\n },\r\n {\r\n provide: NG_VALIDATORS,\r\n useExisting: forwardRef(() => digitsOnlyDirective),\r\n multi: true,\r\n },\r\n ],\r\n})\r\nexport class digitsOnlyDirective implements ControlValueAccessor, Validator, OnInit, OnChanges {\r\n\r\n // ─── Public inputs ──────────────────────────────────────────────────────────\r\n\r\n /** How many decimal places are allowed. 0 = integers only. Ignored when pattern is set. */\r\n @Input() decimalPlaces = 0;\r\n\r\n /** Visual grouping character inserted between digit groups. Ignored when pattern is set. */\r\n @Input() thousandSeparator: '' | ',' | '.' | ' ' | '_' = '';\r\n\r\n /** Visual text prepended to the displayed value (e.g. '$'). Never reaches the model. */\r\n @Input() prefix = '';\r\n\r\n /** Visual text appended to the displayed value (e.g. '%'). Never reaches the model. */\r\n @Input() suffix = '';\r\n\r\n /** When true, a leading minus '-' is allowed for negative numbers. */\r\n @Input() allowNegative = false;\r\n\r\n /** When true, leading zeros (e.g. '007') are preserved in the model value. */\r\n @Input() leadingZeros = false;\r\n\r\n /** Maximum number of raw digit characters the user may enter. */\r\n @Input() maxLength: number | null = null;\r\n\r\n /** Minimum allowed numeric value. Produces a 'min' validation error when violated. */\r\n @Input() min: number | null = null;\r\n\r\n /** Maximum allowed numeric value. Produces a 'max' validation error when violated. */\r\n @Input() max: number | null = null;\r\n\r\n /**\r\n * Controls the JavaScript type emitted to the form model.\r\n * 'number' (default) → number | null\r\n * 'string' → string | null\r\n * Automatically overridden to 'string' when [pattern] is set.\r\n */\r\n @Input() outputType: 'number' | 'string' = 'number';\r\n\r\n /**\r\n * When true (default), Arabic-Indic and Persian digit characters typed or\r\n * pasted by the user are automatically converted to Western digits 0-9\r\n * before any processing happens.\r\n *\r\n * Safe to leave enabled for all use cases — the conversion only touches\r\n * characters in the Unicode ranges U+0660–U+0669 and U+06F0–U+06F9,\r\n * which have zero overlap with any ASCII character.\r\n */\r\n @Input() convertEasternNumerals = true;\r\n\r\n /**\r\n * Display-format mask string or a named alias (see NamedPattern type).\r\n *\r\n * Typed as NamedPattern | (string & {}) so that:\r\n * • Named aliases are autocompleted in IDEs and validated at compile time.\r\n * • Custom raw patterns like '000-0000' are still accepted without error.\r\n *\r\n * '0' = digit slot, any other char = literal separator auto-inserted in display.\r\n * Setting this forces outputType='string' and derives maxLength from slot count.\r\n *\r\n * @example\r\n * pattern=\"card\" → '0000 0000 0000 0000'\r\n * pattern=\"00/00/0000\" → custom date pattern\r\n */\r\n @Input() set pattern(value: NamedPattern | (string & {})) {\r\n // Type guard: check whether the value is one of the known NamedPattern keys\r\n // before indexing NAMED_PATTERNS with it.\r\n //\r\n // Why this is needed:\r\n // NAMED_PATTERNS is typed as Record<NamedPattern, string>, meaning its\r\n // index signature only accepts NamedPattern keys — not arbitrary strings.\r\n // The @Input type is NamedPattern | (string & {}), so `value` could be\r\n // a custom raw pattern like '000-0000' that is NOT a key of NAMED_PATTERNS.\r\n // Indexing with it directly causes TS error ts(7053):\r\n // \"No index signature with a parameter of type 'string' was found\".\r\n //\r\n // The fix: use `value in NAMED_PATTERNS` as a type guard. The `in` operator\r\n // narrows the type of `value` to NamedPattern inside the if-branch, making\r\n // NAMED_PATTERNS[value] type-safe. In the else-branch `value` is treated\r\n // as a raw custom pattern string and used directly.\r\n if (isNamedPattern(value)) {\r\n this._resolvedPattern = NAMED_PATTERNS[value]; // value is NamedPattern\r\n } else {\r\n this._resolvedPattern = value; // value is a raw custom pattern\r\n }\r\n }\r\n\r\n // ─── Internal state ──────────────────────────────────────────────────────────\r\n\r\n /** Full pattern string after named-alias resolution. Empty string = no pattern. */\r\n private _resolvedPattern = '';\r\n\r\n /**\r\n * The canonical internal value: pure digits (and optional '.' and '-').\r\n * No prefix, suffix, separators, or pattern characters ever live here.\r\n * Every other method reads or writes this field.\r\n */\r\n private _rawValue = '';\r\n\r\n // ControlValueAccessor callbacks registered by Angular forms\r\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\r\n private _onChange: (value: any) => void = () => { };\r\n private _onTouched: () => void = () => { };\r\n private _onValidatorChange: () => void = () => { };\r\n\r\n // ─── Computed properties ────────────────────────────────────────────────────\r\n\r\n /** True when a format pattern has been set on this input. */\r\n private get hasPattern(): boolean {\r\n return this._resolvedPattern.length > 0;\r\n }\r\n\r\n /**\r\n * The set of literal separator characters defined in the current pattern.\r\n *\r\n * Pattern '(000) 000-0000' → separators: {'(', ')', ' ', '-'}\r\n * Pattern '0000 0000' → separators: {' '}\r\n *\r\n * Used when stripping decorations back off a displayed value.\r\n */\r\n private get patternSeparatorChars(): Set<string> {\r\n return new Set(\r\n this._resolvedPattern.split('').filter(char => char !== '0')\r\n );\r\n }\r\n\r\n /**\r\n * The total number of digit slots ('0' characters) in the current pattern.\r\n * 'card' pattern has 16 slots; 'expiry' has 4; etc.\r\n */\r\n private get patternDigitSlotCount(): number {\r\n return this._resolvedPattern.split('').filter(char => char === '0').length;\r\n }\r\n\r\n /**\r\n * The effective maximum digit length to enforce.\r\n * In pattern mode, this is always the slot count (overrides any [maxLength] input).\r\n * In non-pattern mode, it comes from the [maxLength] input (may be null = unlimited).\r\n */\r\n private get effectiveMaxLength(): number | null {\r\n return this.hasPattern ? this.patternDigitSlotCount : this.maxLength;\r\n }\r\n\r\n /**\r\n * The effective output type.\r\n * Pattern mode always emits strings because patterns are for identifiers.\r\n * Otherwise respects the [outputType] input.\r\n */\r\n private get effectiveOutputType(): 'number' | 'string' {\r\n return this.hasPattern ? 'string' : this.outputType;\r\n }\r\n\r\n /**\r\n * The decimal separator character used in the DISPLAY.\r\n * If the thousands separator is '.' (European style), we must use ','\r\n * as the decimal character to avoid ambiguity (1.234,56 is valid; 1,234,56 is not).\r\n * Otherwise we use the standard '.'.\r\n */\r\n private get displayDecimalChar(): string {\r\n return this.thousandSeparator === '.' ? ',' : '.';\r\n }\r\n\r\n /**\r\n * Returns true when the input element (or any of its ancestors) is in\r\n * right-to-left mode.\r\n *\r\n * Detection order:\r\n * 1. The input element's own computed `direction` style — this reflects\r\n * any `dir` attribute on the element itself, a CSS `direction` rule,\r\n * or inheritance from a parent element with dir=\"rtl\".\r\n * 2. Falls back to false (LTR) if the window / getComputedStyle is\r\n * unavailable (e.g. server-side rendering).\r\n *\r\n * This is a getter (not a cached field) so it always reflects the current\r\n * DOM state, even if the direction changes dynamically at runtime.\r\n */\r\n private get isRtl(): boolean {\r\n const win = this.document.defaultView;\r\n if (!win) return false;\r\n const dir = win.getComputedStyle(this.el.nativeElement).direction;\r\n return dir === 'rtl';\r\n }\r\n\r\n /**\r\n * In RTL mode the string stored in the input is prefixed with an LRM mark\r\n * so that the bidi algorithm does not reorder digits or separators.\r\n * This helper returns the prefix length including the LRM when RTL is active.\r\n *\r\n * Used wherever we need to know \"how many characters before the editable\r\n * number portion start\" — cursor clamping, minus-key position check, etc.\r\n */\r\n private get editableStartOffset(): number {\r\n // In RTL: LRM + suffix occupies the left side of the display string.\r\n // In LTR: prefix occupies the left side.\r\n return this.isRtl\r\n ? LRM.length + this.suffix.length // RTL: [LRM][suffix][number][prefix]\r\n : this.prefix.length; // LTR: [prefix][number][suffix]\r\n }\r\n\r\n /**\r\n * The number of characters at the RIGHT end of the display string that\r\n * belong to the suffix (LTR) or prefix (RTL) — i.e. characters the cursor\r\n * must never move past on the right side.\r\n */\r\n private get editableEndPadding(): number {\r\n return this.isRtl ? this.prefix.length : this.suffix.length;\r\n }\r\n\r\n constructor(\r\n private el: ElementRef<HTMLInputElement>,\r\n private renderer: Renderer2,\r\n @Inject(DOCUMENT) private document: Document,\r\n ) { }\r\n\r\n // ─── Angular lifecycle ────────────────────────────────────────────────────────\r\n\r\n ngOnInit(): void {\r\n // Always use numeric inputMode so mobile keyboards show the number pad\r\n // regardless of document direction.\r\n this.renderer.setAttribute(this.el.nativeElement, 'inputmode', 'numeric');\r\n\r\n // Render the initial empty state into the input element.\r\n // refreshDisplay() also sets the dir attribute on first call.\r\n this.refreshDisplay();\r\n }\r\n\r\n ngOnChanges(changes: SimpleChanges): void {\r\n const { prefix, suffix, thousandSeparator, decimalPlaces, outputType, pattern } = changes;\r\n\r\n // Re-render whenever any display-affecting input changes\r\n const displayInputsChanged =\r\n prefix ||\r\n suffix ||\r\n thousandSeparator ||\r\n decimalPlaces ||\r\n outputType ||\r\n pattern;\r\n\r\n if (displayInputsChanged) {\r\n this.refreshDisplay();\r\n }\r\n }\r\n\r\n // ─── ControlValueAccessor ────────────────────────────────────────────────────\r\n // These four methods are the bridge between the directive and Angular forms.\r\n\r\n /** Angular calls this when the form model value changes programmatically. */\r\n writeValue(value: string | number | null): void {\r\n if (value === null || value === undefined || value === '') {\r\n this._rawValue = '';\r\n } else {\r\n const stringValue = String(value);\r\n\r\n if (this.hasPattern) {\r\n // Strip any separators that may already be in the incoming string value\r\n this._rawValue = this.stripPatternSeparatorsFrom(stringValue);\r\n } else {\r\n // Normalize decimal separator to '.' for internal storage\r\n this._rawValue = stringValue.replace(',', '.');\r\n }\r\n }\r\n\r\n this.refreshDisplay();\r\n }\r\n\r\n /** Angular calls this to register the function we must call when our value changes. */\r\n registerOnChange(fn: (v: number | string | null) => void): void {\r\n this._onChange = fn;\r\n }\r\n\r\n /** Angular calls this to register the function we must call when the field is touched. */\r\n registerOnTouched(fn: () => void): void {\r\n this._onTouched = fn;\r\n }\r\n\r\n /** Angular calls this to enable or disable the input. */\r\n setDisabledState(isDisabled: boolean): void {\r\n this.renderer.setProperty(this.el.nativeElement, 'disabled', isDisabled);\r\n }\r\n\r\n // ─── Validator ───────────────────────────────────────────────────────────────\r\n\r\n /** Angular calls this to ask whether the current value has any errors. */\r\n validate(_control: AbstractControl): ValidationErrors | null {\r\n const errors: ValidationErrors = {};\r\n const raw = this._rawValue;\r\n\r\n // Empty or partial-minus input: let [required] handle the \"is it filled?\" check\r\n if (raw === '' || raw === '-') {\r\n return null;\r\n }\r\n\r\n // ── Pattern mode validation ──────────────────────────────────────────────\r\n if (this.hasPattern) {\r\n const filledDigitCount = raw.replace(REGEX.NON_DIGIT_CHARACTERS, '').length;\r\n const requiredDigitCount = this.patternDigitSlotCount;\r\n\r\n // The user must fill ALL slots before the value is considered complete\r\n if (filledDigitCount < requiredDigitCount) {\r\n errors['patternIncomplete'] = {\r\n required: requiredDigitCount,\r\n actual: filledDigitCount,\r\n pattern: this._resolvedPattern,\r\n };\r\n }\r\n\r\n return Object.keys(errors).length > 0 ? errors : null;\r\n }\r\n\r\n // ── Non-pattern: maxLength check ─────────────────────────────────────────\r\n const maxLen = this.effectiveMaxLength;\r\n if (maxLen !== null) {\r\n const digitCount = raw.replace(REGEX.NON_DIGIT_CHARACTERS, '').length;\r\n if (digitCount > maxLen) {\r\n errors['maxLength'] = { maxLength: maxLen, actual: digitCount };\r\n }\r\n }\r\n\r\n // ── String output: no numeric range validation needed ────────────────────\r\n // Identifiers (card numbers, phones) don't have a meaningful min/max.\r\n if (this.effectiveOutputType === 'string') {\r\n return Object.keys(errors).length > 0 ? errors : null;\r\n }\r\n\r\n // ── Number output: range validation ──────────────────────────────────────\r\n const numericValue = parseFloat(raw);\r\n\r\n if (isNaN(numericValue)) {\r\n errors['digits'] = { actual: raw };\r\n }\r\n\r\n if (this.min !== null && numericValue < this.min) {\r\n errors['min'] = { min: this.min, actual: numericValue };\r\n }\r\n\r\n if (this.max !== null && numericValue > this.max) {\r\n errors['max'] = { max: this.max, actual: numericValue };\r\n }\r\n\r\n return Object.keys(errors).length > 0 ? errors : null;\r\n }\r\n\r\n /** Angular calls this to register the function we call when validation rules change. */\r\n registerOnValidatorChange(fn: () => void): void {\r\n this._onValidatorChange = fn;\r\n }\r\n\r\n // ─── DOM event handlers ────────────────────────────────────────────────────\r\n\r\n /**\r\n * Intercept keydown BEFORE the character lands in the input.\r\n * This is our first line of defense — blocking bad keys entirely\r\n * so the input event never fires for them.\r\n */\r\n @HostListener('keydown', ['$event'])\r\n onKeyDown(event: KeyboardEvent): void {\r\n\r\n // Always pass through control keys (Backspace, arrows, Ctrl+C, etc.)\r\n if (this.isControlKey(event)) {\r\n return;\r\n }\r\n\r\n const pressedKey = event.key;\r\n\r\n // ── Allow minus sign (number mode only, not pattern mode) ─────────────\r\n const isMinusKey = pressedKey === '-';\r\n const minusIsAllowed =\r\n this.allowNegative &&\r\n !this.hasPattern &&\r\n this.effectiveOutputType === 'number';\r\n\r\n // The \"start of the editable number region\" depends on direction:\r\n // LTR: cursor must be at prefix.length (characters the prefix occupies)\r\n // RTL: cursor must be at LRM.length + suffix.length (LRM + suffix on left)\r\n // editableStartOffset encapsulates this logic so onKeyDown stays clean.\r\n const cursorPosition = this.el.nativeElement.selectionStart ?? 0;\r\n const cursorIsAtStart = cursorPosition === this.editableStartOffset;\r\n\r\n const noMinusYet = !this._rawValue.startsWith('-');\r\n\r\n if (isMinusKey && minusIsAllowed && cursorIsAtStart && noMinusYet) {\r\n return; // allow the minus through\r\n }\r\n\r\n // ── Allow decimal point (number mode only, no pattern) ────────────────\r\n const isDecimalKey = pressedKey === '.' || pressedKey === ',';\r\n const decimalIsAllowed = this.decimalPlaces > 0 && !this.hasPattern;\r\n const noDecimalYet = !this._rawValue.includes('.');\r\n\r\n if (isDecimalKey && decimalIsAllowed && noDecimalYet) {\r\n return; // allow the decimal separator through\r\n }\r\n\r\n // ── Allow digit keys (0–9 AND Eastern Arabic/Persian digits) ────────────\r\n //\r\n // IMPORTANT: REGEX.SINGLE_DIGIT only matches ASCII digits /^\\d$/ (U+0030–U+0039).\r\n // Arabic-Indic digits ٠-٩ (U+0660–U+0669) and Persian digits ۰-۹ (U+06F0–U+06F9)\r\n // are NOT matched by \\d, so they would fall through to the \"block everything else\"\r\n // branch and be prevented — even when convertEasternNumerals is true.\r\n //\r\n // The fix: also check for Eastern digit characters here in keydown.\r\n // The actual conversion from Eastern → Western happens later in onInput(),\r\n // so all we need to do here is let the keystroke land in the input.\r\n const isWesternDigit = REGEX.SINGLE_DIGIT.test(pressedKey);\r\n const isArabicDigit = pressedKey >= REGEX.ARABIC_DIGIT_RANGE.FIRST &&\r\n pressedKey <= REGEX.ARABIC_DIGIT_RANGE.LAST;\r\n const isPersianDigit = pressedKey >= REGEX.PERSIAN_DIGIT_RANGE.FIRST &&\r\n pressedKey <= REGEX.PERSIAN_DIGIT_RANGE.LAST;\r\n const isEasternDigit = this.convertEasternNumerals && (isArabicDigit || isPersianDigit);\r\n const isDigitKey = isWesternDigit || isEasternDigit;\r\n\r\n if (isDigitKey) {\r\n // Enforce the digit slot limit BEFORE the character is inserted.\r\n // Count only the raw digits already in the value (exclude separators/prefix/suffix).\r\n const maxLen = this.effectiveMaxLength;\r\n if (maxLen !== null) {\r\n const currentDigitCount = this._rawValue.replace(REGEX.NON_DIGIT_CHARACTERS, '').length;\r\n const selectedChars = Math.abs(\r\n (this.el.nativeElement.selectionEnd ?? 0) -\r\n (this.el.nativeElement.selectionStart ?? 0)\r\n );\r\n const nothingSelected = selectedChars === 0;\r\n\r\n if (nothingSelected && currentDigitCount >= maxLen) {\r\n event.preventDefault(); // already at limit — block the digit\r\n return;\r\n }\r\n }\r\n return; // digit is allowed — Eastern digits will be converted in onInput()\r\n }\r\n\r\n // ── Block everything else ─────────────────────────────────────────────\r\n event.preventDefault();\r\n }\r\n\r\n /**\r\n * Process input AFTER the character has landed in the input element.\r\n * We read the new display value, strip decorations to get raw digits,\r\n * apply rules (decimal limits, maxLength, leading zeros), then re-render.\r\n */\r\n @HostListener('input')\r\n onInput(): void {\r\n const inputEl = this.el.nativeElement;\r\n const cursorPositionBeforeReformat = inputEl.selectionStart ?? 0;\r\n const displayLengthBeforeReformat = inputEl.value.length;\r\n\r\n // Convert Eastern numerals FIRST, before anything else touches the value\r\n if (this.convertEasternNumerals) {\r\n const converted = convertEasternDigits(inputEl.value);\r\n if (converted !== inputEl.value) {\r\n // Write the converted value directly into the DOM so our stripping below\r\n // works on clean ASCII characters\r\n this.renderer.setProperty(inputEl, 'value', converted);\r\n }\r\n }\r\n\r\n // Strip prefix, suffix, and visual separators — extract the raw digit core\r\n let raw = this.stripDecorationsFrom(inputEl.value);\r\n\r\n if (this.hasPattern) {\r\n // Pattern mode: keep only digits, cap at slot count\r\n raw = raw.replace(REGEX.ONLY_DIGITS, '');\r\n raw = raw.slice(0, this.patternDigitSlotCount);\r\n\r\n } else {\r\n // Non-pattern mode: normalize decimal separator\r\n raw = raw.replace(',', '.');\r\n\r\n if (this.decimalPlaces === 0 || this.effectiveOutputType === 'string') {\r\n // Integer or string mode: no decimal points allowed\r\n raw = raw.replace(REGEX.ALL_DOTS, '');\r\n } else {\r\n // Decimal mode: allow exactly one dot with up to N decimal places\r\n raw = this.enforceDecimalRules(raw);\r\n }\r\n\r\n // Strip leading zeros unless explicitly preserved\r\n const shouldStripLeadingZeros =\r\n !this.leadingZeros && this.effectiveOutputType === 'number';\r\n if (shouldStripLeadingZeros) {\r\n raw = this.stripLeadingZeros(raw);\r\n }\r\n }\r\n\r\n this._rawValue = raw;\r\n this.refreshDisplay(cursorPositionBeforeReformat, displayLengthBeforeReformat);\r\n this._onChange(this.buildModelValue());\r\n this._onValidatorChange();\r\n }\r\n\r\n /**\r\n * When the user leaves the field, mark it as touched (triggers validation\r\n * display) and clean up any trailing decimal point (e.g. the user typed\r\n * '12.' and then tabbed away).\r\n */\r\n @HostListener('blur')\r\n onBlur(): void {\r\n this._onTouched();\r\n\r\n const hasTrailingDot =\r\n !this.hasPattern &&\r\n this.effectiveOutputType === 'number' &&\r\n this._rawValue.endsWith('.');\r\n\r\n if (hasTrailingDot) {\r\n this._rawValue = this._rawValue.slice(0, -1);\r\n this.refreshDisplay();\r\n }\r\n }\r\n\r\n /**\r\n * Handle paste events manually so we can sanitize the pasted content\r\n * before it reaches the input, just like we do for individual keystrokes.\r\n */\r\n @HostListener('paste', ['$event'])\r\n onPaste(event: ClipboardEvent): void {\r\n event.preventDefault(); // stop the browser from pasting the raw text\r\n\r\n const pastedText = event.clipboardData?.getData('text') ?? '';\r\n\r\n // Convert Eastern numerals in pasted content too\r\n const normalized = this.convertEasternNumerals\r\n ? convertEasternDigits(pastedText)\r\n : pastedText;\r\n\r\n // Sanitize the pasted string to only the characters our mode allows\r\n const sanitized = this.hasPattern\r\n ? normalized.replace(REGEX.ONLY_DIGITS, '') // pattern: digits only\r\n : this.sanitizePastedText(normalized); // non-pattern: mode-aware\r\n\r\n // Bail out if there's nothing usable to paste.\r\n // (Previously this also tried to special-case a paste of just '0', but\r\n // comparing a value to both '' and '0' in the same check is a logical\r\n // impossibility — a string can never be both at once — so that branch\r\n // was unreachable dead code and TypeScript correctly flagged it.)\r\n if (sanitized === '') return;\r\n\r\n const inputEl = this.el.nativeElement;\r\n\r\n // Figure out where the cursor/selection is inside the RAW digit string\r\n const rawInsertStart = this.hasPattern\r\n ? this.mapDisplayCursorToRawIndex(inputEl.selectionStart ?? 0)\r\n : (inputEl.selectionStart ?? 0);\r\n const rawInsertEnd = this.hasPattern\r\n ? this.mapDisplayCursorToRawIndex(inputEl.selectionEnd ?? 0)\r\n : (inputEl.selectionEnd ?? 0);\r\n\r\n // Insert sanitized content into the raw value at the cursor position\r\n const beforeCursor = this._rawValue.slice(0, rawInsertStart);\r\n const afterCursor = this._rawValue.slice(rawInsertEnd);\r\n let newRaw = beforeCursor + sanitized + afterCursor;\r\n\r\n if (this.hasPattern) {\r\n // Pattern mode: keep digits only, cap at slot count\r\n newRaw = newRaw.replace(REGEX.ONLY_DIGITS, '').slice(0, this.patternDigitSlotCount);\r\n\r\n } else {\r\n // Non-pattern mode: apply the same rules as onInput\r\n if (this.decimalPlaces === 0 || this.effectiveOutputType === 'string') {\r\n newRaw = newRaw.replace(REGEX.ALL_DOTS, '');\r\n } else {\r\n newRaw = this.enforceDecimalRules(newRaw);\r\n }\r\n\r\n const maxLen = this.effectiveMaxLength;\r\n const rawDigitCount = newRaw.replace(REGEX.NON_DIGIT_CHARACTERS, '').length;\r\n if (maxLen !== null && rawDigitCount > maxLen) {\r\n return; // pasted content would exceed digit limit — discard\r\n }\r\n\r\n const shouldStripLeadingZeros =\r\n !this.leadingZeros && this.effectiveOutputType === 'number';\r\n if (shouldStripLeadingZeros) {\r\n newRaw = this.stripLeadingZeros(newRaw);\r\n }\r\n }\r\n\r\n this._rawValue = newRaw;\r\n this.refreshDisplay();\r\n this._onChange(this.buildModelValue());\r\n this._onValidatorChange();\r\n }\r\n\r\n /**\r\n * Block drag-and-drop into the field.\r\n * Without this a user could drag text from elsewhere and bypass our rules.\r\n */\r\n @HostListener('drop', ['$event'])\r\n onDrop(event: DragEvent): void {\r\n event.preventDefault();\r\n }\r\n\r\n // ─── Private methods ───────────────────────────────────────────────────────\r\n\r\n /**\r\n * Build the value that gets emitted to the Angular form model.\r\n *\r\n * Pattern / string mode → string | null\r\n * '' → null (empty field)\r\n * '123' → '123' (raw digits, leading zeros preserved, no separators)\r\n *\r\n * Number mode → number | null\r\n * '' → null (empty field)\r\n * '-' → null (user typed a minus but no digits yet)\r\n * '123' → 123\r\n * '1.5' → 1.5\r\n * '-7' → -7\r\n */\r\n private buildModelValue(): number | string | null {\r\n if (this._rawValue === '') {\r\n return null;\r\n }\r\n\r\n if (this.effectiveOutputType === 'string') {\r\n return this._rawValue || null;\r\n }\r\n\r\n // Number mode\r\n if (this._rawValue === '-') {\r\n return null; // not a valid number yet\r\n }\r\n\r\n const num = parseFloat(this._rawValue);\r\n return isNaN(num) ? null : num;\r\n }\r\n\r\n /**\r\n * Write the formatted display string back to the <input> DOM element\r\n * and try to restore the cursor to a sensible position.\r\n *\r\n * @param cursorBefore Cursor position in the display string BEFORE we reformatted\r\n * @param lengthBefore Total display string length BEFORE we reformatted\r\n */\r\n private refreshDisplay(cursorBefore?: number, lengthBefore?: number): void {\r\n // ── Step 1: format the number portion ─────────────────────────────────\r\n const formattedContent = this.hasPattern\r\n ? this.applyPatternFormatting(this._rawValue)\r\n : this.effectiveOutputType === 'string'\r\n ? this._rawValue // string mode: no formatting\r\n : this.applyThousandsFormatting(this._rawValue); // number mode: comma groups\r\n\r\n // ── Step 2: assemble the full display string ───────────────────────────\r\n //\r\n // RTL layout: [LRM][suffix][formattedNumber][prefix]\r\n // LTR layout: [prefix][formattedNumber][suffix]\r\n //\r\n // WHY SWAP PREFIX AND SUFFIX IN RTL?\r\n // In RTL reading order, the \"start\" of a line is the right side.\r\n // A currency symbol like \"﷼\" (Rial) naturally sits on the RIGHT of\r\n // the number in Arabic/Persian convention, while a unit like \"kg\"\r\n // sits on the LEFT. That maps to: prefix visually right, suffix left.\r\n // By swapping their positions in the string, the bidi algorithm places\r\n // them correctly without any CSS changes.\r\n //\r\n // WHY LRM AT THE FRONT?\r\n // Prevents the bidi algorithm from treating the numeric content as RTL\r\n // text and mirroring digit order or separator placement.\r\n // The LRM is stripped before the value reaches the model.\r\n const fullDisplayValue = this.isRtl\r\n ? LRM + this.suffix + formattedContent + this.prefix\r\n : this.prefix + formattedContent + this.suffix;\r\n\r\n this.renderer.setProperty(this.el.nativeElement, 'value', fullDisplayValue);\r\n\r\n // ── Step 3: restore cursor position ───────────────────────────────────\r\n if (cursorBefore !== undefined && lengthBefore !== undefined) {\r\n const lengthDelta = fullDisplayValue.length - lengthBefore;\r\n\r\n // Clamp cursor between the editable start and end offsets,\r\n // both of which account for the RTL/LTR decoration layout.\r\n const newCursorPos = Math.max(\r\n this.editableStartOffset,\r\n Math.min(\r\n cursorBefore + lengthDelta,\r\n fullDisplayValue.length - this.editableEndPadding,\r\n ),\r\n );\r\n\r\n // Defer to next animation frame so the browser has committed the new value\r\n requestAnimationFrame(() => {\r\n this.el.nativeElement.setSelectionRange(newCursorPos, newCursorPos);\r\n });\r\n }\r\n }\r\n\r\n /**\r\n * Apply the display format pattern to the current raw digit string.\r\n *\r\n * Algorithm:\r\n * Walk through the pattern character by character.\r\n * On '0': consume the next raw digit and add it to the result.\r\n * On anything else: this is a separator — add it ONLY if more digits follow.\r\n * (The \"more digits follow\" check prevents trailing separators mid-type.)\r\n *\r\n * Examples:\r\n * pattern '0000 0000 0000 0000', raw '41111111' → '4111 1111'\r\n * pattern '(000) 000-0000', raw '5558675' → '(555) 867'\r\n * pattern '00/00', raw '122' → '12/2'\r\n */\r\n private applyPatternFormatting(raw: string): string {\r\n if (!this.hasPattern || !raw) {\r\n return raw;\r\n }\r\n\r\n const digits = raw.replace(REGEX.ONLY_DIGITS, ''); // isolated digit string\r\n let digitIndex = 0;\r\n let displayResult = '';\r\n\r\n for (const patternChar of this._resolvedPattern) {\r\n // Stop when we run out of digits to place\r\n if (digitIndex >= digits.length) {\r\n break;\r\n }\r\n\r\n if (patternChar === '0') {\r\n // Digit slot: consume the next digit\r\n displayResult += digits[digitIndex];\r\n digitIndex++;\r\n } else {\r\n // Separator character: insert it only when more digits are still coming\r\n // so we never show a trailing space/dash after the last typed digit\r\n const moreDigitsRemain = digitIndex < digits.length;\r\n if (moreDigitsRemain) {\r\n displayResult += patternChar;\r\n }\r\n }\r\n }\r\n\r\n return displayResult;\r\n }\r\n\r\n /**\r\n * Apply thousands separator grouping and decimal character to a raw number string.\r\n * Only used in number output mode (not pattern, not string mode).\r\n *\r\n * Examples (separator=',', decimalChar='.'):\r\n * '1234567' → '1,234,567'\r\n * '-9876.50' → '-9,876.50'\r\n * '100' → '100'\r\n */\r\n private applyThousandsFormatting(raw: string): string {\r\n if (raw === '' || raw === '-') {\r\n return raw;\r\n }\r\n\r\n const isNegative = raw.startsWith('-');\r\n const absValue = isNegative ? raw.slice(1) : raw;\r\n const [integerPart, decimalPart] = absValue.split('.');\r\n\r\n // Insert the thousands separator every 3 digits from the right\r\n const formattedInteger = this.thousandSeparator\r\n ? integerPart.replace(REGEX.THOUSANDS_SEPARATOR_POSITIONS, this.thousandSeparator)\r\n : integerPart;\r\n\r\n const formattedDecimal = decimalPart !== undefined\r\n ? this.displayDecimalChar + decimalPart\r\n : '';\r\n\r\n return (isNegative ? '-' : '') + formattedInteger + formattedDecimal;\r\n }\r\n\r\n /**\r\n * Remove all visual decoration from the displayed input value and return\r\n * the bare digit/numeric string ready for internal storage.\r\n *\r\n * Steps:\r\n * 1. Strip prefix from the front\r\n * 2. Strip suffix from the back\r\n * 3. Remove pattern separators OR thousands separators (depending on mode)\r\n * 4. Normalize non-ASCII decimal separator back to '.'\r\n * 5. Remove any remaining non-numeric characters\r\n */\r\n private stripDecorationsFrom(displayValue: string): string {\r\n let raw = displayValue;\r\n\r\n // ── Remove the LRM marker if present ──────────────────────────────────\r\n // In RTL mode every display string starts with LRM (U+200E).\r\n // Strip it first so subsequent prefix/suffix checks work correctly.\r\n if (raw.startsWith(LRM)) {\r\n raw = raw.slice(LRM.length);\r\n }\r\n\r\n if (this.isRtl) {\r\n // RTL layout: [suffix][formattedNumber][prefix]\r\n // Remove suffix from the LEFT (start) and prefix from the RIGHT (end).\r\n if (this.suffix && raw.startsWith(this.suffix)) {\r\n raw = raw.slice(this.suffix.length);\r\n }\r\n if (this.prefix && raw.endsWith(this.prefix)) {\r\n raw = raw.slice(0, -this.prefix.length);\r\n }\r\n } else {\r\n // LTR layout: [prefix][formattedNumber][suffix]\r\n // Remove prefix from the LEFT and suffix from the RIGHT.\r\n if (this.prefix && raw.startsWith(this.prefix)) {\r\n raw = raw.slice(this.prefix.length);\r\n }\r\n if (this.suffix && raw.endsWith(this.suffix)) {\r\n raw = raw.slice(0, -this.suffix.length);\r\n }\r\n }\r\n\r\n if (this.hasPattern) {\r\n // Pattern mode: remove the separator characters defined by the pattern\r\n raw = this.stripPatternSeparatorsFrom(raw);\r\n } else if (this.thousandSeparator) {\r\n // Number mode with thousands grouping: remove the grouping character\r\n const escapedSeparator = this.thousandSeparator.replace(\r\n REGEX.REGEX_SPECIAL_CHARACTERS,\r\n '\\\\$&' // prepend \\ before any regex-special character\r\n );\r\n raw = raw.replace(new RegExp(escapedSeparator, 'g'), '');\r\n }\r\n\r\n // If we're using a non-standard decimal char (e.g. ',' in European mode),\r\n // normalize it back to '.' for internal storage\r\n if (!this.hasPattern && this.displayDecimalChar !== '.') {\r\n raw = raw.replace(this.displayDecimalChar, '.');\r\n }\r\n\r\n // Final sweep: remove anything that still isn't a digit, '.', or '-'\r\n raw = raw.replace(REGEX.ONLY_DIGITS_DOT_AND_MINUS, '');\r\n\r\n return raw;\r\n }\r\n\r\n /**\r\n * Remove every separator character that the current pattern defines.\r\n * For example, pattern '(000) 000-0000' defines '(', ')', ' ', '-' as separators.\r\n * This strips all four from the input string, leaving only digits.\r\n */\r\n private stripPatternSeparatorsFrom(value: string): string {\r\n let result = value;\r\n\r\n for (const separatorChar of this.patternSeparatorChars) {\r\n const escapedChar = separatorChar.replace(REGEX.REGEX_SPECIAL_CHARACTERS, '\\\\$&');\r\n result = result.replace(new RegExp(escapedChar, 'g'), '');\r\n }\r\n\r\n return result;\r\n }\r\n\r\n /**\r\n * Ensure a raw numeric string has at most one decimal point\r\n * and at most [decimalPlaces] digits after it.\r\n *\r\n * Examples (decimalPlaces=2):\r\n * '12.345' → '12.34'\r\n * '1.2.3' → '1.23'\r\n * '100' → '100'\r\n */\r\n private enforceDecimalRules(raw: string): string {\r\n const parts = raw.split('.');\r\n\r\n // More than one dot? Collapse everything after the first dot into one decimal part\r\n if (parts.length > 2) {\r\n raw = parts[0] + '.' + parts.slice(1).join('');\r\n }\r\n\r\n // Too many decimal digits? Truncate to the allowed count\r\n const [intPart, decPart] = raw.split('.');\r\n if (decPart !== undefined && decPart.length > this.decimalPlaces) {\r\n raw = intPart + '.' + decPart.slice(0, this.decimalPlaces);\r\n }\r\n\r\n return raw;\r\n }\r\n\r\n /**\r\n * Remove unnecessary leading zeros from the integer part of a numeric string.\r\n * Always preserves at least one digit and never touches the decimal part.\r\n *\r\n * Examples:\r\n * '007' → '7'\r\n * '0042' → '42'\r\n * '0.5' → '0.5' (leading zero before decimal is correct, don't strip)\r\n * '100' → '100' (no leading zero)\r\n * '-007' → '-7' (handles negative values)\r\n */\r\n private stripLeadingZeros(raw: string): string {\r\n if (!raw) {\r\n return raw;\r\n }\r\n\r\n const isNegative = raw.startsWith('-');\r\n const absValue = isNegative ? raw.slice(1) : raw;\r\n const dotPosition = absValue.indexOf('.');\r\n const integerPart = dotPosition >= 0 ? absValue.slice(0, dotPosition) : absValue;\r\n const decimalPart = dotPosition >= 0 ? absValue.slice(dotPosition) : '';\r\n\r\n // Replace leading zeros but always keep at least one digit\r\n const cleanedInteger = integerPart.replace(REGEX.LEADING_ZEROS, '$1') || integerPart;\r\n\r\n return (isNegative ? '-' : '') + cleanedInteger + decimalPart;\r\n }\r\n\r\n /**\r\n * Sanitize a pasted string for non-pattern mode.\r\n * The characters we keep depend on the current mode:\r\n *\r\n * string mode → digits only (no dot, no minus; identifiers can't be negative)\r\n * integer number → digits + optional leading minus\r\n * decimal number → digits + optional dot + optional leading minus\r\n */\r\n private sanitizePastedText(text: string): string {\r\n // Normalize decimal comma first (e.g. from a European spreadsheet paste)\r\n let clean = text.replace(',', '.');\r\n\r\n if (this.effectiveOutputType === 'string') {\r\n clean = clean.replace(REGEX.ONLY_DIGITS, ''); // string: digits only\r\n } else if (this.decimalPlaces === 0) {\r\n clean = clean.replace(REGEX.ONLY_DIGITS_AND_MINUS, ''); // integer: digits + minus\r\n } else {\r\n clean = clean.replace(REGEX.ONLY_DIGITS_DOT_AND_MINUS, ''); // decimal: digits + . + minus\r\n }\r\n\r\n // Remove minus signs unless negative values are permitted\r\n if (!this.allowNegative || this.effectiveOutputType === 'string') {\r\n clean = clean.replace(REGEX.ALL_MINUS_SIGNS, '');\r\n }\r\n\r\n return clean;\r\n }\r\n\r\n /**\r\n * Map a cursor position in the formatted DISPLAY string back to an index\r\n * in the raw digit string.\r\n *\r\n * This is needed during paste operations in pattern mode so we know\r\n * exactly which digit position the cursor is sitting at.\r\n *\r\n * Example:\r\n * pattern '(000) 000-0000'\r\n * display '(555) 867' cursor at position 8 (after '(555) 86')\r\n *\r\n * Walking the pattern:\r\n * '(' pos 0 → sep (adjusted pos 0, raw index still 0)\r\n * '0' pos 1 → digit (raw index 0) digit consumed → raw index 1\r\n * '0' pos 2 → digit (raw index 1) digit consumed → raw index 2\r\n * '0' pos 3 → digit (raw index 2) digit consumed → raw index 3\r\n * ')' pos 4 → sep\r\n * ' ' pos 5 → sep\r\n * '0' pos 6 → digit (raw index 3) digit consumed → raw index 4\r\n * '0' pos 7 → digit (raw index 4) digit consumed → raw index 5\r\n * We reached position 8, so raw index = 5 ✔\r\n */\r\n private mapDisplayCursorToRawIndex(displayCursorPos: number): number {\r\n if (!this.hasPattern) {\r\n return displayCursorPos;\r\n }\r\n\r\n // Adjust for prefix length so we walk the pattern relative to its own start\r\n const positionInPattern = displayCursorPos - this.prefix.length;\r\n let rawDigitIndex = 0;\r\n let patternPosition = 0;\r\n\r\n for (const patternChar of this._resolvedPattern) {\r\n if (patternPosition >= positionInPattern) {\r\n break;\r\n }\r\n\r\n if (patternChar === '0') {\r\n rawDigitIndex++; // this position consumed a digit\r\n }\r\n\r\n patternPosition++;\r\n }\r\n\r\n return rawDigitIndex;\r\n }\r\n\r\n /**\r\n * Decide whether a keyboard event is a \"control\" key that should always\r\n * be allowed through without interception.\r\n *\r\n * Includes:\r\n * - Modifier keys held (Ctrl, Meta/Cmd, Alt) — so Ctrl+C, Ctrl+V, etc. work\r\n * - Navigation keys (arrows, Home, End)\r\n * - Editing keys (Backspace, Delete, Tab, Escape, Enter)\r\n */\r\n private isControlKey(event: KeyboardEvent): boolean {\r\n const isModifierHeld = event.ctrlKey || event.metaKey || event.altKey;\r\n\r\n const isNavigationOrEditKey = [\r\n 'Backspace', 'Delete',\r\n 'Tab', 'Escape', 'Enter',\r\n 'ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown',\r\n 'Home', 'End',\r\n ].includes(event.key);\r\n\r\n return isModifierHeld || isNavigationOrEditKey;\r\n }\r\n}\r\n","/*\r\n * Public API Surface of digits-only\r\n */\r\n\r\nexport * from './lib/digits-only.directive';\r\nexport * from './lib/eastern-numerals';\r\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ngx-digits-only.mjs","sources":["../../../projects/digits-only/src/lib/digits-only.regex.ts","../../../projects/digits-only/src/lib/eastern-numerals.ts","../../../projects/digits-only/src/lib/digits-only.directive.ts","../../../projects/digits-only/src/public-api.ts","../../../projects/digits-only/src/ngx-digits-only.ts"],"sourcesContent":["/**\r\n * digits-only.regex.ts\r\n *\r\n * Every regular expression used by the digitsOnly directive lives here.\r\n * Each constant has a plain-English name, a detailed breakdown, and live examples\r\n * so you never have to decode cryptic patterns inside the directive itself.\r\n *\r\n * Import what you need:\r\n * import { REGEX } from './digits-only.regex';\r\n * REGEX.SINGLE_DIGIT.test('5') // true\r\n */\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// HOW TO READ REGEX ANATOMY\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n//\r\n// /pattern/flags\r\n// │ │\r\n// │ └── modifiers: g = global (find ALL matches, not just first)\r\n// │ i = case-insensitive\r\n// │ m = multiline\r\n// │\r\n// └── special characters:\r\n// ^ start of string (or line with m flag)\r\n// $ end of string (or line with m flag)\r\n// . any single character except newline\r\n// \\d any digit [0-9]\r\n// \\D any NON-digit\r\n// \\w word character [a-zA-Z0-9_]\r\n// \\s whitespace\r\n// \\B NON-word boundary position\r\n// [abc] character class – matches a, b, or c\r\n// [^abc] negated class – matches anything EXCEPT a, b, c\r\n// (abc) capture group\r\n// (?=x) positive lookahead – \"followed by x\" (zero-width, no consume)\r\n// (?!x) negative lookahead – \"NOT followed by x\"\r\n// {n} exactly n repetitions\r\n// {n,} n or more repetitions\r\n// + one or more (greedy)\r\n// * zero or more (greedy)\r\n// ? zero or one (optional)\r\n// \\. escaped dot – a literal '.' character\r\n// \\\\ escaped backslash – a literal '\\'\r\n// \\- escaped hyphen inside [] – a literal '-'\r\n// $& in replacement strings: the entire matched text\r\n\r\nexport const REGEX = {\r\n\r\n // ──────────────────────────────────────────────────────────────────────────\r\n // 1. SINGLE_DIGIT\r\n // Is this one keyboard character exactly a digit and nothing else?\r\n // ──────────────────────────────────────────────────────────────────────────\r\n //\r\n // Pattern breakdown:\r\n // ^ – anchor: string must START here\r\n // \\d – one digit character (0, 1, 2 … 9)\r\n // $ – anchor: string must END here\r\n //\r\n // The ^ and $ together mean the whole string must be exactly one digit.\r\n // Without them, /\\d/ would match '5' inside 'abc5xyz' too.\r\n //\r\n // Used in: onKeyDown() — to decide if a pressed key is a digit\r\n //\r\n // ✔ matches: '0' '5' '9'\r\n // ✖ no match: 'a' '-' '.' 'Enter' '12' ''\r\n //\r\n SINGLE_DIGIT: /^\\d$/,\r\n\r\n // ──────────────────────────────────────────────────────────────────────────\r\n // 2. NON_DIGIT_CHARACTERS\r\n // Find (and remove) every character that is NOT a digit.\r\n // ──────────────────────────────────────────────────────────────────────────\r\n //\r\n // Pattern breakdown:\r\n // [^0-9] – negated character class\r\n // ^ inside [] = NOT\r\n // 0-9 = any digit\r\n // → \"any character that is not 0 through 9\"\r\n // g – global flag: match ALL occurrences, not just the first\r\n //\r\n // Used in: onKeyDown (count digits), validate (count digits), onPaste\r\n //\r\n // Purpose: Calling .replace(NON_DIGIT_CHARACTERS, '') strips everything\r\n // except digits so we can measure the pure digit length,\r\n // ignoring separators, dots, minus signs, prefix/suffix.\r\n //\r\n // Examples:\r\n // '$1,234.56'.replace(REGEX.NON_DIGIT_CHARACTERS, '') → '123456'\r\n // '-99_000'.replace(REGEX.NON_DIGIT_CHARACTERS, '') → '99000'\r\n // '007'.replace(REGEX.NON_DIGIT_CHARACTERS, '') → '007'\r\n //\r\n NON_DIGIT_CHARACTERS: /[^0-9]/g,\r\n\r\n // ──────────────────────────────────────────────────────────────────────────\r\n // 3. ONLY_DIGITS_AND_MINUS\r\n // Remove everything except digits and the minus sign.\r\n // Used for integer-mode paste sanitization.\r\n // ──────────────────────────────────────────────────────────────────────────\r\n //\r\n // Pattern breakdown:\r\n // [^0-9\\-] – negated class that keeps digits and the literal hyphen\r\n // \\- = escaped hyphen (a bare '-' in the middle of [] is\r\n // treated as a range operator, e.g. [a-z];\r\n // escaping with \\ makes it a literal minus character)\r\n // g – global\r\n //\r\n // Used in: sanitizePastedText() when decimalPlaces === 0\r\n //\r\n // Examples (integers only, negatives allowed):\r\n // '1,234.56'.replace(REGEX.ONLY_DIGITS_AND_MINUS, '') → '123456'\r\n // '-99.9%'.replace(REGEX.ONLY_DIGITS_AND_MINUS, '') → '-999'\r\n // '007'.replace(REGEX.ONLY_DIGITS_AND_MINUS, '') → '007'\r\n //\r\n ONLY_DIGITS_AND_MINUS: /[^0-9\\-]/g,\r\n\r\n // ──────────────────────────────────────────────────────────────────────────\r\n // 4. ONLY_DIGITS_DOT_AND_MINUS\r\n // Remove everything except digits, a decimal point, and the minus sign.\r\n // Used for decimal-number paste sanitization.\r\n // ──────────────────────────────────────────────────────────────────────────\r\n //\r\n // Pattern breakdown:\r\n // [^0-9.\\-] – negated class\r\n // . inside [] is a literal dot (no escape needed inside [])\r\n // \\- is the literal minus\r\n // g – global\r\n //\r\n // Difference from ONLY_DIGITS_AND_MINUS:\r\n // This one also KEEPS the dot '.' so decimal numbers like '12.5' survive.\r\n //\r\n // Used in: sanitizePastedText() when decimalPlaces > 0\r\n //\r\n // Examples:\r\n // '$-1,234.56 USD'.replace(REGEX.ONLY_DIGITS_DOT_AND_MINUS, '') → '-1234.56'\r\n // '€ 99.9%'.replace(REGEX.ONLY_DIGITS_DOT_AND_MINUS, '') → '99.9'\r\n // 'abc123def'.replace(REGEX.ONLY_DIGITS_DOT_AND_MINUS, '') → '123'\r\n //\r\n ONLY_DIGITS_DOT_AND_MINUS: /[^0-9.\\-]/g,\r\n\r\n // ──────────────────────────────────────────────────────────────────────────\r\n // 5. ONLY_DIGITS\r\n // Remove everything except bare digits.\r\n // Used for string-mode (identifiers like card numbers) where dots and\r\n // minus signs are also illegal.\r\n // ──────────────────────────────────────────────────────────────────────────\r\n //\r\n // Pattern breakdown:\r\n // [^0-9] – keep only digit characters\r\n // g – global\r\n //\r\n // Used in: sanitizePastedText() in string / pattern mode,\r\n // onPaste() in pattern mode,\r\n // _applyPattern() to extract digit string before formatting\r\n //\r\n // Examples:\r\n // '4111-1111-1111-1111'.replace(REGEX.ONLY_DIGITS, '') → '4111111111111111'\r\n // '(555) 867-5309'.replace(REGEX.ONLY_DIGITS, '') → '5558675309'\r\n // '01/01/1990'.replace(REGEX.ONLY_DIGITS, '') → '01011990'\r\n //\r\n ONLY_DIGITS: /[^0-9]/g,\r\n\r\n // ──────────────────────────────────────────────────────────────────────────\r\n // 6. ALL_DOTS\r\n // Remove every dot/period from a string.\r\n // ──────────────────────────────────────────────────────────────────────────\r\n //\r\n // Pattern breakdown:\r\n // \\. – escaped dot.\r\n // In regex, a bare '.' means \"any character except newline\".\r\n // The backslash \\ escapes it to mean a LITERAL period character.\r\n // Without the backslash, /./g would erase EVERY character.\r\n // g – global: remove all dots, not just the first\r\n //\r\n // Used in: onInput() and onPaste() when decimalPlaces === 0 or pattern is set.\r\n // If integers are expected, any dot that slips through (e.g. from paste)\r\n // must be stripped.\r\n //\r\n // Examples:\r\n // '12.34'.replace(REGEX.ALL_DOTS, '') → '1234'\r\n // '1.2.3'.replace(REGEX.ALL_DOTS, '') → '123'\r\n // '100'.replace(REGEX.ALL_DOTS, '') → '100' (no dot, unchanged)\r\n //\r\n ALL_DOTS: /\\./g,\r\n\r\n // ──────────────────────────────────────────────────────────────────────────\r\n // 7. ALL_MINUS_SIGNS\r\n // Remove every minus / hyphen character from a string.\r\n // ──────────────────────────────────────────────────────────────────────────\r\n //\r\n // Pattern breakdown:\r\n // - – a literal hyphen/minus character\r\n // (safe outside of a character class [] without escaping)\r\n // g – global\r\n //\r\n // Used in: sanitizePastedText() to strip minus signs when allowNegative is false\r\n // or when outputType is 'string' (identifiers can't be negative).\r\n //\r\n // Examples:\r\n // '-99'.replace(REGEX.ALL_MINUS_SIGNS, '') → '99'\r\n // '1-2-3'.replace(REGEX.ALL_MINUS_SIGNS, '') → '123'\r\n //\r\n ALL_MINUS_SIGNS: /-/g,\r\n\r\n // ──────────────────────────────────────────────────────────────────────────\r\n // 8. THOUSANDS_SEPARATOR_POSITIONS\r\n // Find every position in the INTEGER PART of a number where a thousands\r\n // separator (comma, dot, space, underscore) should be inserted.\r\n // This is the most sophisticated regex in the codebase.\r\n // ──────────────────────────────────────────────────────────────────────────\r\n //\r\n // Full pattern: /\\B(?=(\\d{3})+(?!\\d))/g\r\n //\r\n // Token-by-token breakdown:\r\n //\r\n // \\B – NON-word-boundary position.\r\n // Matches a position that is NOT at the very start of the\r\n // string and NOT right after a non-word character.\r\n // This prevents a separator being inserted before the first\r\n // digit (e.g. \",1,234\" would be wrong).\r\n //\r\n // (?= – POSITIVE LOOKAHEAD — zero-width assertion.\r\n // \"The following characters must match, but don't consume them.\"\r\n // The regex engine peeks ahead without advancing its position.\r\n //\r\n // (\\d{3})+ – one or more groups of EXACTLY THREE digits.\r\n // \\d = any digit\r\n // {3} = exactly 3 of them\r\n // + = one or more such groups (so 3, 6, 9, 12 … digits ahead)\r\n //\r\n // (?!\\d) – NEGATIVE LOOKAHEAD.\r\n // \"What follows the groups of 3 is NOT another digit.\"\r\n // This anchors the groups to the END of the number so the\r\n // separator only lands at multiples of 3 from the RIGHT.\r\n //\r\n // ) – close the positive lookahead\r\n // g – global: insert at every valid position\r\n //\r\n // How the engine works step-by-step on '1234567':\r\n //\r\n // Position between '1' and '2' → lookahead sees '234567' → 6 digits → 6=3×2 ✔ → INSERT\r\n // Position between '2' and '3' → lookahead sees '34567' → 5 digits → not ÷3 ✖\r\n // Position between '3' and '4' → lookahead sees '4567' → 4 digits → not ÷3 ✖\r\n // Position between '4' and '5' → lookahead sees '567' → 3 digits → 3=3×1 ✔ → INSERT\r\n // … and so on\r\n // Result → '1,234,567'\r\n //\r\n // Works with ANY separator — just change the replacement string:\r\n // '1234567'.replace(REGEX.THOUSANDS_SEPARATOR_POSITIONS, ',') → '1,234,567'\r\n // '1234567'.replace(REGEX.THOUSANDS_SEPARATOR_POSITIONS, '.') → '1.234.567'\r\n // '1234567'.replace(REGEX.THOUSANDS_SEPARATOR_POSITIONS, ' ') → '1 234 567'\r\n // '1234567'.replace(REGEX.THOUSANDS_SEPARATOR_POSITIONS, '_') → '1_234_567'\r\n //\r\n // More examples:\r\n // '1234' → '1,234'\r\n // '100' → '100' (fewer than 4 digits — no separator)\r\n // '1000000' → '1,000,000'\r\n //\r\n THOUSANDS_SEPARATOR_POSITIONS: /\\B(?=(\\d{3})+(?!\\d))/g,\r\n\r\n // ──────────────────────────────────────────────────────────────────────────\r\n // 9. REGEX_SPECIAL_CHARACTERS\r\n // Escape any character that has a special meaning inside a RegExp pattern.\r\n // Used before passing a user-supplied string into new RegExp(…).\r\n // ──────────────────────────────────────────────────────────────────────────\r\n //\r\n // Why this is needed:\r\n // The thousandSeparator input can be '.', which inside a regex means\r\n // \"any character\", not a literal dot. Before building\r\n // new RegExp(thousandSeparator, 'g') we must escape it to '\\.'\r\n // so it matches only a literal period.\r\n //\r\n // The replacement string '\\\\$&' means:\r\n // \\\\ – insert a literal backslash\r\n // $& – followed by the matched character itself\r\n // So '.' becomes '\\.' and '(' becomes '\\(' etc.\r\n //\r\n // Pattern breakdown (character class contents):\r\n // . – dot\r\n // * – asterisk\r\n // + – plus\r\n // ? – question mark\r\n // ^ – caret\r\n // $ – dollar sign\r\n // { } – curly braces\r\n // ( ) – parentheses\r\n // | – pipe\r\n // [ – open bracket\r\n // \\] – close bracket (must be escaped to close the class correctly)\r\n // \\\\ – backslash (represents a single \\)\r\n // g – global\r\n //\r\n // Used in: _stripDecoration(), _stripPatternSeparators()\r\n // Anywhere a dynamic string is inserted into new RegExp(…)\r\n //\r\n // Examples:\r\n // '.'.replace(REGEX.REGEX_SPECIAL_CHARACTERS, '\\\\$&') → '\\\\.'\r\n // ','.replace(REGEX.REGEX_SPECIAL_CHARACTERS, '\\\\$&') → ',' (no change)\r\n // '('.replace(REGEX.REGEX_SPECIAL_CHARACTERS, '\\\\$&') → '\\\\('\r\n // ' '.replace(REGEX.REGEX_SPECIAL_CHARACTERS, '\\\\$&') → ' ' (no change)\r\n //\r\n REGEX_SPECIAL_CHARACTERS: /[.*+?^${}()|[\\]\\\\]/g,\r\n\r\n // ──────────────────────────────────────────────────────────────────────────\r\n // 10. LEADING_ZEROS\r\n // Remove leading zeros from the integer part, keeping at least one digit.\r\n // ──────────────────────────────────────────────────────────────────────────\r\n //\r\n // Pattern breakdown:\r\n // ^ – anchor at the start of the string\r\n // 0+ – one or more '0' characters (the leading zeros to remove)\r\n // ( – open capture group 1\r\n // \\d – one digit (the first SIGNIFICANT digit after the zeros)\r\n // ) – close capture group 1\r\n //\r\n // No 'g' flag — we only care about the beginning of the string.\r\n //\r\n // Replacement '$1':\r\n // $1 = capture group 1 = the first significant digit.\r\n // The entire match (zeros + that digit) is replaced by just that digit.\r\n // Any digits AFTER it in the string are left untouched.\r\n //\r\n // Used in: _stripLeadingZeros()\r\n //\r\n // Examples:\r\n // '007'.replace(REGEX.LEADING_ZEROS, '$1') → '7'\r\n // '0042'.replace(REGEX.LEADING_ZEROS, '$1') → '42'\r\n // '0.5'.replace(REGEX.LEADING_ZEROS, '$1') → '0.5' (no match — char after 0 is '.')\r\n // '100'.replace(REGEX.LEADING_ZEROS, '$1') → '100' (no match — first char is '1')\r\n // '0'.replace(REGEX.LEADING_ZEROS, '$1') → '0' (no match — no digit after the 0)\r\n //\r\n LEADING_ZEROS: /^0+(\\d)/,\r\n\r\n // ──────────────────────────────────────────────────────────────────────────\r\n // 11. ARABIC_DIGITS\r\n // Match Arabic-Indic digit characters (used across Middle East & South Asia).\r\n // ──────────────────────────────────────────────────────────────────────────\r\n //\r\n // Unicode range:\r\n // \\u0660 = Arabic-Indic digit zero ٠\r\n // \\u0669 = Arabic-Indic digit nine ٩\r\n // [\\u0660-\\u0669] matches any of the 10 Arabic-Indic digit characters.\r\n //\r\n // g flag: replace ALL occurrences in one pass.\r\n //\r\n // Used in: convertEasternDigits() in eastern-numerals.ts\r\n //\r\n // Background:\r\n // Arabic keyboards and some IMEs output these instead of 0–9.\r\n // Unicode offset from Western digit: charCode - 0x0660 + 48 ('0')\r\n // e.g. '٥'.charCodeAt(0) = 0x0665 → 0x0665 - 0x0660 = 5 → '5'\r\n //\r\n // Examples:\r\n // '١٢٣'.replace(REGEX.ARABIC_DIGITS, …) → '123'\r\n // '٠٩'.replace(REGEX.ARABIC_DIGITS, …) → '09'\r\n //\r\n ARABIC_DIGITS: /[\\u0660-\\u0669]/g,\r\n\r\n // ──────────────────────────────────────────────────────────────────────────\r\n // 12. PERSIAN_DIGITS\r\n // Match Extended Arabic-Indic digit characters used in Persian / Farsi.\r\n // ──────────────────────────────────────────────────────────────────────────\r\n //\r\n // Unicode range:\r\n // \\u06F0 = Extended Arabic-Indic digit zero ۰\r\n // \\u06F9 = Extended Arabic-Indic digit nine ۹\r\n //\r\n // Persian digits are a DIFFERENT Unicode block from Arabic digits even though\r\n // they look similar:\r\n // Arabic zero ٠ = U+0660\r\n // Persian zero ۰ = U+06F0 ← different code point!\r\n //\r\n // Both blocks need separate patterns.\r\n //\r\n // g flag: replace ALL occurrences.\r\n //\r\n // Used in: convertEasternDigits() in eastern-numerals.ts\r\n //\r\n // Examples:\r\n // '۱۲۳'.replace(REGEX.PERSIAN_DIGITS, …) → '123'\r\n // '۰۹'.replace(REGEX.PERSIAN_DIGITS, …) → '09'\r\n //\r\n PERSIAN_DIGITS: /[\\u06F0-\\u06F9]/g,\r\n\r\n // ──────────────────────────────────────────────────────────────────────────\r\n // 13. ARABIC_DECIMAL_COMMA\r\n // Match the Arabic decimal comma character ٫ (U+066B).\r\n // ──────────────────────────────────────────────────────────────────────────\r\n //\r\n // In Arabic locale, the decimal separator is ٫ (Arabic Decimal Separator)\r\n // not the Western period '.'. We convert it to '.' so the rest of the\r\n // directive can process it uniformly.\r\n //\r\n // Used in: convertEasternDigits() in eastern-numerals.ts\r\n //\r\n // Example:\r\n // '١٢٫٥'.replace(REGEX.ARABIC_DECIMAL_COMMA, '.') → '١٢.٥'\r\n // (then after digit conversion) → '12.5'\r\n //\r\n ARABIC_DECIMAL_COMMA: /\\u066B/g,\r\n\r\n // ──────────────────────────────────────────────────────────────────────────\r\n // 14. ARABIC_THOUSANDS_SEPARATOR\r\n // Match the Arabic thousands separator ٬ (U+066C).\r\n // ──────────────────────────────────────────────────────────────────────────\r\n //\r\n // Similar to the decimal comma above: the Arabic locale uses U+066C as its\r\n // thousands grouping character. We strip it so the directive does not\r\n // mistake it for a digit.\r\n //\r\n // Used in: convertEasternDigits() in eastern-numerals.ts\r\n //\r\n // Example:\r\n // '١٬٢٣٤'.replace(REGEX.ARABIC_THOUSANDS_SEPARATOR, '') → '١٢٣٤'\r\n //\r\n ARABIC_THOUSANDS_SEPARATOR: /\\u066C/g,\r\n\r\n // ──────────────────────────────────────────────────────────────────────────\r\n // 15. ARABIC_DIGIT_RANGE\r\n // The Unicode code-point boundaries for Arabic-Indic digit characters.\r\n // ──────────────────────────────────────────────────────────────────────────\r\n //\r\n // Unlike ARABIC_DIGITS (which is a /g regex used with .replace()), these\r\n // are plain string constants used for a boundary COMPARISON in onKeyDown():\r\n //\r\n // pressedKey >= REGEX.ARABIC_DIGIT_RANGE.FIRST &&\r\n // pressedKey <= REGEX.ARABIC_DIGIT_RANGE.LAST\r\n //\r\n // Why not reuse ARABIC_DIGITS here?\r\n // ARABIC_DIGITS has the global 'g' flag and is designed for .replace()\r\n // across an entire string. A single-character range check via >= / <=\r\n // is simpler, has no flag state to worry about, and is O(1).\r\n // JavaScript compares strings by Unicode code point, so this works\r\n // correctly for single characters.\r\n //\r\n // Used in: onKeyDown() — to allow Eastern keystrokes through before\r\n // onInput() converts them to Western digits.\r\n //\r\n // Examples:\r\n // '١' >= REGEX.ARABIC_DIGIT_RANGE.FIRST → true (U+0661 >= U+0660)\r\n // '١' <= REGEX.ARABIC_DIGIT_RANGE.LAST → true (U+0661 <= U+0669)\r\n // 'a' >= REGEX.ARABIC_DIGIT_RANGE.FIRST → false (U+0061 < U+0660)\r\n //\r\n ARABIC_DIGIT_RANGE: {\r\n FIRST: '\\u0660', // ٠ Arabic-Indic digit ZERO\r\n LAST: '\\u0669', // ٩ Arabic-Indic digit NINE\r\n },\r\n\r\n // ──────────────────────────────────────────────────────────────────────────\r\n // 16. PERSIAN_DIGIT_RANGE\r\n // The Unicode code-point boundaries for Persian digit characters.\r\n // ──────────────────────────────────────────────────────────────────────────\r\n //\r\n // Same idea as ARABIC_DIGIT_RANGE above, but for the Persian/Extended\r\n // Arabic-Indic block (U+06F0–U+06F9).\r\n //\r\n // Persian digits live in a DIFFERENT Unicode block from Arabic digits\r\n // even though they look similar:\r\n // Arabic zero ٠ = U+0660\r\n // Persian zero ۰ = U+06F0 ← different block, needs its own range\r\n //\r\n // Used in: onKeyDown() — same purpose as ARABIC_DIGIT_RANGE.\r\n //\r\n // Examples:\r\n // '۶' >= REGEX.PERSIAN_DIGIT_RANGE.FIRST → true (U+06F6 >= U+06F0)\r\n // '۶' <= REGEX.PERSIAN_DIGIT_RANGE.LAST → true (U+06F6 <= U+06F9)\r\n // '١' >= REGEX.PERSIAN_DIGIT_RANGE.FIRST → false (U+0661 < U+06F0)\r\n //\r\n PERSIAN_DIGIT_RANGE: {\r\n FIRST: '\\u06F0', // ۰ Persian digit ZERO\r\n LAST: '\\u06F9', // ۹ Persian digit NINE\r\n },\r\n\r\n} as const;\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// QUICK-REFERENCE TABLE\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n//\r\n// Key Pattern Purpose\r\n// ────────────────────────── ───────────────────────────── ────────────────────────────────────────\r\n// SINGLE_DIGIT /^\\d$/ Is a keypress exactly one digit?\r\n// NON_DIGIT_CHARACTERS /[^0-9]/g Strip everything except digits\r\n// ONLY_DIGITS_AND_MINUS /[^0-9\\-]/g Keep digits + minus (integer paste)\r\n// ONLY_DIGITS_DOT_AND_MINUS /[^0-9.\\-]/g Keep digits + dot + minus (decimal paste)\r\n// ONLY_DIGITS /[^0-9]/g Keep digits only (string/pattern paste)\r\n// ALL_DOTS /\\./g Remove all literal dot characters\r\n// ALL_MINUS_SIGNS /-/g Remove all minus/hyphen characters\r\n// THOUSANDS_SEPARATOR_POSITIONS /\\B(?=(\\d{3})+(?!\\d))/g Find positions for thousands separator\r\n// REGEX_SPECIAL_CHARACTERS /[.*+?^${}()|[\\]\\\\]/g Escape chars before new RegExp(…)\r\n// LEADING_ZEROS /^0+(\\d)/ Strip leading zeros, keep ≥ 1 digit\r\n// ARABIC_DIGITS /[\\u0660-\\u0669]/g Match Arabic-Indic digits ٠-٩\r\n// PERSIAN_DIGITS /[\\u06F0-\\u06F9]/g Match Persian digits ۰-۹\r\n// ARABIC_DECIMAL_COMMA /\\u066B/g Arabic decimal separator ٫ → '.'\r\n// ARABIC_THOUSANDS_SEPARATOR /\\u066C/g Arabic grouping separator ٬ → strip\r\n// ARABIC_DIGIT_RANGE { FIRST: '\\u0660', LAST: '\\u0669' } Boundary check for Arabic digits in keydown\r\n// PERSIAN_DIGIT_RANGE { FIRST: '\\u06F0', LAST: '\\u06F9' } Boundary check for Persian digits in keydown\r\n","/**\r\n * eastern-numerals.ts\r\n *\r\n * A fully standalone, zero-dependency TypeScript utility that converts\r\n * Arabic-Indic and Persian/Farsi digit characters to their Western (ASCII)\r\n * equivalents.\r\n *\r\n * ─── Zero dependencies ────────────────────────────────────────────────────────\r\n *\r\n * This file has NO imports. Copy it into ANY TypeScript or JavaScript project\r\n * and use it immediately — Angular, React, Vue, Node.js, plain TS, or a browser\r\n * script. Nothing else is required.\r\n *\r\n * ─── Why this exists ──────────────────────────────────────────────────────────\r\n *\r\n * Arabic and Persian keyboards — and many mobile IMEs across the Middle East\r\n * and South Asia — produce digit characters from a DIFFERENT Unicode block\r\n * than the ASCII digits 0–9 most Western software expects:\r\n *\r\n * Western / ASCII 0 1 2 3 4 5 6 7 8 9\r\n * Code points U+0030 ── U+0039\r\n *\r\n * Arabic-Indic ٠ ١ ٢ ٣ ٤ ٥ ٦ ٧ ٨ ٩\r\n * Code points U+0660 ── U+0669\r\n *\r\n * Persian / Farsi ۰ ۱ ۲ ۳ ۴ ۵ ۶ ۷ ۸ ۹\r\n * Code points U+06F0 ── U+06F9\r\n *\r\n * When a user types ١٢٣ on an Arabic keyboard the browser sends those exact\r\n * Unicode code points, NOT '123'. Without conversion:\r\n * • /^\\d$/ does NOT match them (it only matches U+0030–U+0039)\r\n * • Number('١٢٣') returns NaN\r\n * • parseInt('١٢٣', 10) returns NaN\r\n *\r\n * ─── Safety guarantee ─────────────────────────────────────────────────────────\r\n *\r\n * This conversion is completely conflict-free because the Arabic and Persian\r\n * digit Unicode blocks have ZERO overlap with:\r\n * • ASCII digits (U+0030–U+0039)\r\n * • ASCII letters (U+0041–U+007A)\r\n * • ASCII punctuation (. , - + $ € % @ # etc.)\r\n *\r\n * Converting ٥ → '5' can NEVER accidentally transform a separator, currency\r\n * symbol, or letter into something else. The substitution is exact and\r\n * unambiguous.\r\n *\r\n * ─── What is NOT converted ────────────────────────────────────────────────────\r\n *\r\n * • Arabic letters ا ب ت ث … — no Western equivalent, left as-is\r\n * • Arabic diacritics (harakat) — no Western equivalent, left as-is\r\n * • Any character outside the two digit blocks described above\r\n *\r\n * ─── Exports ──────────────────────────────────────────────────────────────────\r\n *\r\n * convertEasternDigits(input) Main conversion — Eastern digits → 0-9\r\n * hasEasternDigits(input) Quick check — does the string contain any?\r\n * getDigitScript(input) Identify which script the digits are in\r\n * EASTERN_DIGIT_MAP Full lookup table (for testing / docs)\r\n * EASTERN_NUMERAL_REGEX The two RegExp patterns (for advanced use)\r\n *\r\n * ─── Quick usage ──────────────────────────────────────────────────────────────\r\n *\r\n * import { convertEasternDigits } from './eastern-numerals';\r\n *\r\n * convertEasternDigits('١٢٣٫٤٥') // '123.45'\r\n * convertEasternDigits('۶۷۸') // '678'\r\n * convertEasternDigits('price: ١٠٠') // 'price: 100' (non-digits untouched)\r\n * convertEasternDigits('123') // '123' (already Western)\r\n * convertEasternDigits(null) // ''\r\n */\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// Internal regex constants\r\n// (Inline here so the file has zero dependencies and can be used anywhere.)\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Regex patterns used internally by this module.\r\n * Also exported so callers can use them directly if needed.\r\n *\r\n * These are the same patterns documented in digits-only.regex.ts entries\r\n * ARABIC_DIGITS, PERSIAN_DIGITS, ARABIC_DECIMAL_COMMA, ARABIC_THOUSANDS_SEPARATOR\r\n * but duplicated here so this file remains self-contained.\r\n */\r\nexport const EASTERN_NUMERAL_REGEX = {\r\n\r\n /**\r\n * Matches any Arabic-Indic digit character (U+0660 – U+0669).\r\n * These are the digits used on Arabic keyboards: ٠١٢٣٤٥٦٧٨٩\r\n * The g flag ensures ALL occurrences in a string are replaced, not just the first.\r\n */\r\n ARABIC_DIGITS: /[\\u0660-\\u0669]/g,\r\n\r\n /**\r\n * Matches any Persian / Extended Arabic-Indic digit (U+06F0 – U+06F9).\r\n * These are the digits used on Persian / Farsi keyboards: ۰۱۲۳۴۵۶۷۸۹\r\n * Note: a DIFFERENT Unicode block from Arabic even though they look similar.\r\n * Arabic zero ٠ = U+0660\r\n * Persian zero ۰ = U+06F0 ← different!\r\n */\r\n PERSIAN_DIGITS: /[\\u06F0-\\u06F9]/g,\r\n\r\n /**\r\n * Matches the Arabic Decimal Separator ٫ (U+066B).\r\n * In Arabic locale this character plays the role of the Western period '.'\r\n * as a decimal point. We normalize it to '.' so parseFloat / Number work.\r\n */\r\n ARABIC_DECIMAL_COMMA: /\\u066B/g,\r\n\r\n /**\r\n * Matches the Arabic Thousands Separator ٬ (U+066C).\r\n * In Arabic locale this character plays the role of the Western comma ','\r\n * as a digit-group separator. We strip it so it doesn't pollute the number.\r\n */\r\n ARABIC_THOUSANDS_SEPARATOR: /\\u066C/g,\r\n\r\n} as const;\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// Full Unicode character map (exported for testing and documentation)\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Every Eastern digit character mapped to its Western ASCII equivalent.\r\n *\r\n * Key = Eastern character (as it arrives from the keyboard / clipboard)\r\n * Value = Western ASCII character it should become\r\n *\r\n * This map is exported for:\r\n * • Unit tests that want to iterate over every case\r\n * • Documentation / debugging\r\n * • Manual lookups without running a conversion\r\n */\r\nexport const EASTERN_DIGIT_MAP: Readonly<Record<string, string>> = {\r\n\r\n // ── Arabic-Indic digits (U+0660 – U+0669) ────────────────────────────────\r\n // Used in: Arabic, Urdu, Sindhi, and other Arabic-script languages\r\n '٠': '0', // U+0660 Arabic-Indic digit zero\r\n '١': '1', // U+0661 Arabic-Indic digit one\r\n '٢': '2', // U+0662 Arabic-Indic digit two\r\n '٣': '3', // U+0663 Arabic-Indic digit three\r\n '٤': '4', // U+0664 Arabic-Indic digit four\r\n '٥': '5', // U+0665 Arabic-Indic digit five\r\n '٦': '6', // U+0666 Arabic-Indic digit six\r\n '٧': '7', // U+0667 Arabic-Indic digit seven\r\n '٨': '8', // U+0668 Arabic-Indic digit eight\r\n '٩': '9', // U+0669 Arabic-Indic digit nine\r\n\r\n // ── Persian / Extended Arabic-Indic digits (U+06F0 – U+06F9) ─────────────\r\n // Used in: Persian (Farsi), Pashto, Kurdish, and related languages\r\n '۰': '0', // U+06F0 Extended Arabic-Indic digit zero\r\n '۱': '1', // U+06F1 Extended Arabic-Indic digit one\r\n '۲': '2', // U+06F2 Extended Arabic-Indic digit two\r\n '۳': '3', // U+06F3 Extended Arabic-Indic digit three\r\n '۴': '4', // U+06F4 Extended Arabic-Indic digit four\r\n '۵': '5', // U+06F5 Extended Arabic-Indic digit five\r\n '۶': '6', // U+06F6 Extended Arabic-Indic digit six\r\n '۷': '7', // U+06F7 Extended Arabic-Indic digit seven\r\n '۸': '8', // U+06F8 Extended Arabic-Indic digit eight\r\n '۹': '9', // U+06F9 Extended Arabic-Indic digit nine\r\n\r\n // ── Arabic punctuation normalized as part of number handling ─────────────\r\n '٫': '.', // U+066B Arabic Decimal Separator → Western decimal point\r\n '٬': '', // U+066C Arabic Thousands Separator → stripped (empty string)\r\n\r\n} as const;\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// Core conversion function\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Convert all Arabic-Indic and Persian digit characters in a string\r\n * to their Western ASCII equivalents (0–9).\r\n *\r\n * The function runs four passes in order:\r\n * 1. Strip Arabic thousands separator ٬ (U+066C)\r\n * 2. Convert Arabic decimal comma ٫ (U+066B) → '.'\r\n * 3. Convert Arabic-Indic digits ٠-٩ (U+0660–U+0669) → '0'-'9'\r\n * 4. Convert Persian digits ۰-۹ (U+06F0–U+06F9) → '0'-'9'\r\n *\r\n * The function is PURE — it never mutates its input and has no side effects.\r\n *\r\n * @param input Any string, number, null, or undefined.\r\n * Numbers are converted via String() first.\r\n * null / undefined / '' all return ''.\r\n * @returns A new string with every Eastern digit replaced by its Western\r\n * equivalent. Non-digit characters are passed through unchanged.\r\n *\r\n * @example\r\n * convertEasternDigits('١٢٣') // '123'\r\n * convertEasternDigits('۶۷۸') // '678'\r\n * convertEasternDigits('١٢٣٫٤٥') // '123.45' (decimal comma)\r\n * convertEasternDigits('١٬٢٣٤') // '1234' (thousands sep stripped)\r\n * convertEasternDigits('price: ٩٩') // 'price: 99' (non-digits untouched)\r\n * convertEasternDigits('123') // '123' (already Western)\r\n * convertEasternDigits('') // ''\r\n * convertEasternDigits(null) // ''\r\n * convertEasternDigits(undefined) // ''\r\n * convertEasternDigits(42) // '42' (number input)\r\n */\r\nexport function convertEasternDigits(\r\n input: string | number | null | undefined,\r\n): string {\r\n\r\n // ── Guard ─────────────────────────────────────────────────────────────────\r\n // Handle null, undefined, and empty-string early so the rest of the\r\n // function can assume it has a non-empty string to work with.\r\n if (input === null || input === undefined || input === '') {\r\n return '';\r\n }\r\n\r\n // Coerce numbers to strings so the function also works when called\r\n // directly on a numeric value (e.g. after a model value update).\r\n const text = typeof input === 'number' ? String(input) : input;\r\n\r\n return text\r\n\r\n // ── Step 1: Remove Arabic thousands separator ──────────────────────────\r\n //\r\n // ٬ (U+066C) looks like a comma but is a completely different code point.\r\n // It must be stripped FIRST — before digit conversion — so it cannot\r\n // accidentally become part of the number string.\r\n //\r\n // '١٬٢٣٤' → '١٢٣٤' (grouping character removed)\r\n .replace(EASTERN_NUMERAL_REGEX.ARABIC_THOUSANDS_SEPARATOR, '')\r\n\r\n // ── Step 2: Arabic decimal comma → Western decimal point ──────────────\r\n //\r\n // ٫ (U+066B) is the decimal separator in Arabic locale.\r\n // We convert it to '.' so parseFloat / Number can parse the result.\r\n //\r\n // '١٢٫٥' → '١٢.٥' (decimal comma replaced, digits still Arabic)\r\n .replace(EASTERN_NUMERAL_REGEX.ARABIC_DECIMAL_COMMA, '.')\r\n\r\n // ── Step 3: Arabic-Indic digits → Western digits ───────────────────────\r\n //\r\n // The conversion formula uses the Unicode offset of the Arabic zero (0x0660):\r\n //\r\n // Example with '٥':\r\n // '٥'.charCodeAt(0) = 0x0665 (1637 decimal)\r\n // 0x0665 - 0x0660 = 5 (the digit's numeric value)\r\n // 5 + 0x30 = 53 (ASCII code of '5')\r\n // String.fromCharCode(53) = '5'\r\n //\r\n // '٠١٢٣٤٥٦٧٨٩' → '0123456789'\r\n .replace(EASTERN_NUMERAL_REGEX.ARABIC_DIGITS, (arabicChar: string) => {\r\n const ARABIC_ZERO_CODE_POINT = 0x0660; // Unicode code point of Arabic ٠\r\n const WESTERN_ZERO_CODE_POINT = 0x0030; // Unicode code point of ASCII 0\r\n\r\n const digitValue = arabicChar.charCodeAt(0) - ARABIC_ZERO_CODE_POINT;\r\n return String.fromCharCode(digitValue + WESTERN_ZERO_CODE_POINT);\r\n })\r\n\r\n // ── Step 4: Persian digits → Western digits ────────────────────────────\r\n //\r\n // Identical math to step 3 but using the Persian zero offset (0x06F0).\r\n // Persian and Arabic digits look similar but are different code points —\r\n // both blocks need their own replacement pass.\r\n //\r\n // '۰۱۲۳۴۵۶۷۸۹' → '0123456789'\r\n .replace(EASTERN_NUMERAL_REGEX.PERSIAN_DIGITS, (persianChar: string) => {\r\n const PERSIAN_ZERO_CODE_POINT = 0x06F0; // Unicode code point of Persian ۰\r\n const WESTERN_ZERO_CODE_POINT = 0x0030; // Unicode code point of ASCII 0\r\n\r\n const digitValue = persianChar.charCodeAt(0) - PERSIAN_ZERO_CODE_POINT;\r\n return String.fromCharCode(digitValue + WESTERN_ZERO_CODE_POINT);\r\n });\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// Helper utilities\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Returns true if the string contains at least one Arabic-Indic or\r\n * Persian digit character.\r\n *\r\n * Useful as a cheap pre-check before calling convertEasternDigits — if this\r\n * returns false the string is already all-Western and no conversion is needed.\r\n *\r\n * @param input Any string to check.\r\n * @returns true if any Eastern digit is present, false otherwise.\r\n *\r\n * @example\r\n * hasEasternDigits('١٢٣') // true — Arabic\r\n * hasEasternDigits('۴۵۶') // true — Persian\r\n * hasEasternDigits('٥0') // true — mix of Arabic and Western\r\n * hasEasternDigits('123') // false — already Western\r\n * hasEasternDigits('hello') // false — no digits at all\r\n * hasEasternDigits('') // false\r\n */\r\nexport function hasEasternDigits(input: string): boolean {\r\n if (!input) return false;\r\n\r\n // We must reset lastIndex before using a /g regex with .test()\r\n // because stateful regex objects remember where they left off.\r\n // The simplest safe approach is to re-create a clean regex each time.\r\n return /[\\u0660-\\u0669\\u06F0-\\u06F9]/.test(input);\r\n}\r\n\r\n/**\r\n * Identify which numeral script(s) the string's digits are written in.\r\n * Returns one of four values so callers can branch on the exact script\r\n * rather than just knowing \"something eastern is present\".\r\n *\r\n * @param input Any string to inspect.\r\n * @returns\r\n * 'western' — only ASCII digits 0-9 found (or no digits at all)\r\n * 'arabic' — only Arabic-Indic digits ٠-٩ found\r\n * 'persian' — only Persian digits ۰-۹ found\r\n * 'mixed' — both Eastern script(s) AND/OR Western digits found together\r\n *\r\n * @example\r\n * getDigitScript('١٢٣') // 'arabic'\r\n * getDigitScript('۴۵۶') // 'persian'\r\n * getDigitScript('123') // 'western'\r\n * getDigitScript('١23') // 'mixed' (Arabic + Western)\r\n * getDigitScript('١۴') // 'mixed' (Arabic + Persian)\r\n * getDigitScript('hello') // 'western' (no digits → treated as Western)\r\n */\r\nexport type DigitScript = 'western' | 'arabic' | 'persian' | 'mixed';\r\n\r\nexport function getDigitScript(input: string): DigitScript {\r\n if (!input) return 'western';\r\n\r\n const containsArabic = /[\\u0660-\\u0669]/.test(input);\r\n const containsPersian = /[\\u06F0-\\u06F9]/.test(input);\r\n const containsWestern = /[0-9]/.test(input);\r\n\r\n // Mixed: more than one script present simultaneously\r\n const scriptCount = [containsArabic, containsPersian, containsWestern]\r\n .filter(Boolean).length;\r\n\r\n if (scriptCount > 1) return 'mixed';\r\n if (containsArabic) return 'arabic';\r\n if (containsPersian) return 'persian';\r\n return 'western';\r\n}\r\n\r\n/**\r\n * Convert a string that may contain Eastern digits to a JavaScript number.\r\n * A convenience wrapper around convertEasternDigits + parseFloat.\r\n *\r\n * Returns NaN for strings that don't represent a valid number\r\n * (same contract as parseFloat / Number).\r\n *\r\n * @param input A string possibly containing Eastern digits.\r\n * @returns A JavaScript number, or NaN if parsing fails.\r\n *\r\n * @example\r\n * toWesternNumber('١٢٣') // 123\r\n * toWesternNumber('١٢٣٫٤٥') // 123.45\r\n * toWesternNumber('۹٩') // 99 (mixed Arabic + Persian → still works)\r\n * toWesternNumber('abc') // NaN\r\n * toWesternNumber('') // NaN\r\n */\r\nexport function toWesternNumber(input: string | null | undefined): number {\r\n const converted = convertEasternDigits(input);\r\n return parseFloat(converted);\r\n}\r\n","/**\r\n * digits-only.directive.ts\r\n *\r\n * A self-contained Angular directive that replicates the key numeric features\r\n * of ngx-mask without any external dependency.\r\n *\r\n * ─── Files in this module ─────────────────────────────────────────────────────\r\n *\r\n * digits-only.directive.ts ← you are here — the Angular directive\r\n * digits-only.regex.ts ← every RegExp constant with detailed explanations\r\n * digits-only.module.ts ← NgModule that declares and exports the directive\r\n * eastern-numerals.ts ← standalone Arabic / Persian → Western digit converter\r\n *\r\n * ─── Features ─────────────────────────────────────────────────────────────────\r\n *\r\n * [digitsOnly] Selector — add to any <input> element\r\n * [decimalPlaces] Number of decimal places allowed (default: 0 = integers)\r\n * [thousandSeparator] Visual grouping character: ',' | '.' | ' ' | '_' | ''\r\n * [prefix] Visual prefix shown in input (e.g. '$') — stripped from model\r\n * [suffix] Visual suffix shown in input (e.g. '%') — stripped from model\r\n * [allowNegative] Allow a leading minus sign (default: false)\r\n * [leadingZeros] Preserve leading zeros like '007' (default: false)\r\n * [maxLength] Maximum number of raw digits allowed\r\n * [min] Minimum numeric value (validation, number mode only)\r\n * [max] Maximum numeric value (validation, number mode only)\r\n * [outputType] 'number' (default) | 'string' — type emitted to the model\r\n * [pattern] Display-format mask: '0000 0000 0000 0000' or named alias\r\n * [convertEasternNumerals] Convert Arabic/Persian digits to Western on input (default: true)\r\n *\r\n * ─── outputType ───────────────────────────────────────────────────────────────\r\n *\r\n * 'number' (default)\r\n * Model receives: number | null\r\n * Use for: prices, quantities, temperatures — values used in arithmetic\r\n * Note: Leading zeros are lost (007 → 7); values beyond\r\n * Number.MAX_SAFE_INTEGER may lose precision\r\n *\r\n * 'string'\r\n * Model receives: string | null\r\n * Use for: card numbers, phone numbers, postal codes, SSNs, OTPs —\r\n * digit strings that are identifiers, not quantities\r\n * Note: Leading zeros are always preserved; no precision limit\r\n *\r\n * ─── pattern ──────────────────────────────────────────────────────────────────\r\n *\r\n * A mask string made of '0' (digit slot) and any other character (separator).\r\n * Separators are inserted in the display automatically but NEVER reach the model.\r\n * Setting a pattern forces outputType='string' and derives maxLength automatically.\r\n *\r\n * Named aliases:\r\n * 'card' → '0000 0000 0000 0000' Visa / Mastercard (16 digits)\r\n *\r\n * ─── convertEasternNumerals ───────────────────────────────────────────────────\r\n *\r\n * When true (default), Arabic-Indic (٠١٢٣٤٥٦٧٨٩) and Persian (۰۱۲۳۴۵۶۷۸۹)\r\n * digit characters are silently converted to their Western equivalents before\r\n * any processing. This allows users on Arabic / Persian keyboards to type\r\n * naturally into any digitsOnly field.\r\n * Set to false only if you specifically need to block Eastern numerals.\r\n *\r\n * ─── Usage examples ───────────────────────────────────────────────────────────\r\n *\r\n * <!-- Integer amount, $ prefix, comma thousands -->\r\n * <input digitsOnly thousandSeparator=\",\" prefix=\"$\" formControlName=\"salary\" />\r\n *\r\n * <!-- Decimal price, 2 places -->\r\n * <input digitsOnly [decimalPlaces]=\"2\" thousandSeparator=\",\" formControlName=\"price\" />\r\n *\r\n * <!-- Card number with automatic space grouping -->\r\n * <input digitsOnly pattern=\"card\" formControlName=\"cardNumber\" />\r\n *\r\n * <!-- Custom pattern -->\r\n * <input digitsOnly pattern=\"000-00-0000\" formControlName=\"ssn\" />\r\n *\r\n * <!-- Opt out of Eastern numeral conversion -->\r\n * <input digitsOnly [convertEasternNumerals]=\"false\" formControlName=\"code\" />\r\n */\r\n\r\nimport {\r\n Directive,\r\n ElementRef,\r\n HostListener,\r\n Inject,\r\n Input,\r\n OnChanges,\r\n OnInit,\r\n Renderer2,\r\n SimpleChanges,\r\n forwardRef,\r\n} from '@angular/core';\r\nimport { DOCUMENT } from '@angular/common';\r\nimport {\r\n AbstractControl,\r\n ControlValueAccessor,\r\n NG_VALIDATORS,\r\n NG_VALUE_ACCESSOR,\r\n ValidationErrors,\r\n Validator,\r\n} from '@angular/forms';\r\n\r\nimport { REGEX } from './digits-only.regex';\r\nimport { convertEasternDigits } from './eastern-numerals';\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// Unicode directional constants\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Left-to-Right Mark (U+200E) — an invisible zero-width Unicode character.\r\n *\r\n * WHY IT IS NEEDED:\r\n * In RTL documents (dir=\"rtl\"), the browser's Unicode Bidirectional Algorithm\r\n * (UBA) may try to mirror numeric content — reordering digits or misplacing\r\n * separators like commas. For example, \"1,234\" can display as \"234,1\" in\r\n * some bidi contexts.\r\n *\r\n * Prepending LRM to the input value tells the bidi algorithm:\r\n * \"treat everything that follows as left-to-right text\".\r\n * Numbers, thousands separators, and decimal points always read correctly.\r\n *\r\n * ZERO IMPACT ON MODEL:\r\n * LRM is stripped inside stripDecorationsFrom() before any value reaches\r\n * the model, so your component never sees this character.\r\n *\r\n * BROWSER SUPPORT:\r\n * All modern browsers respect U+200E in input values.\r\n */\r\nconst LRM = '\\u200E'; // Left-to-Right Mark — invisible, prevents bidi digit mirroring\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// Named pattern registry + type\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// '0' = digit slot, any other character = literal separator inserted in display.\r\n\r\nexport type NamedPattern = 'card';\r\n\r\n\r\n/**\r\n * Maps every NamedPattern alias to its full mask string.\r\n * Typed as Record<NamedPattern, string> so TypeScript catches missing entries —\r\n * add a new entry here and the type forces you to add it to NamedPattern too.\r\n */\r\nconst NAMED_PATTERNS: Record<NamedPattern, string> = {\r\n 'card': '0000 0000 0000 0000',\r\n};\r\n\r\n/**\r\n * Type guard that narrows an arbitrary string to NamedPattern.\r\n *\r\n * Using the `in` operator against NAMED_PATTERNS tells TypeScript:\r\n * \"if this key exists in the object, treat it as NamedPattern from here on\".\r\n * This is the only way to safely index Record<NamedPattern, string> with a\r\n * value that might be a NamedPattern OR a plain custom pattern string.\r\n *\r\n * @example\r\n * isNamedPattern('card') // true → safe to use as NAMED_PATTERNS key\r\n * isNamedPattern('000-0000') // false → treat as raw custom pattern\r\n */\r\nfunction isNamedPattern(value: string): value is NamedPattern {\r\n return value in NAMED_PATTERNS;\r\n}\r\n\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n// Directive\r\n// ─────────────────────────────────────────────────────────────────────────────\r\n\r\n@Directive({\r\n selector: '[digitsOnly]',\r\n standalone: true,\r\n providers: [\r\n {\r\n provide: NG_VALUE_ACCESSOR,\r\n useExisting: forwardRef(() => DigitsOnlyDirective),\r\n multi: true,\r\n },\r\n {\r\n provide: NG_VALIDATORS,\r\n useExisting: forwardRef(() => DigitsOnlyDirective),\r\n multi: true,\r\n },\r\n ],\r\n})\r\nexport class DigitsOnlyDirective implements ControlValueAccessor, Validator, OnInit, OnChanges {\r\n\r\n // ─── Public inputs ──────────────────────────────────────────────────────────\r\n\r\n /** How many decimal places are allowed. 0 = integers only. Ignored when pattern is set. */\r\n @Input() decimalPlaces = 0;\r\n\r\n /** Visual grouping character inserted between digit groups. Ignored when pattern is set. */\r\n @Input() thousandSeparator: '' | ',' | '.' | ' ' | '_' = '';\r\n\r\n /** Visual text prepended to the displayed value (e.g. '$'). Never reaches the model. */\r\n @Input() prefix = '';\r\n\r\n /** Visual text appended to the displayed value (e.g. '%'). Never reaches the model. */\r\n @Input() suffix = '';\r\n\r\n /** When true, a leading minus '-' is allowed for negative numbers. */\r\n @Input() allowNegative = false;\r\n\r\n /** When true, leading zeros (e.g. '007') are preserved in the model value. */\r\n @Input() leadingZeros = false;\r\n\r\n /** Maximum number of raw digit characters the user may enter. */\r\n @Input() maxLength: number | null = null;\r\n\r\n /** Minimum allowed numeric value. Produces a 'min' validation error when violated. */\r\n @Input() min: number | null = null;\r\n\r\n /** Maximum allowed numeric value. Produces a 'max' validation error when violated. */\r\n @Input() max: number | null = null;\r\n\r\n /**\r\n * Controls the JavaScript type emitted to the form model.\r\n * 'number' (default) → number | null\r\n * 'string' → string | null\r\n * Automatically overridden to 'string' when [pattern] is set.\r\n */\r\n @Input() outputType: 'number' | 'string' = 'number';\r\n\r\n /**\r\n * When true (default), Arabic-Indic and Persian digit characters typed or\r\n * pasted by the user are automatically converted to Western digits 0-9\r\n * before any processing happens.\r\n *\r\n * Safe to leave enabled for all use cases — the conversion only touches\r\n * characters in the Unicode ranges U+0660–U+0669 and U+06F0–U+06F9,\r\n * which have zero overlap with any ASCII character.\r\n */\r\n @Input() convertEasternNumerals = true;\r\n\r\n /**\r\n * Display-format mask string or a named alias (see NamedPattern type).\r\n *\r\n * Typed as NamedPattern | (string & {}) so that:\r\n * • Named aliases are autocompleted in IDEs and validated at compile time.\r\n * • Custom raw patterns like '000-0000' are still accepted without error.\r\n *\r\n * '0' = digit slot, any other char = literal separator auto-inserted in display.\r\n * Setting this forces outputType='string' and derives maxLength from slot count.\r\n *\r\n * @example\r\n * pattern=\"card\" → '0000 0000 0000 0000'\r\n * pattern=\"00/00/0000\" → custom date pattern\r\n */\r\n @Input() set pattern(value: NamedPattern | (string & {})) {\r\n // Type guard: check whether the value is one of the known NamedPattern keys\r\n // before indexing NAMED_PATTERNS with it.\r\n //\r\n // Why this is needed:\r\n // NAMED_PATTERNS is typed as Record<NamedPattern, string>, meaning its\r\n // index signature only accepts NamedPattern keys — not arbitrary strings.\r\n // The @Input type is NamedPattern | (string & {}), so `value` could be\r\n // a custom raw pattern like '000-0000' that is NOT a key of NAMED_PATTERNS.\r\n // Indexing with it directly causes TS error ts(7053):\r\n // \"No index signature with a parameter of type 'string' was found\".\r\n //\r\n // The fix: use `value in NAMED_PATTERNS` as a type guard. The `in` operator\r\n // narrows the type of `value` to NamedPattern inside the if-branch, making\r\n // NAMED_PATTERNS[value] type-safe. In the else-branch `value` is treated\r\n // as a raw custom pattern string and used directly.\r\n if (isNamedPattern(value)) {\r\n this._resolvedPattern = NAMED_PATTERNS[value]; // value is NamedPattern\r\n } else {\r\n this._resolvedPattern = value; // value is a raw custom pattern\r\n }\r\n }\r\n\r\n // ─── Internal state ──────────────────────────────────────────────────────────\r\n\r\n /** Full pattern string after named-alias resolution. Empty string = no pattern. */\r\n private _resolvedPattern = '';\r\n\r\n /**\r\n * The canonical internal value: pure digits (and optional '.' and '-').\r\n * No prefix, suffix, separators, or pattern characters ever live here.\r\n * Every other method reads or writes this field.\r\n */\r\n private _rawValue = '';\r\n\r\n // ControlValueAccessor callbacks registered by Angular forms\r\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\r\n private _onChange: (value: any) => void = () => { };\r\n private _onTouched: () => void = () => { };\r\n private _onValidatorChange: () => void = () => { };\r\n\r\n // ─── Computed properties ────────────────────────────────────────────────────\r\n\r\n /** True when a format pattern has been set on this input. */\r\n private get hasPattern(): boolean {\r\n return this._resolvedPattern.length > 0;\r\n }\r\n\r\n /**\r\n * The set of literal separator characters defined in the current pattern.\r\n *\r\n * Pattern '(000) 000-0000' → separators: {'(', ')', ' ', '-'}\r\n * Pattern '0000 0000' → separators: {' '}\r\n *\r\n * Used when stripping decorations back off a displayed value.\r\n */\r\n private get patternSeparatorChars(): Set<string> {\r\n return new Set(\r\n this._resolvedPattern.split('').filter(char => char !== '0')\r\n );\r\n }\r\n\r\n /**\r\n * The total number of digit slots ('0' characters) in the current pattern.\r\n * 'card' pattern has 16 slots; 'expiry' has 4; etc.\r\n */\r\n private get patternDigitSlotCount(): number {\r\n return this._resolvedPattern.split('').filter(char => char === '0').length;\r\n }\r\n\r\n /**\r\n * The effective maximum digit length to enforce.\r\n * In pattern mode, this is always the slot count (overrides any [maxLength] input).\r\n * In non-pattern mode, it comes from the [maxLength] input (may be null = unlimited).\r\n */\r\n private get effectiveMaxLength(): number | null {\r\n return this.hasPattern ? this.patternDigitSlotCount : this.maxLength;\r\n }\r\n\r\n /**\r\n * The effective output type.\r\n * Pattern mode always emits strings because patterns are for identifiers.\r\n * Otherwise respects the [outputType] input.\r\n */\r\n private get effectiveOutputType(): 'number' | 'string' {\r\n return this.hasPattern ? 'string' : this.outputType;\r\n }\r\n\r\n /**\r\n * The decimal separator character used in the DISPLAY.\r\n * If the thousands separator is '.' (European style), we must use ','\r\n * as the decimal character to avoid ambiguity (1.234,56 is valid; 1,234,56 is not).\r\n * Otherwise we use the standard '.'.\r\n */\r\n private get displayDecimalChar(): string {\r\n return this.thousandSeparator === '.' ? ',' : '.';\r\n }\r\n\r\n /**\r\n * Returns true when the input element (or any of its ancestors) is in\r\n * right-to-left mode.\r\n *\r\n * Detection order:\r\n * 1. The input element's own computed `direction` style — this reflects\r\n * any `dir` attribute on the element itself, a CSS `direction` rule,\r\n * or inheritance from a parent element with dir=\"rtl\".\r\n * 2. Falls back to false (LTR) if the window / getComputedStyle is\r\n * unavailable (e.g. server-side rendering).\r\n *\r\n * This is a getter (not a cached field) so it always reflects the current\r\n * DOM state, even if the direction changes dynamically at runtime.\r\n */\r\n private get isRtl(): boolean {\r\n const win = this.document.defaultView;\r\n if (!win) return false;\r\n const dir = win.getComputedStyle(this.el.nativeElement).direction;\r\n return dir === 'rtl';\r\n }\r\n\r\n /**\r\n * In RTL mode the string stored in the input is prefixed with an LRM mark\r\n * so that the bidi algorithm does not reorder digits or separators.\r\n * This helper returns the prefix length including the LRM when RTL is active.\r\n *\r\n * Used wherever we need to know \"how many characters before the editable\r\n * number portion start\" — cursor clamping, minus-key position check, etc.\r\n */\r\n private get editableStartOffset(): number {\r\n // In RTL: LRM + suffix occupies the left side of the display string.\r\n // In LTR: prefix occupies the left side.\r\n return this.isRtl\r\n ? LRM.length + this.suffix.length // RTL: [LRM][suffix][number][prefix]\r\n : this.prefix.length; // LTR: [prefix][number][suffix]\r\n }\r\n\r\n /**\r\n * The number of characters at the RIGHT end of the display string that\r\n * belong to the suffix (LTR) or prefix (RTL) — i.e. characters the cursor\r\n * must never move past on the right side.\r\n */\r\n private get editableEndPadding(): number {\r\n return this.isRtl ? this.prefix.length : this.suffix.length;\r\n }\r\n\r\n constructor(\r\n private el: ElementRef<HTMLInputElement>,\r\n private renderer: Renderer2,\r\n @Inject(DOCUMENT) private document: Document,\r\n ) { }\r\n\r\n // ─── Angular lifecycle ────────────────────────────────────────────────────────\r\n\r\n ngOnInit(): void {\r\n // Always use numeric inputMode so mobile keyboards show the number pad\r\n // regardless of document direction.\r\n this.renderer.setAttribute(this.el.nativeElement, 'inputmode', 'numeric');\r\n\r\n // Render the initial empty state into the input element.\r\n // refreshDisplay() also sets the dir attribute on first call.\r\n this.refreshDisplay();\r\n }\r\n\r\n ngOnChanges(changes: SimpleChanges): void {\r\n const { prefix, suffix, thousandSeparator, decimalPlaces, outputType, pattern } = changes;\r\n\r\n // Re-render whenever any display-affecting input changes\r\n const displayInputsChanged =\r\n prefix ||\r\n suffix ||\r\n thousandSeparator ||\r\n decimalPlaces ||\r\n outputType ||\r\n pattern;\r\n\r\n if (displayInputsChanged) {\r\n this.refreshDisplay();\r\n }\r\n }\r\n\r\n // ─── ControlValueAccessor ────────────────────────────────────────────────────\r\n // These four methods are the bridge between the directive and Angular forms.\r\n\r\n /** Angular calls this when the form model value changes programmatically. */\r\n writeValue(value: string | number | null): void {\r\n if (value === null || value === undefined || value === '') {\r\n this._rawValue = '';\r\n } else {\r\n const stringValue = String(value);\r\n\r\n if (this.hasPattern) {\r\n // Strip any separators that may already be in the incoming string value\r\n this._rawValue = this.stripPatternSeparatorsFrom(stringValue);\r\n } else {\r\n // Normalize decimal separator to '.' for internal storage\r\n this._rawValue = stringValue.replace(',', '.');\r\n }\r\n }\r\n\r\n this.refreshDisplay();\r\n }\r\n\r\n /** Angular calls this to register the function we must call when our value changes. */\r\n registerOnChange(fn: (v: number | string | null) => void): void {\r\n this._onChange = fn;\r\n }\r\n\r\n /** Angular calls this to register the function we must call when the field is touched. */\r\n registerOnTouched(fn: () => void): void {\r\n this._onTouched = fn;\r\n }\r\n\r\n /** Angular calls this to enable or disable the input. */\r\n setDisabledState(isDisabled: boolean): void {\r\n this.renderer.setProperty(this.el.nativeElement, 'disabled', isDisabled);\r\n }\r\n\r\n // ─── Validator ───────────────────────────────────────────────────────────────\r\n\r\n /** Angular calls this to ask whether the current value has any errors. */\r\n validate(_control: AbstractControl): ValidationErrors | null {\r\n const errors: ValidationErrors = {};\r\n const raw = this._rawValue;\r\n\r\n // Empty or partial-minus input: let [required] handle the \"is it filled?\" check\r\n if (raw === '' || raw === '-') {\r\n return null;\r\n }\r\n\r\n // ── Pattern mode validation ──────────────────────────────────────────────\r\n if (this.hasPattern) {\r\n const filledDigitCount = raw.replace(REGEX.NON_DIGIT_CHARACTERS, '').length;\r\n const requiredDigitCount = this.patternDigitSlotCount;\r\n\r\n // The user must fill ALL slots before the value is considered complete\r\n if (filledDigitCount < requiredDigitCount) {\r\n errors['patternIncomplete'] = {\r\n required: requiredDigitCount,\r\n actual: filledDigitCount,\r\n pattern: this._resolvedPattern,\r\n };\r\n }\r\n\r\n return Object.keys(errors).length > 0 ? errors : null;\r\n }\r\n\r\n // ── Non-pattern: maxLength check ─────────────────────────────────────────\r\n const maxLen = this.effectiveMaxLength;\r\n if (maxLen !== null) {\r\n const digitCount = raw.replace(REGEX.NON_DIGIT_CHARACTERS, '').length;\r\n if (digitCount > maxLen) {\r\n errors['maxLength'] = { maxLength: maxLen, actual: digitCount };\r\n }\r\n }\r\n\r\n // ── String output: no numeric range validation needed ────────────────────\r\n // Identifiers (card numbers, phones) don't have a meaningful min/max.\r\n if (this.effectiveOutputType === 'string') {\r\n return Object.keys(errors).length > 0 ? errors : null;\r\n }\r\n\r\n // ── Number output: range validation ──────────────────────────────────────\r\n const numericValue = parseFloat(raw);\r\n\r\n if (isNaN(numericValue)) {\r\n errors['digits'] = { actual: raw };\r\n }\r\n\r\n if (this.min !== null && numericValue < this.min) {\r\n errors['min'] = { min: this.min, actual: numericValue };\r\n }\r\n\r\n if (this.max !== null && numericValue > this.max) {\r\n errors['max'] = { max: this.max, actual: numericValue };\r\n }\r\n\r\n return Object.keys(errors).length > 0 ? errors : null;\r\n }\r\n\r\n /** Angular calls this to register the function we call when validation rules change. */\r\n registerOnValidatorChange(fn: () => void): void {\r\n this._onValidatorChange = fn;\r\n }\r\n\r\n // ─── DOM event handlers ────────────────────────────────────────────────────\r\n\r\n /**\r\n * Intercept keydown BEFORE the character lands in the input.\r\n * This is our first line of defense — blocking bad keys entirely\r\n * so the input event never fires for them.\r\n */\r\n @HostListener('keydown', ['$event'])\r\n onKeyDown(event: KeyboardEvent): void {\r\n\r\n // Always pass through control keys (Backspace, arrows, Ctrl+C, etc.)\r\n if (this.isControlKey(event)) {\r\n return;\r\n }\r\n\r\n const pressedKey = event.key;\r\n\r\n // ── Allow minus sign (number mode only, not pattern mode) ─────────────\r\n const isMinusKey = pressedKey === '-';\r\n const minusIsAllowed =\r\n this.allowNegative &&\r\n !this.hasPattern &&\r\n this.effectiveOutputType === 'number';\r\n\r\n // The \"start of the editable number region\" depends on direction:\r\n // LTR: cursor must be at prefix.length (characters the prefix occupies)\r\n // RTL: cursor must be at LRM.length + suffix.length (LRM + suffix on left)\r\n // editableStartOffset encapsulates this logic so onKeyDown stays clean.\r\n const cursorPosition = this.el.nativeElement.selectionStart ?? 0;\r\n const cursorIsAtStart = cursorPosition === this.editableStartOffset;\r\n\r\n const noMinusYet = !this._rawValue.startsWith('-');\r\n\r\n if (isMinusKey && minusIsAllowed && cursorIsAtStart && noMinusYet) {\r\n return; // allow the minus through\r\n }\r\n\r\n // ── Allow decimal point (number mode only, no pattern) ────────────────\r\n const isDecimalKey = pressedKey === '.' || pressedKey === ',';\r\n const decimalIsAllowed = this.decimalPlaces > 0 && !this.hasPattern;\r\n const noDecimalYet = !this._rawValue.includes('.');\r\n\r\n if (isDecimalKey && decimalIsAllowed && noDecimalYet) {\r\n return; // allow the decimal separator through\r\n }\r\n\r\n // ── Allow digit keys (0–9 AND Eastern Arabic/Persian digits) ────────────\r\n //\r\n // IMPORTANT: REGEX.SINGLE_DIGIT only matches ASCII digits /^\\d$/ (U+0030–U+0039).\r\n // Arabic-Indic digits ٠-٩ (U+0660–U+0669) and Persian digits ۰-۹ (U+06F0–U+06F9)\r\n // are NOT matched by \\d, so they would fall through to the \"block everything else\"\r\n // branch and be prevented — even when convertEasternNumerals is true.\r\n //\r\n // The fix: also check for Eastern digit characters here in keydown.\r\n // The actual conversion from Eastern → Western happens later in onInput(),\r\n // so all we need to do here is let the keystroke land in the input.\r\n const isWesternDigit = REGEX.SINGLE_DIGIT.test(pressedKey);\r\n const isArabicDigit = pressedKey >= REGEX.ARABIC_DIGIT_RANGE.FIRST &&\r\n pressedKey <= REGEX.ARABIC_DIGIT_RANGE.LAST;\r\n const isPersianDigit = pressedKey >= REGEX.PERSIAN_DIGIT_RANGE.FIRST &&\r\n pressedKey <= REGEX.PERSIAN_DIGIT_RANGE.LAST;\r\n const isEasternDigit = this.convertEasternNumerals && (isArabicDigit || isPersianDigit);\r\n const isDigitKey = isWesternDigit || isEasternDigit;\r\n\r\n if (isDigitKey) {\r\n // Enforce the digit slot limit BEFORE the character is inserted.\r\n // Count only the raw digits already in the value (exclude separators/prefix/suffix).\r\n const maxLen = this.effectiveMaxLength;\r\n if (maxLen !== null) {\r\n const currentDigitCount = this._rawValue.replace(REGEX.NON_DIGIT_CHARACTERS, '').length;\r\n const selectedChars = Math.abs(\r\n (this.el.nativeElement.selectionEnd ?? 0) -\r\n (this.el.nativeElement.selectionStart ?? 0)\r\n );\r\n const nothingSelected = selectedChars === 0;\r\n\r\n if (nothingSelected && currentDigitCount >= maxLen) {\r\n event.preventDefault(); // already at limit — block the digit\r\n return;\r\n }\r\n }\r\n return; // digit is allowed — Eastern digits will be converted in onInput()\r\n }\r\n\r\n // ── Block everything else ─────────────────────────────────────────────\r\n event.preventDefault();\r\n }\r\n\r\n /**\r\n * Process input AFTER the character has landed in the input element.\r\n * We read the new display value, strip decorations to get raw digits,\r\n * apply rules (decimal limits, maxLength, leading zeros), then re-render.\r\n */\r\n @HostListener('input')\r\n onInput(): void {\r\n const inputEl = this.el.nativeElement;\r\n const cursorPositionBeforeReformat = inputEl.selectionStart ?? 0;\r\n const displayLengthBeforeReformat = inputEl.value.length;\r\n\r\n // Convert Eastern numerals FIRST, before anything else touches the value\r\n if (this.convertEasternNumerals) {\r\n const converted = convertEasternDigits(inputEl.value);\r\n if (converted !== inputEl.value) {\r\n // Write the converted value directly into the DOM so our stripping below\r\n // works on clean ASCII characters\r\n this.renderer.setProperty(inputEl, 'value', converted);\r\n }\r\n }\r\n\r\n // Strip prefix, suffix, and visual separators — extract the raw digit core\r\n let raw = this.stripDecorationsFrom(inputEl.value);\r\n\r\n if (this.hasPattern) {\r\n // Pattern mode: keep only digits, cap at slot count\r\n raw = raw.replace(REGEX.ONLY_DIGITS, '');\r\n raw = raw.slice(0, this.patternDigitSlotCount);\r\n\r\n } else {\r\n // Non-pattern mode: normalize decimal separator\r\n raw = raw.replace(',', '.');\r\n\r\n if (this.decimalPlaces === 0 || this.effectiveOutputType === 'string') {\r\n // Integer or string mode: no decimal points allowed\r\n raw = raw.replace(REGEX.ALL_DOTS, '');\r\n } else {\r\n // Decimal mode: allow exactly one dot with up to N decimal places\r\n raw = this.enforceDecimalRules(raw);\r\n }\r\n\r\n // Strip leading zeros unless explicitly preserved\r\n const shouldStripLeadingZeros =\r\n !this.leadingZeros && this.effectiveOutputType === 'number';\r\n if (shouldStripLeadingZeros) {\r\n raw = this.stripLeadingZeros(raw);\r\n }\r\n }\r\n\r\n this._rawValue = raw;\r\n this.refreshDisplay(cursorPositionBeforeReformat, displayLengthBeforeReformat);\r\n this._onChange(this.buildModelValue());\r\n this._onValidatorChange();\r\n }\r\n\r\n /**\r\n * When the user leaves the field, mark it as touched (triggers validation\r\n * display) and clean up any trailing decimal point (e.g. the user typed\r\n * '12.' and then tabbed away).\r\n */\r\n @HostListener('blur')\r\n onBlur(): void {\r\n this._onTouched();\r\n\r\n const hasTrailingDot =\r\n !this.hasPattern &&\r\n this.effectiveOutputType === 'number' &&\r\n this._rawValue.endsWith('.');\r\n\r\n if (hasTrailingDot) {\r\n this._rawValue = this._rawValue.slice(0, -1);\r\n this.refreshDisplay();\r\n }\r\n }\r\n\r\n /**\r\n * Handle paste events manually so we can sanitize the pasted content\r\n * before it reaches the input, just like we do for individual keystrokes.\r\n */\r\n @HostListener('paste', ['$event'])\r\n onPaste(event: ClipboardEvent): void {\r\n event.preventDefault(); // stop the browser from pasting the raw text\r\n\r\n const pastedText = event.clipboardData?.getData('text') ?? '';\r\n\r\n // Convert Eastern numerals in pasted content too\r\n const normalized = this.convertEasternNumerals\r\n ? convertEasternDigits(pastedText)\r\n : pastedText;\r\n\r\n // Sanitize the pasted string to only the characters our mode allows\r\n const sanitized = this.hasPattern\r\n ? normalized.replace(REGEX.ONLY_DIGITS, '') // pattern: digits only\r\n : this.sanitizePastedText(normalized); // non-pattern: mode-aware\r\n\r\n // Bail out if there's nothing usable to paste.\r\n // (Previously this also tried to special-case a paste of just '0', but\r\n // comparing a value to both '' and '0' in the same check is a logical\r\n // impossibility — a string can never be both at once — so that branch\r\n // was unreachable dead code and TypeScript correctly flagged it.)\r\n if (sanitized === '') return;\r\n\r\n const inputEl = this.el.nativeElement;\r\n\r\n // Figure out where the cursor/selection is inside the RAW digit string\r\n const rawInsertStart = this.hasPattern\r\n ? this.mapDisplayCursorToRawIndex(inputEl.selectionStart ?? 0)\r\n : (inputEl.selectionStart ?? 0);\r\n const rawInsertEnd = this.hasPattern\r\n ? this.mapDisplayCursorToRawIndex(inputEl.selectionEnd ?? 0)\r\n : (inputEl.selectionEnd ?? 0);\r\n\r\n // Insert sanitized content into the raw value at the cursor position\r\n const beforeCursor = this._rawValue.slice(0, rawInsertStart);\r\n const afterCursor = this._rawValue.slice(rawInsertEnd);\r\n let newRaw = beforeCursor + sanitized + afterCursor;\r\n\r\n if (this.hasPattern) {\r\n // Pattern mode: keep digits only, cap at slot count\r\n newRaw = newRaw.replace(REGEX.ONLY_DIGITS, '').slice(0, this.patternDigitSlotCount);\r\n\r\n } else {\r\n // Non-pattern mode: apply the same rules as onInput\r\n if (this.decimalPlaces === 0 || this.effectiveOutputType === 'string') {\r\n newRaw = newRaw.replace(REGEX.ALL_DOTS, '');\r\n } else {\r\n newRaw = this.enforceDecimalRules(newRaw);\r\n }\r\n\r\n const maxLen = this.effectiveMaxLength;\r\n const rawDigitCount = newRaw.replace(REGEX.NON_DIGIT_CHARACTERS, '').length;\r\n if (maxLen !== null && rawDigitCount > maxLen) {\r\n return; // pasted content would exceed digit limit — discard\r\n }\r\n\r\n const shouldStripLeadingZeros =\r\n !this.leadingZeros && this.effectiveOutputType === 'number';\r\n if (shouldStripLeadingZeros) {\r\n newRaw = this.stripLeadingZeros(newRaw);\r\n }\r\n }\r\n\r\n this._rawValue = newRaw;\r\n this.refreshDisplay();\r\n this._onChange(this.buildModelValue());\r\n this._onValidatorChange();\r\n }\r\n\r\n /**\r\n * Block drag-and-drop into the field.\r\n * Without this a user could drag text from elsewhere and bypass our rules.\r\n */\r\n @HostListener('drop', ['$event'])\r\n onDrop(event: DragEvent): void {\r\n event.preventDefault();\r\n }\r\n\r\n // ─── Private methods ───────────────────────────────────────────────────────\r\n\r\n /**\r\n * Build the value that gets emitted to the Angular form model.\r\n *\r\n * Pattern / string mode → string | null\r\n * '' → null (empty field)\r\n * '123' → '123' (raw digits, leading zeros preserved, no separators)\r\n *\r\n * Number mode → number | null\r\n * '' → null (empty field)\r\n * '-' → null (user typed a minus but no digits yet)\r\n * '123' → 123\r\n * '1.5' → 1.5\r\n * '-7' → -7\r\n */\r\n private buildModelValue(): number | string | null {\r\n if (this._rawValue === '') {\r\n return null;\r\n }\r\n\r\n if (this.effectiveOutputType === 'string') {\r\n return this._rawValue || null;\r\n }\r\n\r\n // Number mode\r\n if (this._rawValue === '-') {\r\n return null; // not a valid number yet\r\n }\r\n\r\n const num = parseFloat(this._rawValue);\r\n return isNaN(num) ? null : num;\r\n }\r\n\r\n /**\r\n * Write the formatted display string back to the <input> DOM element\r\n * and try to restore the cursor to a sensible position.\r\n *\r\n * @param cursorBefore Cursor position in the display string BEFORE we reformatted\r\n * @param lengthBefore Total display string length BEFORE we reformatted\r\n */\r\n private refreshDisplay(cursorBefore?: number, lengthBefore?: number): void {\r\n // ── Step 1: format the number portion ─────────────────────────────────\r\n const formattedContent = this.hasPattern\r\n ? this.applyPatternFormatting(this._rawValue)\r\n : this.effectiveOutputType === 'string'\r\n ? this._rawValue // string mode: no formatting\r\n : this.applyThousandsFormatting(this._rawValue); // number mode: comma groups\r\n\r\n // ── Step 2: assemble the full display string ───────────────────────────\r\n //\r\n // RTL layout: [LRM][suffix][formattedNumber][prefix]\r\n // LTR layout: [prefix][formattedNumber][suffix]\r\n //\r\n // WHY SWAP PREFIX AND SUFFIX IN RTL?\r\n // In RTL reading order, the \"start\" of a line is the right side.\r\n // A currency symbol like \"﷼\" (Rial) naturally sits on the RIGHT of\r\n // the number in Arabic/Persian convention, while a unit like \"kg\"\r\n // sits on the LEFT. That maps to: prefix visually right, suffix left.\r\n // By swapping their positions in the string, the bidi algorithm places\r\n // them correctly without any CSS changes.\r\n //\r\n // WHY LRM AT THE FRONT?\r\n // Prevents the bidi algorithm from treating the numeric content as RTL\r\n // text and mirroring digit order or separator placement.\r\n // The LRM is stripped before the value reaches the model.\r\n const fullDisplayValue = this.isRtl\r\n ? LRM + this.suffix + formattedContent + this.prefix\r\n : this.prefix + formattedContent + this.suffix;\r\n\r\n this.renderer.setProperty(this.el.nativeElement, 'value', fullDisplayValue);\r\n\r\n // ── Step 3: restore cursor position ───────────────────────────────────\r\n if (cursorBefore !== undefined && lengthBefore !== undefined) {\r\n const lengthDelta = fullDisplayValue.length - lengthBefore;\r\n\r\n // Clamp cursor between the editable start and end offsets,\r\n // both of which account for the RTL/LTR decoration layout.\r\n const newCursorPos = Math.max(\r\n this.editableStartOffset,\r\n Math.min(\r\n cursorBefore + lengthDelta,\r\n fullDisplayValue.length - this.editableEndPadding,\r\n ),\r\n );\r\n\r\n // Defer to next animation frame so the browser has committed the new value\r\n requestAnimationFrame(() => {\r\n this.el.nativeElement.setSelectionRange(newCursorPos, newCursorPos);\r\n });\r\n }\r\n }\r\n\r\n /**\r\n * Apply the display format pattern to the current raw digit string.\r\n *\r\n * Algorithm:\r\n * Walk through the pattern character by character.\r\n * On '0': consume the next raw digit and add it to the result.\r\n * On anything else: this is a separator — add it ONLY if more digits follow.\r\n * (The \"more digits follow\" check prevents trailing separators mid-type.)\r\n *\r\n * Examples:\r\n * pattern '0000 0000 0000 0000', raw '41111111' → '4111 1111'\r\n * pattern '(000) 000-0000', raw '5558675' → '(555) 867'\r\n * pattern '00/00', raw '122' → '12/2'\r\n */\r\n private applyPatternFormatting(raw: string): string {\r\n if (!this.hasPattern || !raw) {\r\n return raw;\r\n }\r\n\r\n const digits = raw.replace(REGEX.ONLY_DIGITS, ''); // isolated digit string\r\n let digitIndex = 0;\r\n let displayResult = '';\r\n\r\n for (const patternChar of this._resolvedPattern) {\r\n // Stop when we run out of digits to place\r\n if (digitIndex >= digits.length) {\r\n break;\r\n }\r\n\r\n if (patternChar === '0') {\r\n // Digit slot: consume the next digit\r\n displayResult += digits[digitIndex];\r\n digitIndex++;\r\n } else {\r\n // Separator character: insert it only when more digits are still coming\r\n // so we never show a trailing space/dash after the last typed digit\r\n const moreDigitsRemain = digitIndex < digits.length;\r\n if (moreDigitsRemain) {\r\n displayResult += patternChar;\r\n }\r\n }\r\n }\r\n\r\n return displayResult;\r\n }\r\n\r\n /**\r\n * Apply thousands separator grouping and decimal character to a raw number string.\r\n * Only used in number output mode (not pattern, not string mode).\r\n *\r\n * Examples (separator=',', decimalChar='.'):\r\n * '1234567' → '1,234,567'\r\n * '-9876.50' → '-9,876.50'\r\n * '100' → '100'\r\n */\r\n private applyThousandsFormatting(raw: string): string {\r\n if (raw === '' || raw === '-') {\r\n return raw;\r\n }\r\n\r\n const isNegative = raw.startsWith('-');\r\n const absValue = isNegative ? raw.slice(1) : raw;\r\n const [integerPart, decimalPart] = absValue.split('.');\r\n\r\n // Insert the thousands separator every 3 digits from the right\r\n const formattedInteger = this.thousandSeparator\r\n ? integerPart.replace(REGEX.THOUSANDS_SEPARATOR_POSITIONS, this.thousandSeparator)\r\n : integerPart;\r\n\r\n const formattedDecimal = decimalPart !== undefined\r\n ? this.displayDecimalChar + decimalPart\r\n : '';\r\n\r\n return (isNegative ? '-' : '') + formattedInteger + formattedDecimal;\r\n }\r\n\r\n /**\r\n * Remove all visual decoration from the displayed input value and return\r\n * the bare digit/numeric string ready for internal storage.\r\n *\r\n * Steps:\r\n * 1. Strip prefix from the front\r\n * 2. Strip suffix from the back\r\n * 3. Remove pattern separators OR thousands separators (depending on mode)\r\n * 4. Normalize non-ASCII decimal separator back to '.'\r\n * 5. Remove any remaining non-numeric characters\r\n */\r\n private stripDecorationsFrom(displayValue: string): string {\r\n let raw = displayValue;\r\n\r\n // ── Remove the LRM marker if present ──────────────────────────────────\r\n // In RTL mode every display string starts with LRM (U+200E).\r\n // Strip it first so subsequent prefix/suffix checks work correctly.\r\n if (raw.startsWith(LRM)) {\r\n raw = raw.slice(LRM.length);\r\n }\r\n\r\n if (this.isRtl) {\r\n // RTL layout: [suffix][formattedNumber][prefix]\r\n // Remove suffix from the LEFT (start) and prefix from the RIGHT (end).\r\n if (this.suffix && raw.startsWith(this.suffix)) {\r\n raw = raw.slice(this.suffix.length);\r\n }\r\n if (this.prefix && raw.endsWith(this.prefix)) {\r\n raw = raw.slice(0, -this.prefix.length);\r\n }\r\n } else {\r\n // LTR layout: [prefix][formattedNumber][suffix]\r\n // Remove prefix from the LEFT and suffix from the RIGHT.\r\n if (this.prefix && raw.startsWith(this.prefix)) {\r\n raw = raw.slice(this.prefix.length);\r\n }\r\n if (this.suffix && raw.endsWith(this.suffix)) {\r\n raw = raw.slice(0, -this.suffix.length);\r\n }\r\n }\r\n\r\n if (this.hasPattern) {\r\n // Pattern mode: remove the separator characters defined by the pattern\r\n raw = this.stripPatternSeparatorsFrom(raw);\r\n } else if (this.thousandSeparator) {\r\n // Number mode with thousands grouping: remove the grouping character\r\n const escapedSeparator = this.thousandSeparator.replace(\r\n REGEX.REGEX_SPECIAL_CHARACTERS,\r\n '\\\\$&' // prepend \\ before any regex-special character\r\n );\r\n raw = raw.replace(new RegExp(escapedSeparator, 'g'), '');\r\n }\r\n\r\n // If we're using a non-standard decimal char (e.g. ',' in European mode),\r\n // normalize it back to '.' for internal storage\r\n if (!this.hasPattern && this.displayDecimalChar !== '.') {\r\n raw = raw.replace(this.displayDecimalChar, '.');\r\n }\r\n\r\n // Final sweep: remove anything that still isn't a digit, '.', or '-'\r\n raw = raw.replace(REGEX.ONLY_DIGITS_DOT_AND_MINUS, '');\r\n\r\n return raw;\r\n }\r\n\r\n /**\r\n * Remove every separator character that the current pattern defines.\r\n * For example, pattern '(000) 000-0000' defines '(', ')', ' ', '-' as separators.\r\n * This strips all four from the input string, leaving only digits.\r\n */\r\n private stripPatternSeparatorsFrom(value: string): string {\r\n let result = value;\r\n\r\n for (const separatorChar of this.patternSeparatorChars) {\r\n const escapedChar = separatorChar.replace(REGEX.REGEX_SPECIAL_CHARACTERS, '\\\\$&');\r\n result = result.replace(new RegExp(escapedChar, 'g'), '');\r\n }\r\n\r\n return result;\r\n }\r\n\r\n /**\r\n * Ensure a raw numeric string has at most one decimal point\r\n * and at most [decimalPlaces] digits after it.\r\n *\r\n * Examples (decimalPlaces=2):\r\n * '12.345' → '12.34'\r\n * '1.2.3' → '1.23'\r\n * '100' → '100'\r\n */\r\n private enforceDecimalRules(raw: string): string {\r\n const parts = raw.split('.');\r\n\r\n // More than one dot? Collapse everything after the first dot into one decimal part\r\n if (parts.length > 2) {\r\n raw = parts[0] + '.' + parts.slice(1).join('');\r\n }\r\n\r\n // Too many decimal digits? Truncate to the allowed count\r\n const [intPart, decPart] = raw.split('.');\r\n if (decPart !== undefined && decPart.length > this.decimalPlaces) {\r\n raw = intPart + '.' + decPart.slice(0, this.decimalPlaces);\r\n }\r\n\r\n return raw;\r\n }\r\n\r\n /**\r\n * Remove unnecessary leading zeros from the integer part of a numeric string.\r\n * Always preserves at least one digit and never touches the decimal part.\r\n *\r\n * Examples:\r\n * '007' → '7'\r\n * '0042' → '42'\r\n * '0.5' → '0.5' (leading zero before decimal is correct, don't strip)\r\n * '100' → '100' (no leading zero)\r\n * '-007' → '-7' (handles negative values)\r\n */\r\n private stripLeadingZeros(raw: string): string {\r\n if (!raw) {\r\n return raw;\r\n }\r\n\r\n const isNegative = raw.startsWith('-');\r\n const absValue = isNegative ? raw.slice(1) : raw;\r\n const dotPosition = absValue.indexOf('.');\r\n const integerPart = dotPosition >= 0 ? absValue.slice(0, dotPosition) : absValue;\r\n const decimalPart = dotPosition >= 0 ? absValue.slice(dotPosition) : '';\r\n\r\n // Replace leading zeros but always keep at least one digit\r\n const cleanedInteger = integerPart.replace(REGEX.LEADING_ZEROS, '$1') || integerPart;\r\n\r\n return (isNegative ? '-' : '') + cleanedInteger + decimalPart;\r\n }\r\n\r\n /**\r\n * Sanitize a pasted string for non-pattern mode.\r\n * The characters we keep depend on the current mode:\r\n *\r\n * string mode → digits only (no dot, no minus; identifiers can't be negative)\r\n * integer number → digits + optional leading minus\r\n * decimal number → digits + optional dot + optional leading minus\r\n */\r\n private sanitizePastedText(text: string): string {\r\n // Normalize decimal comma first (e.g. from a European spreadsheet paste)\r\n let clean = text.replace(',', '.');\r\n\r\n if (this.effectiveOutputType === 'string') {\r\n clean = clean.replace(REGEX.ONLY_DIGITS, ''); // string: digits only\r\n } else if (this.decimalPlaces === 0) {\r\n clean = clean.replace(REGEX.ONLY_DIGITS_AND_MINUS, ''); // integer: digits + minus\r\n } else {\r\n clean = clean.replace(REGEX.ONLY_DIGITS_DOT_AND_MINUS, ''); // decimal: digits + . + minus\r\n }\r\n\r\n // Remove minus signs unless negative values are permitted\r\n if (!this.allowNegative || this.effectiveOutputType === 'string') {\r\n clean = clean.replace(REGEX.ALL_MINUS_SIGNS, '');\r\n }\r\n\r\n return clean;\r\n }\r\n\r\n /**\r\n * Map a cursor position in the formatted DISPLAY string back to an index\r\n * in the raw digit string.\r\n *\r\n * This is needed during paste operations in pattern mode so we know\r\n * exactly which digit position the cursor is sitting at.\r\n *\r\n * Example:\r\n * pattern '(000) 000-0000'\r\n * display '(555) 867' cursor at position 8 (after '(555) 86')\r\n *\r\n * Walking the pattern:\r\n * '(' pos 0 → sep (adjusted pos 0, raw index still 0)\r\n * '0' pos 1 → digit (raw index 0) digit consumed → raw index 1\r\n * '0' pos 2 → digit (raw index 1) digit consumed → raw index 2\r\n * '0' pos 3 → digit (raw index 2) digit consumed → raw index 3\r\n * ')' pos 4 → sep\r\n * ' ' pos 5 → sep\r\n * '0' pos 6 → digit (raw index 3) digit consumed → raw index 4\r\n * '0' pos 7 → digit (raw index 4) digit consumed → raw index 5\r\n * We reached position 8, so raw index = 5 ✔\r\n */\r\n private mapDisplayCursorToRawIndex(displayCursorPos: number): number {\r\n if (!this.hasPattern) {\r\n return displayCursorPos;\r\n }\r\n\r\n // Adjust for prefix length so we walk the pattern relative to its own start\r\n const positionInPattern = displayCursorPos - this.prefix.length;\r\n let rawDigitIndex = 0;\r\n let patternPosition = 0;\r\n\r\n for (const patternChar of this._resolvedPattern) {\r\n if (patternPosition >= positionInPattern) {\r\n break;\r\n }\r\n\r\n if (patternChar === '0') {\r\n rawDigitIndex++; // this position consumed a digit\r\n }\r\n\r\n patternPosition++;\r\n }\r\n\r\n return rawDigitIndex;\r\n }\r\n\r\n /**\r\n * Decide whether a keyboard event is a \"control\" key that should always\r\n * be allowed through without interception.\r\n *\r\n * Includes:\r\n * - Modifier keys held (Ctrl, Meta/Cmd, Alt) — so Ctrl+C, Ctrl+V, etc. work\r\n * - Navigation keys (arrows, Home, End)\r\n * - Editing keys (Backspace, Delete, Tab, Escape, Enter)\r\n */\r\n private isControlKey(event: KeyboardEvent): boolean {\r\n const isModifierHeld = event.ctrlKey || event.metaKey || event.altKey;\r\n\r\n const isNavigationOrEditKey = [\r\n 'Backspace', 'Delete',\r\n 'Tab', 'Escape', 'Enter',\r\n 'ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown',\r\n 'Home', 'End',\r\n ].includes(event.key);\r\n\r\n return isModifierHeld || isNavigationOrEditKey;\r\n }\r\n}\r\n","/*\r\n * Public API Surface of digits-only\r\n */\r\n\r\nexport * from './lib/digits-only.directive';\r\nexport * from './lib/eastern-numerals';\r\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ngx-digits-only",
3
- "version": "1.0.2",
3
+ "version": "1.0.3",
4
4
  "peerDependencies": {
5
5
  "@angular/common": ">=16.0.0",
6
6
  "@angular/core": ">=16.0.0"
@@ -81,7 +81,7 @@ import { ControlValueAccessor, Validator, AbstractControl, ValidationErrors } fr
81
81
  */
82
82
 
83
83
  type NamedPattern = 'card';
84
- declare class digitsOnlyDirective implements ControlValueAccessor, Validator, OnInit, OnChanges {
84
+ declare class DigitsOnlyDirective implements ControlValueAccessor, Validator, OnInit, OnChanges {
85
85
  private el;
86
86
  private renderer;
87
87
  private document;
@@ -384,8 +384,8 @@ declare class digitsOnlyDirective implements ControlValueAccessor, Validator, On
384
384
  * - Editing keys (Backspace, Delete, Tab, Escape, Enter)
385
385
  */
386
386
  private isControlKey;
387
- static ɵfac: i0.ɵɵFactoryDeclaration<digitsOnlyDirective, never>;
388
- static ɵdir: i0.ɵɵDirectiveDeclaration<digitsOnlyDirective, "[digitsOnly]", never, { "decimalPlaces": { "alias": "decimalPlaces"; "required": false; }; "thousandSeparator": { "alias": "thousandSeparator"; "required": false; }; "prefix": { "alias": "prefix"; "required": false; }; "suffix": { "alias": "suffix"; "required": false; }; "allowNegative": { "alias": "allowNegative"; "required": false; }; "leadingZeros": { "alias": "leadingZeros"; "required": false; }; "maxLength": { "alias": "maxLength"; "required": false; }; "min": { "alias": "min"; "required": false; }; "max": { "alias": "max"; "required": false; }; "outputType": { "alias": "outputType"; "required": false; }; "convertEasternNumerals": { "alias": "convertEasternNumerals"; "required": false; }; "pattern": { "alias": "pattern"; "required": false; }; }, {}, never, never, true, never>;
387
+ static ɵfac: i0.ɵɵFactoryDeclaration<DigitsOnlyDirective, never>;
388
+ static ɵdir: i0.ɵɵDirectiveDeclaration<DigitsOnlyDirective, "[digitsOnly]", never, { "decimalPlaces": { "alias": "decimalPlaces"; "required": false; }; "thousandSeparator": { "alias": "thousandSeparator"; "required": false; }; "prefix": { "alias": "prefix"; "required": false; }; "suffix": { "alias": "suffix"; "required": false; }; "allowNegative": { "alias": "allowNegative"; "required": false; }; "leadingZeros": { "alias": "leadingZeros"; "required": false; }; "maxLength": { "alias": "maxLength"; "required": false; }; "min": { "alias": "min"; "required": false; }; "max": { "alias": "max"; "required": false; }; "outputType": { "alias": "outputType"; "required": false; }; "convertEasternNumerals": { "alias": "convertEasternNumerals"; "required": false; }; "pattern": { "alias": "pattern"; "required": false; }; }, {}, never, never, true, never>;
389
389
  }
390
390
 
391
391
  /**
@@ -597,5 +597,5 @@ declare function getDigitScript(input: string): DigitScript;
597
597
  */
598
598
  declare function toWesternNumber(input: string | null | undefined): number;
599
599
 
600
- export { EASTERN_DIGIT_MAP, EASTERN_NUMERAL_REGEX, convertEasternDigits, digitsOnlyDirective, getDigitScript, hasEasternDigits, toWesternNumber };
600
+ export { DigitsOnlyDirective, EASTERN_DIGIT_MAP, EASTERN_NUMERAL_REGEX, convertEasternDigits, getDigitScript, hasEasternDigits, toWesternNumber };
601
601
  export type { DigitScript, NamedPattern };