ngx-mask 19.0.2 → 19.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -189,6 +189,14 @@ const maskConfigFunction: () => Partial<NgxMaskConfig> = () => {
189
189
 
190
190
  Then, just define masks in inputs.
191
191
 
192
+ ## Actively supported versions
193
+
194
+ ngx-mask follows the official Angular support policy, supporting the Active and LTS (Long-Term Support) versions of Angular. As of the latest release, Angular v17 and newer are supported.
195
+
196
+ Projects using Angular versions outside the supported range (e.g., older than v17) should use the last compatible version of ngx-mask. However, these versions will no longer receive updates, bug fixes, or new features.
197
+
198
+ For detailed information about Angular's versioning and support schedule, visit the official [Angular releases page](https://angular.dev/reference/releases#actively-supported-versions).
199
+
192
200
  ## Usage
193
201
 
194
202
  Text [documentation](https://github.com/JsDaddy/ngx-mask/blob/develop/USAGE.md)
@@ -58,9 +58,10 @@ const initialConfig = {
58
58
  decimalMarker: ['.', ','],
59
59
  clearIfNotMatch: false,
60
60
  showMaskTyped: false,
61
+ instantPrefix: false,
61
62
  placeHolderCharacter: '_',
62
63
  dropSpecialCharacters: true,
63
- hiddenInput: null,
64
+ hiddenInput: false,
64
65
  shownMaskExpression: '',
65
66
  separatorLimit: '',
66
67
  allowNegativeNumbers: false,
@@ -158,6 +159,7 @@ class NgxMaskApplierService {
158
159
  inputTransformFn = this._config.inputTransformFn;
159
160
  outputTransformFn = this._config.outputTransformFn;
160
161
  keepCharacterPositions = this._config.keepCharacterPositions;
162
+ instantPrefix = this._config.instantPrefix;
161
163
  _shift = new Set();
162
164
  plusOnePosition = false;
163
165
  maskExpression = '';
@@ -455,7 +457,7 @@ class NgxMaskApplierService {
455
457
  ? processedPosition + 1
456
458
  : processedPosition;
457
459
  cursor += 1;
458
- this._shiftStep(maskExpression, cursor, inputArray.length);
460
+ this._shiftStep(cursor);
459
461
  i--;
460
462
  if (this.leadZeroDateTime) {
461
463
  result += '0';
@@ -491,7 +493,7 @@ class NgxMaskApplierService {
491
493
  ? processedPosition + 1
492
494
  : processedPosition;
493
495
  cursor += 1;
494
- this._shiftStep(maskExpression, cursor, inputArray.length);
496
+ this._shiftStep(cursor);
495
497
  i--;
496
498
  if (this.leadZeroDateTime) {
497
499
  result += '0';
@@ -530,7 +532,7 @@ class NgxMaskApplierService {
530
532
  ? processedPosition + 1
531
533
  : processedPosition;
532
534
  cursor += 1;
533
- this._shiftStep(maskExpression, cursor, inputArray.length);
535
+ this._shiftStep(cursor);
534
536
  i--;
535
537
  if (this.leadZeroDateTime) {
536
538
  result += '0';
@@ -580,7 +582,7 @@ class NgxMaskApplierService {
580
582
  ? processedPosition + 1
581
583
  : processedPosition;
582
584
  cursor += 1;
583
- this._shiftStep(maskExpression, cursor, inputArray.length);
585
+ this._shiftStep(cursor);
584
586
  i--;
585
587
  if (this.leadZeroDateTime) {
586
588
  result += '0';
@@ -599,12 +601,12 @@ class NgxMaskApplierService {
599
601
  else if (this.specialCharacters.indexOf(maskExpression[cursor] ?? MaskExpression.EMPTY_STRING) !== -1) {
600
602
  result += maskExpression[cursor];
601
603
  cursor++;
602
- this._shiftStep(maskExpression, cursor, inputArray.length);
604
+ this._shiftStep(cursor);
603
605
  i--;
604
606
  }
605
607
  else if (maskExpression[cursor] === MaskExpression.NUMBER_NINE &&
606
608
  this.showMaskTyped) {
607
- this._shiftStep(maskExpression, cursor, inputArray.length);
609
+ this._shiftStep(cursor);
608
610
  }
609
611
  else if (this.patterns[maskExpression[cursor] ?? MaskExpression.EMPTY_STRING] &&
610
612
  this.patterns[maskExpression[cursor] ?? MaskExpression.EMPTY_STRING]?.optional) {
@@ -646,7 +648,8 @@ class NgxMaskApplierService {
646
648
  }
647
649
  }
648
650
  }
649
- if (result.length + 1 === maskExpression.length &&
651
+ if (result[processedPosition - 1] &&
652
+ result.length + 1 === maskExpression.length &&
650
653
  this.specialCharacters.indexOf(maskExpression[maskExpression.length - 1] ?? MaskExpression.EMPTY_STRING) !== -1) {
651
654
  result += maskExpression[maskExpression.length - 1];
652
655
  }
@@ -673,7 +676,7 @@ class NgxMaskApplierService {
673
676
  }
674
677
  let res = `${this.prefix}${onlySpecial ? MaskExpression.EMPTY_STRING : result}${this.showMaskTyped ? '' : this.suffix}`;
675
678
  if (result.length === 0) {
676
- res = !this.dropSpecialCharacters ? `${this.prefix}${result}` : `${result}`;
679
+ res = this.instantPrefix ? `${this.prefix}${result}` : `${result}`;
677
680
  }
678
681
  const isSpecialCharacterMaskFirstSymbol = processedValue.length === 1 &&
679
682
  this.specialCharacters.includes(maskExpression[0]) &&
@@ -812,11 +815,8 @@ class NgxMaskApplierService {
812
815
  }
813
816
  return char;
814
817
  }
815
- _shiftStep(maskExpression, cursor, inputLength) {
816
- const shiftStep = /[*?]/g.test(maskExpression.slice(0, cursor))
817
- ? inputLength
818
- : cursor;
819
- this._shift.add(shiftStep + this.prefix.length || 0);
818
+ _shiftStep(cursor) {
819
+ this._shift.add(cursor + this.prefix.length || 0);
820
820
  }
821
821
  _compareOrIncludes(value, comparedValue, excludedValue) {
822
822
  return Array.isArray(comparedValue)
@@ -913,36 +913,67 @@ class NgxMaskService extends NgxMaskApplierService {
913
913
  : MaskExpression.EMPTY_STRING;
914
914
  let newInputValue = '';
915
915
  let newPosition = position;
916
- if (this.hiddenInput !== null && !this.writingValue) {
916
+ if ((this.hiddenInput ||
917
+ (inputValue && inputValue.indexOf(MaskExpression.SYMBOL_STAR) >= 0)) &&
918
+ !this.writingValue) {
917
919
  let actualResult = inputValue && inputValue.length === 1
918
920
  ? inputValue.split(MaskExpression.EMPTY_STRING)
919
921
  : this.actualValue.split(MaskExpression.EMPTY_STRING);
922
+ if (backspaced) {
923
+ actualResult = actualResult
924
+ .slice(0, position)
925
+ .concat(actualResult.slice(position + 1));
926
+ }
927
+ if (this.showMaskTyped) {
928
+ inputValue = this.removeMask(inputValue);
929
+ actualResult = this.removeMask(actualResult.join('')).split(MaskExpression.EMPTY_STRING);
930
+ }
920
931
  if (typeof this.selStart === 'object' && typeof this.selEnd === 'object') {
921
932
  this.selStart = Number(this.selStart);
922
933
  this.selEnd = Number(this.selEnd);
923
934
  }
924
935
  else {
925
- inputValue !== MaskExpression.EMPTY_STRING && actualResult.length
926
- ? typeof this.selStart === 'number' && typeof this.selEnd === 'number'
927
- ? inputValue.length > actualResult.length
928
- ? actualResult.splice(this.selStart, 0, getSymbol)
929
- : inputValue.length < actualResult.length
930
- ? actualResult.length - inputValue.length === 1
931
- ? backspaced
932
- ? actualResult.splice(this.selStart - 1, 1)
933
- : actualResult.splice(inputValue.length - 1, 1)
934
- : actualResult.splice(this.selStart, this.selEnd - this.selStart)
935
- : null
936
- : null
937
- : (actualResult = []);
936
+ if (inputValue !== MaskExpression.EMPTY_STRING && actualResult.length) {
937
+ if (typeof this.selStart === 'number' && typeof this.selEnd === 'number') {
938
+ if (inputValue.length > actualResult.length) {
939
+ actualResult.splice(this.selStart, 0, getSymbol);
940
+ }
941
+ else if (inputValue.length < actualResult.length) {
942
+ if (actualResult.length - inputValue.length === 1) {
943
+ if (backspaced) {
944
+ actualResult.splice(this.selStart - 1, 1);
945
+ }
946
+ else {
947
+ actualResult.splice(inputValue.length - 1, 1);
948
+ }
949
+ }
950
+ else {
951
+ actualResult.splice(this.selStart, this.selEnd - this.selStart);
952
+ }
953
+ }
954
+ }
955
+ }
956
+ else {
957
+ actualResult = [];
958
+ }
938
959
  }
939
960
  if (this.showMaskTyped && !this.hiddenInput) {
940
961
  newInputValue = this.removeMask(inputValue);
941
962
  }
942
- newInputValue =
943
- this.actualValue.length && actualResult.length <= inputValue.length
944
- ? this.shiftTypedSymbols(actualResult.join(MaskExpression.EMPTY_STRING))
945
- : inputValue;
963
+ if (this.actualValue.length) {
964
+ if (actualResult.length < inputValue.length) {
965
+ newInputValue = this.shiftTypedSymbols(actualResult.join(MaskExpression.EMPTY_STRING));
966
+ }
967
+ else if (actualResult.length === inputValue.length) {
968
+ newInputValue = actualResult.join(MaskExpression.EMPTY_STRING);
969
+ }
970
+ else {
971
+ newInputValue = inputValue;
972
+ }
973
+ }
974
+ else {
975
+ newInputValue = inputValue;
976
+ }
946
977
  }
947
978
  if (justPasted && (this.hiddenInput || !this.hiddenInput)) {
948
979
  newInputValue = inputValue;
@@ -965,7 +996,7 @@ class NgxMaskService extends NgxMaskApplierService {
965
996
  if (this.showMaskTyped &&
966
997
  this.placeHolderCharacter.length === 1 &&
967
998
  !this.leadZeroDateTime) {
968
- newInputValue = this.removeMask(inputValue);
999
+ newInputValue = this.removeMask(newInputValue);
969
1000
  }
970
1001
  if (this.maskChanged) {
971
1002
  newInputValue = inputValue;
@@ -1013,9 +1044,6 @@ class NgxMaskService extends NgxMaskApplierService {
1013
1044
  : '';
1014
1045
  if (!this.showMaskTyped || (this.showMaskTyped && this.hiddenInput)) {
1015
1046
  if (this.hiddenInput) {
1016
- if (backspaced) {
1017
- return this.hideInput(result, this.maskExpression);
1018
- }
1019
1047
  return `${this.hideInput(result, this.maskExpression)}${this.maskIsShown.slice(result.length)}`;
1020
1048
  }
1021
1049
  return result;
@@ -1258,7 +1286,7 @@ class NgxMaskService extends NgxMaskApplierService {
1258
1286
  }
1259
1287
  formControlResult(inputValue) {
1260
1288
  if (this.writingValue && !inputValue) {
1261
- this.onChange(this.outputTransformFn(''));
1289
+ this.onChange(this.outputTransformFn(null));
1262
1290
  return;
1263
1291
  }
1264
1292
  if (this.writingValue || (!this.triggerOnMaskChange && this.maskChanged)) {
@@ -1489,6 +1517,7 @@ class NgxMaskDirective {
1489
1517
  inputTransformFn = input(null);
1490
1518
  outputTransformFn = input(null);
1491
1519
  keepCharacterPositions = input(null);
1520
+ instantPrefix = input(null);
1492
1521
  maskFilled = output();
1493
1522
  _maskValue = signal('');
1494
1523
  _inputValue = signal('');
@@ -1504,7 +1533,7 @@ class NgxMaskDirective {
1504
1533
  onChange = (_) => { };
1505
1534
  onTouch = () => { };
1506
1535
  ngOnChanges(changes) {
1507
- const { mask, specialCharacters, patterns, prefix, suffix, thousandSeparator, decimalMarker, dropSpecialCharacters, hiddenInput, showMaskTyped, placeHolderCharacter, shownMaskExpression, clearIfNotMatch, validation, separatorLimit, allowNegativeNumbers, leadZeroDateTime, leadZero, triggerOnMaskChange, apm, inputTransformFn, outputTransformFn, keepCharacterPositions, } = changes;
1536
+ const { mask, specialCharacters, patterns, prefix, suffix, thousandSeparator, decimalMarker, dropSpecialCharacters, hiddenInput, showMaskTyped, placeHolderCharacter, shownMaskExpression, clearIfNotMatch, validation, separatorLimit, allowNegativeNumbers, leadZeroDateTime, leadZero, triggerOnMaskChange, apm, inputTransformFn, outputTransformFn, keepCharacterPositions, instantPrefix, } = changes;
1508
1537
  if (mask) {
1509
1538
  if (mask.currentValue !== mask.previousValue && !mask.firstChange) {
1510
1539
  this._maskService.maskChanged = true;
@@ -1541,6 +1570,9 @@ class NgxMaskDirective {
1541
1570
  if (apm && apm.currentValue) {
1542
1571
  this._maskService.apm = apm.currentValue;
1543
1572
  }
1573
+ if (instantPrefix) {
1574
+ this._maskService.instantPrefix = instantPrefix.currentValue;
1575
+ }
1544
1576
  if (prefix) {
1545
1577
  this._maskService.prefix = prefix.currentValue;
1546
1578
  }
@@ -1558,6 +1590,9 @@ class NgxMaskDirective {
1558
1590
  }
1559
1591
  if (hiddenInput) {
1560
1592
  this._maskService.hiddenInput = hiddenInput.currentValue;
1593
+ if (hiddenInput.previousValue === true && hiddenInput.currentValue === false) {
1594
+ this._inputValue.set(this._maskService.actualValue);
1595
+ }
1561
1596
  }
1562
1597
  if (showMaskTyped) {
1563
1598
  this._maskService.showMaskTyped = showMaskTyped.currentValue;
@@ -2143,7 +2178,7 @@ class NgxMaskDirective {
2143
2178
  }
2144
2179
  this._maskService.isNumberValue = true;
2145
2180
  }
2146
- if (typeof inputValue !== 'string') {
2181
+ if (typeof inputValue !== 'string' || value === null || typeof value === 'undefined') {
2147
2182
  inputValue = '';
2148
2183
  }
2149
2184
  this._inputValue.set(inputValue);
@@ -2258,11 +2293,12 @@ class NgxMaskDirective {
2258
2293
  }
2259
2294
  }
2260
2295
  else {
2296
+ const cleanMask = this._maskService.removeMask(mask);
2261
2297
  const check = this._maskService
2262
2298
  .removeMask(this._inputValue())
2263
2299
  ?.split(MaskExpression.EMPTY_STRING)
2264
2300
  .every((character, index) => {
2265
- const indexMask = mask.charAt(index);
2301
+ const indexMask = cleanMask.charAt(index);
2266
2302
  return this._maskService._checkSymbolMask(character, indexMask);
2267
2303
  });
2268
2304
  if (check || this._justPasted()) {
@@ -2286,7 +2322,7 @@ class NgxMaskDirective {
2286
2322
  });
2287
2323
  }
2288
2324
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.0.3", ngImport: i0, type: NgxMaskDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
2289
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "19.0.3", type: NgxMaskDirective, isStandalone: true, selector: "input[mask], textarea[mask]", inputs: { mask: { classPropertyName: "mask", publicName: "mask", isSignal: true, isRequired: false, transformFunction: null }, specialCharacters: { classPropertyName: "specialCharacters", publicName: "specialCharacters", isSignal: true, isRequired: false, transformFunction: null }, patterns: { classPropertyName: "patterns", publicName: "patterns", isSignal: true, isRequired: false, transformFunction: null }, prefix: { classPropertyName: "prefix", publicName: "prefix", isSignal: true, isRequired: false, transformFunction: null }, suffix: { classPropertyName: "suffix", publicName: "suffix", isSignal: true, isRequired: false, transformFunction: null }, thousandSeparator: { classPropertyName: "thousandSeparator", publicName: "thousandSeparator", isSignal: true, isRequired: false, transformFunction: null }, decimalMarker: { classPropertyName: "decimalMarker", publicName: "decimalMarker", isSignal: true, isRequired: false, transformFunction: null }, dropSpecialCharacters: { classPropertyName: "dropSpecialCharacters", publicName: "dropSpecialCharacters", isSignal: true, isRequired: false, transformFunction: null }, hiddenInput: { classPropertyName: "hiddenInput", publicName: "hiddenInput", isSignal: true, isRequired: false, transformFunction: null }, showMaskTyped: { classPropertyName: "showMaskTyped", publicName: "showMaskTyped", isSignal: true, isRequired: false, transformFunction: null }, placeHolderCharacter: { classPropertyName: "placeHolderCharacter", publicName: "placeHolderCharacter", isSignal: true, isRequired: false, transformFunction: null }, shownMaskExpression: { classPropertyName: "shownMaskExpression", publicName: "shownMaskExpression", isSignal: true, isRequired: false, transformFunction: null }, clearIfNotMatch: { classPropertyName: "clearIfNotMatch", publicName: "clearIfNotMatch", isSignal: true, isRequired: false, transformFunction: null }, validation: { classPropertyName: "validation", publicName: "validation", isSignal: true, isRequired: false, transformFunction: null }, separatorLimit: { classPropertyName: "separatorLimit", publicName: "separatorLimit", isSignal: true, isRequired: false, transformFunction: null }, allowNegativeNumbers: { classPropertyName: "allowNegativeNumbers", publicName: "allowNegativeNumbers", isSignal: true, isRequired: false, transformFunction: null }, leadZeroDateTime: { classPropertyName: "leadZeroDateTime", publicName: "leadZeroDateTime", isSignal: true, isRequired: false, transformFunction: null }, leadZero: { classPropertyName: "leadZero", publicName: "leadZero", isSignal: true, isRequired: false, transformFunction: null }, triggerOnMaskChange: { classPropertyName: "triggerOnMaskChange", publicName: "triggerOnMaskChange", isSignal: true, isRequired: false, transformFunction: null }, apm: { classPropertyName: "apm", publicName: "apm", isSignal: true, isRequired: false, transformFunction: null }, inputTransformFn: { classPropertyName: "inputTransformFn", publicName: "inputTransformFn", isSignal: true, isRequired: false, transformFunction: null }, outputTransformFn: { classPropertyName: "outputTransformFn", publicName: "outputTransformFn", isSignal: true, isRequired: false, transformFunction: null }, keepCharacterPositions: { classPropertyName: "keepCharacterPositions", publicName: "keepCharacterPositions", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { maskFilled: "maskFilled" }, host: { listeners: { "paste": "onPaste()", "focus": "onFocus($event)", "ngModelChange": "onModelChange($event)", "input": "onInput($event)", "compositionstart": "onCompositionStart($event)", "compositionend": "onCompositionEnd($event)", "blur": "onBlur($event)", "click": "onClick($event)", "keydown": "onKeyDown($event)" } }, providers: [
2325
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "19.0.3", type: NgxMaskDirective, isStandalone: true, selector: "input[mask], textarea[mask]", inputs: { mask: { classPropertyName: "mask", publicName: "mask", isSignal: true, isRequired: false, transformFunction: null }, specialCharacters: { classPropertyName: "specialCharacters", publicName: "specialCharacters", isSignal: true, isRequired: false, transformFunction: null }, patterns: { classPropertyName: "patterns", publicName: "patterns", isSignal: true, isRequired: false, transformFunction: null }, prefix: { classPropertyName: "prefix", publicName: "prefix", isSignal: true, isRequired: false, transformFunction: null }, suffix: { classPropertyName: "suffix", publicName: "suffix", isSignal: true, isRequired: false, transformFunction: null }, thousandSeparator: { classPropertyName: "thousandSeparator", publicName: "thousandSeparator", isSignal: true, isRequired: false, transformFunction: null }, decimalMarker: { classPropertyName: "decimalMarker", publicName: "decimalMarker", isSignal: true, isRequired: false, transformFunction: null }, dropSpecialCharacters: { classPropertyName: "dropSpecialCharacters", publicName: "dropSpecialCharacters", isSignal: true, isRequired: false, transformFunction: null }, hiddenInput: { classPropertyName: "hiddenInput", publicName: "hiddenInput", isSignal: true, isRequired: false, transformFunction: null }, showMaskTyped: { classPropertyName: "showMaskTyped", publicName: "showMaskTyped", isSignal: true, isRequired: false, transformFunction: null }, placeHolderCharacter: { classPropertyName: "placeHolderCharacter", publicName: "placeHolderCharacter", isSignal: true, isRequired: false, transformFunction: null }, shownMaskExpression: { classPropertyName: "shownMaskExpression", publicName: "shownMaskExpression", isSignal: true, isRequired: false, transformFunction: null }, clearIfNotMatch: { classPropertyName: "clearIfNotMatch", publicName: "clearIfNotMatch", isSignal: true, isRequired: false, transformFunction: null }, validation: { classPropertyName: "validation", publicName: "validation", isSignal: true, isRequired: false, transformFunction: null }, separatorLimit: { classPropertyName: "separatorLimit", publicName: "separatorLimit", isSignal: true, isRequired: false, transformFunction: null }, allowNegativeNumbers: { classPropertyName: "allowNegativeNumbers", publicName: "allowNegativeNumbers", isSignal: true, isRequired: false, transformFunction: null }, leadZeroDateTime: { classPropertyName: "leadZeroDateTime", publicName: "leadZeroDateTime", isSignal: true, isRequired: false, transformFunction: null }, leadZero: { classPropertyName: "leadZero", publicName: "leadZero", isSignal: true, isRequired: false, transformFunction: null }, triggerOnMaskChange: { classPropertyName: "triggerOnMaskChange", publicName: "triggerOnMaskChange", isSignal: true, isRequired: false, transformFunction: null }, apm: { classPropertyName: "apm", publicName: "apm", isSignal: true, isRequired: false, transformFunction: null }, inputTransformFn: { classPropertyName: "inputTransformFn", publicName: "inputTransformFn", isSignal: true, isRequired: false, transformFunction: null }, outputTransformFn: { classPropertyName: "outputTransformFn", publicName: "outputTransformFn", isSignal: true, isRequired: false, transformFunction: null }, keepCharacterPositions: { classPropertyName: "keepCharacterPositions", publicName: "keepCharacterPositions", isSignal: true, isRequired: false, transformFunction: null }, instantPrefix: { classPropertyName: "instantPrefix", publicName: "instantPrefix", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { maskFilled: "maskFilled" }, host: { listeners: { "paste": "onPaste()", "focus": "onFocus($event)", "ngModelChange": "onModelChange($event)", "input": "onInput($event)", "compositionstart": "onCompositionStart($event)", "compositionend": "onCompositionEnd($event)", "blur": "onBlur($event)", "click": "onClick($event)", "keydown": "onKeyDown($event)" } }, providers: [
2290
2326
  {
2291
2327
  provide: NG_VALUE_ACCESSOR,
2292
2328
  useExisting: NgxMaskDirective,