ngx-mask 22.0.1 → 22.1.0
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/fesm2022/ngx-mask.mjs +152 -35
- package/package.json +1 -1
- package/types/ngx-mask.d.ts +50 -8
package/fesm2022/ngx-mask.mjs
CHANGED
|
@@ -105,12 +105,14 @@ const initialConfig = {
|
|
|
105
105
|
},
|
|
106
106
|
d: {
|
|
107
107
|
pattern: new RegExp('\\d'),
|
|
108
|
+
symbol: '*',
|
|
108
109
|
},
|
|
109
110
|
m: {
|
|
110
111
|
pattern: new RegExp('\\d'),
|
|
111
112
|
},
|
|
112
113
|
M: {
|
|
113
114
|
pattern: new RegExp('\\d'),
|
|
115
|
+
symbol: '*',
|
|
114
116
|
},
|
|
115
117
|
H: {
|
|
116
118
|
pattern: new RegExp('\\d'),
|
|
@@ -550,7 +552,7 @@ const percentHandler = function (state, params) {
|
|
|
550
552
|
}
|
|
551
553
|
processedValue = `${base}${processedValue.substring(processedValue.indexOf(decimalMarker), processedValue.length)}`;
|
|
552
554
|
}
|
|
553
|
-
let value
|
|
555
|
+
let value;
|
|
554
556
|
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
|
|
555
557
|
this.allowNegativeNumbers && processedValue.slice(cursor, cursor + 1) === MaskExpression.MINUS
|
|
556
558
|
? (value = `${MaskExpression.MINUS}${processedValue.slice(cursor + 1, cursor + processedValue.length)}`)
|
|
@@ -580,7 +582,8 @@ const percentHandler = function (state, params) {
|
|
|
580
582
|
*/
|
|
581
583
|
const separatorHandler = function (state, params) {
|
|
582
584
|
const { backspaced, justPasted, startsWithPrefix, prefixAlreadyRemovedByCaller, cb, inputValue, } = params;
|
|
583
|
-
let { processedValue, processedPosition,
|
|
585
|
+
let { processedValue, processedPosition, backspaceShift, shift, stepBack } = state;
|
|
586
|
+
let result;
|
|
584
587
|
if (processedValue.match('[wа-яА-Я]') ||
|
|
585
588
|
processedValue.match('[ЁёА-я]') ||
|
|
586
589
|
processedValue.match('[a-z]|[A-Z]') ||
|
|
@@ -942,7 +945,7 @@ class NgxMaskApplierService {
|
|
|
942
945
|
}
|
|
943
946
|
let cursor = 0;
|
|
944
947
|
let result = '';
|
|
945
|
-
|
|
948
|
+
const multi = false;
|
|
946
949
|
let backspaceShift = false;
|
|
947
950
|
let shift = 1;
|
|
948
951
|
let stepBack = false;
|
|
@@ -1011,7 +1014,6 @@ class NgxMaskApplierService {
|
|
|
1011
1014
|
processedPosition = resolved.processedPosition;
|
|
1012
1015
|
cursor = resolved.cursor;
|
|
1013
1016
|
result = resolved.result;
|
|
1014
|
-
multi = resolved.multi;
|
|
1015
1017
|
backspaceShift = resolved.backspaceShift;
|
|
1016
1018
|
shift = resolved.shift;
|
|
1017
1019
|
stepBack = resolved.stepBack;
|
|
@@ -1091,8 +1093,8 @@ class NgxMaskApplierService {
|
|
|
1091
1093
|
false);
|
|
1092
1094
|
}
|
|
1093
1095
|
_formatWithSeparators = (str, thousandSeparatorChar, decimalChars, precision) => {
|
|
1094
|
-
let x
|
|
1095
|
-
let decimalChar
|
|
1096
|
+
let x;
|
|
1097
|
+
let decimalChar;
|
|
1096
1098
|
if (Array.isArray(decimalChars)) {
|
|
1097
1099
|
const regExp = new RegExp(decimalChars.map((v) => ('[\\^$.|?*+()'.indexOf(v) >= 0 ? `\\${v}` : v)).join('|'));
|
|
1098
1100
|
x = str.split(regExp);
|
|
@@ -1302,10 +1304,10 @@ class NgxMaskApplierService {
|
|
|
1302
1304
|
nonZeroIndex,
|
|
1303
1305
|
};
|
|
1304
1306
|
}
|
|
1305
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.
|
|
1306
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.
|
|
1307
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: NgxMaskApplierService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
1308
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: NgxMaskApplierService });
|
|
1307
1309
|
}
|
|
1308
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.
|
|
1310
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: NgxMaskApplierService, decorators: [{
|
|
1309
1311
|
type: Injectable
|
|
1310
1312
|
}] });
|
|
1311
1313
|
|
|
@@ -1488,10 +1490,6 @@ class NgxMaskService extends NgxMaskApplierService {
|
|
|
1488
1490
|
actualResult = [];
|
|
1489
1491
|
}
|
|
1490
1492
|
}
|
|
1491
|
-
// Remove mask if showMaskTyped is true and hiddenInput is false
|
|
1492
|
-
if (this.showMaskTyped && !this.hiddenInput) {
|
|
1493
|
-
newInputValue = this.removeMask(inputValue);
|
|
1494
|
-
}
|
|
1495
1493
|
// Handle actual value length
|
|
1496
1494
|
if (this.actualValue.length) {
|
|
1497
1495
|
if (actualResult.length < inputValue.length) {
|
|
@@ -1668,11 +1666,24 @@ class NgxMaskService extends NgxMaskApplierService {
|
|
|
1668
1666
|
return inputValue
|
|
1669
1667
|
.split(MaskExpression.EMPTY_STRING)
|
|
1670
1668
|
.map((curr, index) => {
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
return
|
|
1675
|
-
|
|
1669
|
+
const maskChar = maskExpression[index] ?? MaskExpression.EMPTY_STRING;
|
|
1670
|
+
const pattern = this.patterns?.[maskChar];
|
|
1671
|
+
if (pattern?.symbol) {
|
|
1672
|
+
return pattern.symbol;
|
|
1673
|
+
}
|
|
1674
|
+
// #1574: a '0' immediately following a concealed DAY/MONTH token
|
|
1675
|
+
// is that field's second digit (d0/M0) — conceal it too so the
|
|
1676
|
+
// whole 2-digit field is hidden, not just its first character.
|
|
1677
|
+
// Display-only: does not touch maskExpression/processedValue, so
|
|
1678
|
+
// date validation/leadZero clamping in generic-pattern.handler.ts
|
|
1679
|
+
// is unaffected.
|
|
1680
|
+
const prevMaskChar = maskExpression[index - 1];
|
|
1681
|
+
const prevPattern = prevMaskChar ? this.patterns?.[prevMaskChar] : null;
|
|
1682
|
+
if (maskChar === MaskExpression.NUMBER_ZERO &&
|
|
1683
|
+
(prevMaskChar === MaskExpression.DAY ||
|
|
1684
|
+
prevMaskChar === MaskExpression.MONTH) &&
|
|
1685
|
+
prevPattern?.symbol) {
|
|
1686
|
+
return prevPattern.symbol;
|
|
1676
1687
|
}
|
|
1677
1688
|
return curr;
|
|
1678
1689
|
})
|
|
@@ -2190,10 +2201,10 @@ class NgxMaskService extends NgxMaskApplierService {
|
|
|
2190
2201
|
currentLocaleDecimalMarker() {
|
|
2191
2202
|
return MaskExpression.DOT;
|
|
2192
2203
|
}
|
|
2193
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.
|
|
2194
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.
|
|
2204
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: NgxMaskService, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
|
|
2205
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: NgxMaskService });
|
|
2195
2206
|
}
|
|
2196
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.
|
|
2207
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: NgxMaskService, decorators: [{
|
|
2197
2208
|
type: Injectable
|
|
2198
2209
|
}] });
|
|
2199
2210
|
|
|
@@ -2574,7 +2585,7 @@ class NgxMaskDirective {
|
|
|
2574
2585
|
this._hasPendingInitialValue = false;
|
|
2575
2586
|
const pendingValue = this._pendingInitialValue;
|
|
2576
2587
|
this._pendingInitialValue = null;
|
|
2577
|
-
|
|
2588
|
+
this.writeValue(pendingValue);
|
|
2578
2589
|
}
|
|
2579
2590
|
}
|
|
2580
2591
|
}
|
|
@@ -2977,10 +2988,17 @@ class NgxMaskDirective {
|
|
|
2977
2988
|
inputSymbol +
|
|
2978
2989
|
this._inputValue().slice(position + 1);
|
|
2979
2990
|
}
|
|
2980
|
-
|
|
2981
|
-
|
|
2982
|
-
|
|
2983
|
-
|
|
2991
|
+
// #1488: generalizes the single-mask (d0/M0/0000) caret-shift-past-separator
|
|
2992
|
+
// fix to any date/time field. Locates the date/time field containing
|
|
2993
|
+
// `position` by scanning maskExpression for a DAY/MONTH/HOURS/HOUR/MINUTE/
|
|
2994
|
+
// SECOND token followed by its digit run, then checks overflow against the
|
|
2995
|
+
// same per-field thresholds generic-pattern.handler.ts already establishes
|
|
2996
|
+
// (day 31, month 12, hour 23/12 w/ apm, minute/second 59). Read-only against
|
|
2997
|
+
// generic-pattern.handler.ts — mirrors its thresholds, does not import from it.
|
|
2998
|
+
if (this.leadZeroDateTime()) {
|
|
2999
|
+
const field = this._findDateTimeFieldAt(this._maskService.maskExpression, position);
|
|
3000
|
+
if (field &&
|
|
3001
|
+
this._isFieldOverflowing(field, position, el.value, this._inputValue(), !!this.apm())) {
|
|
2984
3002
|
position = position + 2;
|
|
2985
3003
|
}
|
|
2986
3004
|
}
|
|
@@ -3082,6 +3100,86 @@ class NgxMaskDirective {
|
|
|
3082
3100
|
this._maskService.applyValueChanges(el.value.length, this._justPasted(), this._code() === MaskExpression.BACKSPACE || this._code() === MaskExpression.DELETE);
|
|
3083
3101
|
}
|
|
3084
3102
|
}
|
|
3103
|
+
/**
|
|
3104
|
+
* #1488: locates the date/time field (DAY/MONTH/HOURS/HOUR/MINUTE/SECOND) that
|
|
3105
|
+
* `position` falls within, scanning `maskExpression` left-to-right. A field spans
|
|
3106
|
+
* its token plus its digit-pair companion — `0` for DAY/MONTH/MINUTE/SECOND (e.g.
|
|
3107
|
+
* `d0`, `M0`, `m0`, `s0`), or `HOUR` for the combined `Hh` hour field. Matches any
|
|
3108
|
+
* position from the field's first char up to and including its end (separator index),
|
|
3109
|
+
* so both a still-mid-typing caret and the just-completed field are covered — the
|
|
3110
|
+
* precise overflow decision (single first digit vs. completed 2-digit value) happens
|
|
3111
|
+
* in `_isFieldOverflowing`. Returns null for non-date/time masks, a no-op for the caller.
|
|
3112
|
+
*/
|
|
3113
|
+
_findDateTimeFieldAt(maskExpression, position) {
|
|
3114
|
+
const dateTimeTokens = [
|
|
3115
|
+
MaskExpression.DAY,
|
|
3116
|
+
MaskExpression.MONTH,
|
|
3117
|
+
MaskExpression.HOURS,
|
|
3118
|
+
MaskExpression.HOUR,
|
|
3119
|
+
MaskExpression.MINUTE,
|
|
3120
|
+
MaskExpression.SECOND,
|
|
3121
|
+
];
|
|
3122
|
+
let index = 0;
|
|
3123
|
+
while (index < maskExpression.length) {
|
|
3124
|
+
const token = maskExpression[index] ?? MaskExpression.EMPTY_STRING;
|
|
3125
|
+
if (dateTimeTokens.includes(token)) {
|
|
3126
|
+
const next = maskExpression[index + 1];
|
|
3127
|
+
const isCombinedHour = token === MaskExpression.HOURS && next === MaskExpression.HOUR;
|
|
3128
|
+
const end = next === MaskExpression.NUMBER_ZERO || isCombinedHour ? index + 2 : index + 1;
|
|
3129
|
+
if (position >= index && position <= end) {
|
|
3130
|
+
return {
|
|
3131
|
+
token: token,
|
|
3132
|
+
start: index,
|
|
3133
|
+
end,
|
|
3134
|
+
};
|
|
3135
|
+
}
|
|
3136
|
+
index = end;
|
|
3137
|
+
continue;
|
|
3138
|
+
}
|
|
3139
|
+
index++;
|
|
3140
|
+
}
|
|
3141
|
+
return null;
|
|
3142
|
+
}
|
|
3143
|
+
/**
|
|
3144
|
+
* Per-token overflow thresholds, mirroring the ones `generic-pattern.handler.ts`
|
|
3145
|
+
* already establishes (day 31, month 12, hour 23/12 w/ apm, minute/second 59) — kept
|
|
3146
|
+
* as a small local table rather than importing from the handler (see design's
|
|
3147
|
+
* Rejected Alternatives: the directive and the handler stay on separate sides of the
|
|
3148
|
+
* display/parse boundary).
|
|
3149
|
+
*
|
|
3150
|
+
* Two overflow shapes are checked, mirroring how the handler itself decides mid-type:
|
|
3151
|
+
* - `firstDigitValue`: when `position` is exactly one char past the field's start (the
|
|
3152
|
+
* just-typed char is the field's first digit), an out-of-range first digit alone
|
|
3153
|
+
* (e.g. month digit `3`, day digit `4`) already triggers the handler's own leadZero
|
|
3154
|
+
* pad within this same keystroke — `el.value` hasn't caught up yet at this point in
|
|
3155
|
+
* the pipeline, so the raw typed digit (`inputValue`) must be read instead.
|
|
3156
|
+
* - `fieldValue`: once both digits of the field are present in `el.value`, an
|
|
3157
|
+
* out-of-range 2-digit value (e.g. day `33`, month `13`) triggers the shift directly.
|
|
3158
|
+
*/
|
|
3159
|
+
_isFieldOverflowing(field, position, elValue, inputValue, apm) {
|
|
3160
|
+
const fieldValue = Number(elValue.slice(field.start, field.end));
|
|
3161
|
+
const isFirstDigitOfField = position === field.start + 1;
|
|
3162
|
+
const firstDigitValue = isFirstDigitOfField
|
|
3163
|
+
? Number(inputValue.slice(position - 1, position))
|
|
3164
|
+
: NaN;
|
|
3165
|
+
switch (field.token) {
|
|
3166
|
+
case MaskExpression.DAY:
|
|
3167
|
+
return (fieldValue > 31 && fieldValue < 40) || firstDigitValue > 3;
|
|
3168
|
+
case MaskExpression.MONTH:
|
|
3169
|
+
return fieldValue > 12 || firstDigitValue > 1;
|
|
3170
|
+
case MaskExpression.HOURS:
|
|
3171
|
+
return apm
|
|
3172
|
+
? fieldValue > 9 || firstDigitValue > 9
|
|
3173
|
+
: fieldValue > 23 || firstDigitValue > 2;
|
|
3174
|
+
case MaskExpression.HOUR:
|
|
3175
|
+
return apm ? fieldValue > 12 : fieldValue > 23;
|
|
3176
|
+
case MaskExpression.MINUTE:
|
|
3177
|
+
case MaskExpression.SECOND:
|
|
3178
|
+
return fieldValue > 59 || firstDigitValue > 5;
|
|
3179
|
+
default:
|
|
3180
|
+
return false;
|
|
3181
|
+
}
|
|
3182
|
+
}
|
|
3085
3183
|
/**
|
|
3086
3184
|
* Whether any pattern slot of the current mask expression can accept a letter.
|
|
3087
3185
|
* IME composition is only meaningful for such masks; for purely numeric masks
|
|
@@ -3144,7 +3242,11 @@ class NgxMaskDirective {
|
|
|
3144
3242
|
MaskExpression.NUMBER_ZERO.repeat(precision) +
|
|
3145
3243
|
suffix;
|
|
3146
3244
|
this._maskService.actualValue = el.value;
|
|
3147
|
-
|
|
3245
|
+
// #1634: propagate through the regular formControlResult pipeline (not a
|
|
3246
|
+
// raw onChange) so outputTransformFn runs on this blur-time reformat, same
|
|
3247
|
+
// as every typing-time emission. A raw onChange here sent the masked display
|
|
3248
|
+
// string straight to the model, bypassing outputTransformFn entirely.
|
|
3249
|
+
this._maskService.formControlResult(this._maskService.actualValue);
|
|
3148
3250
|
}
|
|
3149
3251
|
}
|
|
3150
3252
|
this._maskService.clearIfNotMatchFn();
|
|
@@ -3356,7 +3458,7 @@ class NgxMaskDirective {
|
|
|
3356
3458
|
}
|
|
3357
3459
|
}
|
|
3358
3460
|
/** It writes the value in the input */
|
|
3359
|
-
|
|
3461
|
+
writeValue(controlValue) {
|
|
3360
3462
|
if (!this._configApplied && this.mask()) {
|
|
3361
3463
|
// Called before the first ngOnChanges pass configured the mask service (happens with
|
|
3362
3464
|
// Signal Forms' [formField], whose control-sync instruction runs before sibling
|
|
@@ -3570,8 +3672,23 @@ class NgxMaskDirective {
|
|
|
3570
3672
|
el.selectionStart = Math.min(Math.max(prefixLength, el.selectionStart), inputValueLength - suffixLength);
|
|
3571
3673
|
el.selectionEnd = Math.min(Math.max(prefixLength, el.selectionEnd), inputValueLength - suffixLength);
|
|
3572
3674
|
}
|
|
3573
|
-
/**
|
|
3675
|
+
/**
|
|
3676
|
+
* It disables the input element.
|
|
3677
|
+
*
|
|
3678
|
+
* Mirrors `_writeElementValueSync` (#1305): the service's `formElementProperty` setter
|
|
3679
|
+
* defers ALL DOM writes via `queueMicrotask` to dodge ExpressionChangedAfterItHasBeenChecked
|
|
3680
|
+
* and keep FIFO ordering with config-driven re-renders. For `disabled` specifically, that
|
|
3681
|
+
* deferral leaves `nativeElement.disabled` stale for at least one microtask after Angular
|
|
3682
|
+
* Forms' `setUpControl` calls this method synchronously during init — long enough for a
|
|
3683
|
+
* consumer reading the DOM property in the same synchronous phase (or another deferred write
|
|
3684
|
+
* racing in FIFO order) to observe the wrong value, e.g. an initially-disabled FormControl
|
|
3685
|
+
* whose native input briefly (or, depending on interleaving, persistently) reports
|
|
3686
|
+
* `disabled === false` (#1633). Writing synchronously here closes that gap; the deferred
|
|
3687
|
+
* write below still runs afterwards and re-applies the same final value, preserving the
|
|
3688
|
+
* existing ordering guarantees other call sites rely on.
|
|
3689
|
+
*/
|
|
3574
3690
|
setDisabledState(isDisabled) {
|
|
3691
|
+
this._renderer.setProperty(this._elementRef.nativeElement, 'disabled', isDisabled);
|
|
3575
3692
|
this._maskService.formElementProperty = ['disabled', isDisabled];
|
|
3576
3693
|
}
|
|
3577
3694
|
_applyMask() {
|
|
@@ -3784,8 +3901,8 @@ class NgxMaskDirective {
|
|
|
3784
3901
|
return uniqueCharacters.size === 1;
|
|
3785
3902
|
});
|
|
3786
3903
|
}
|
|
3787
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.
|
|
3788
|
-
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.
|
|
3904
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: NgxMaskDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
3905
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.7", 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 }, typeFromDecimals: { classPropertyName: "typeFromDecimals", publicName: "typeFromDecimals", 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 }, defaultValueOnBlur: { classPropertyName: "defaultValueOnBlur", publicName: "defaultValueOnBlur", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, touched: { classPropertyName: "touched", publicName: "touched", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", touched: "touchedChange", maskFilled: "maskFilled" }, host: { listeners: { "paste": "onPaste()", "focus": "onFocus()", "ngModelChange": "onModelChange($event)", "input": "onInput($event)", "compositionstart": "onCompositionStart()", "compositionend": "onCompositionEnd($event)", "blur": "onBlur($event)", "click": "onClick($event)", "keydown": "onKeyDown($event)" } }, providers: [
|
|
3789
3906
|
{
|
|
3790
3907
|
provide: NG_VALUE_ACCESSOR,
|
|
3791
3908
|
useExisting: NgxMaskDirective,
|
|
@@ -3799,7 +3916,7 @@ class NgxMaskDirective {
|
|
|
3799
3916
|
NgxMaskService,
|
|
3800
3917
|
], exportAs: ["mask", "ngxMask"], usesOnChanges: true, ngImport: i0 });
|
|
3801
3918
|
}
|
|
3802
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.
|
|
3919
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: NgxMaskDirective, decorators: [{
|
|
3803
3920
|
type: Directive,
|
|
3804
3921
|
args: [{
|
|
3805
3922
|
selector: 'input[mask], textarea[mask]',
|
|
@@ -3937,10 +4054,10 @@ class NgxMaskPipe {
|
|
|
3937
4054
|
});
|
|
3938
4055
|
}
|
|
3939
4056
|
}
|
|
3940
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.
|
|
3941
|
-
static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "22.0.
|
|
4057
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: NgxMaskPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
|
|
4058
|
+
static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "22.0.7", ngImport: i0, type: NgxMaskPipe, isStandalone: true, name: "mask" });
|
|
3942
4059
|
}
|
|
3943
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.
|
|
4060
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: NgxMaskPipe, decorators: [{
|
|
3944
4061
|
type: Pipe,
|
|
3945
4062
|
args: [{
|
|
3946
4063
|
name: 'mask',
|
package/package.json
CHANGED
package/types/ngx-mask.d.ts
CHANGED
|
@@ -120,7 +120,7 @@ declare class NgxMaskApplierService {
|
|
|
120
120
|
writingValue: boolean;
|
|
121
121
|
ipError?: boolean;
|
|
122
122
|
cpfCnpjError?: boolean;
|
|
123
|
-
applyMask(inputValue: string | object | boolean | null | undefined, maskExpression: string, position?: number, justPasted?: boolean, backspaced?: boolean, cb?: (
|
|
123
|
+
applyMask(inputValue: string | object | boolean | null | undefined, maskExpression: string, position?: number, justPasted?: boolean, backspaced?: boolean, cb?: (actualShift: number, backspaceShift: boolean) => void): string;
|
|
124
124
|
_findDropSpecialChar(inputSymbol: string): undefined | string;
|
|
125
125
|
_findSpecialChar(inputSymbol: string): undefined | string;
|
|
126
126
|
_checkSymbolMask(inputSymbol: string, maskSymbol: string): boolean;
|
|
@@ -177,7 +177,7 @@ declare class NgxMaskService extends NgxMaskApplierService {
|
|
|
177
177
|
private _emitValue;
|
|
178
178
|
private _start;
|
|
179
179
|
private _end;
|
|
180
|
-
onChange: (_:
|
|
180
|
+
onChange: (_: unknown) => void;
|
|
181
181
|
readonly _elementRef: ElementRef<any> | null;
|
|
182
182
|
private readonly document;
|
|
183
183
|
protected _config: NgxMaskConfig;
|
|
@@ -192,9 +192,9 @@ declare class NgxMaskService extends NgxMaskApplierService {
|
|
|
192
192
|
* @param cb Callback function.
|
|
193
193
|
* @returns The masked value.
|
|
194
194
|
*/
|
|
195
|
-
applyMask(inputValue: string, maskExpression: string, position?: number, justPasted?: boolean, backspaced?: boolean, cb?: (
|
|
195
|
+
applyMask(inputValue: string, maskExpression: string, position?: number, justPasted?: boolean, backspaced?: boolean, cb?: (actualShift: number, backspaceShift: boolean) => void): string;
|
|
196
196
|
private _numberSkipedSymbols;
|
|
197
|
-
applyValueChanges(position: number, justPasted: boolean, backspaced: boolean, cb?: (
|
|
197
|
+
applyValueChanges(position: number, justPasted: boolean, backspaced: boolean, cb?: (actualShift: number, backspaceShift: boolean) => void): void;
|
|
198
198
|
hideInput(inputValue: string, maskExpression: string): string;
|
|
199
199
|
getActualValue(res: string): string;
|
|
200
200
|
shiftTypedSymbols(inputValue: string): string;
|
|
@@ -231,7 +231,7 @@ declare class NgxMaskService extends NgxMaskApplierService {
|
|
|
231
231
|
* model value too. Therefore, we don't trigger onChange in this scenario.
|
|
232
232
|
* @param inputValue the current form input value
|
|
233
233
|
*/
|
|
234
|
-
|
|
234
|
+
formControlResult(inputValue: string): void;
|
|
235
235
|
private _toNumber;
|
|
236
236
|
private _removeMask;
|
|
237
237
|
private _removePrefix;
|
|
@@ -380,7 +380,7 @@ declare class NgxMaskDirective implements ControlValueAccessor, OnChanges, Valid
|
|
|
380
380
|
private readonly _injector;
|
|
381
381
|
private _ngControl;
|
|
382
382
|
private _resolveNgControl;
|
|
383
|
-
onChange: (_:
|
|
383
|
+
onChange: (_: unknown) => void;
|
|
384
384
|
onTouch: () => void;
|
|
385
385
|
constructor();
|
|
386
386
|
ngOnChanges(changes: SimpleChanges): void;
|
|
@@ -389,6 +389,34 @@ declare class NgxMaskDirective implements ControlValueAccessor, OnChanges, Valid
|
|
|
389
389
|
onFocus(): void;
|
|
390
390
|
onModelChange(value: unknown): void;
|
|
391
391
|
onInput(e: Event): void;
|
|
392
|
+
/**
|
|
393
|
+
* #1488: locates the date/time field (DAY/MONTH/HOURS/HOUR/MINUTE/SECOND) that
|
|
394
|
+
* `position` falls within, scanning `maskExpression` left-to-right. A field spans
|
|
395
|
+
* its token plus its digit-pair companion — `0` for DAY/MONTH/MINUTE/SECOND (e.g.
|
|
396
|
+
* `d0`, `M0`, `m0`, `s0`), or `HOUR` for the combined `Hh` hour field. Matches any
|
|
397
|
+
* position from the field's first char up to and including its end (separator index),
|
|
398
|
+
* so both a still-mid-typing caret and the just-completed field are covered — the
|
|
399
|
+
* precise overflow decision (single first digit vs. completed 2-digit value) happens
|
|
400
|
+
* in `_isFieldOverflowing`. Returns null for non-date/time masks, a no-op for the caller.
|
|
401
|
+
*/
|
|
402
|
+
private _findDateTimeFieldAt;
|
|
403
|
+
/**
|
|
404
|
+
* Per-token overflow thresholds, mirroring the ones `generic-pattern.handler.ts`
|
|
405
|
+
* already establishes (day 31, month 12, hour 23/12 w/ apm, minute/second 59) — kept
|
|
406
|
+
* as a small local table rather than importing from the handler (see design's
|
|
407
|
+
* Rejected Alternatives: the directive and the handler stay on separate sides of the
|
|
408
|
+
* display/parse boundary).
|
|
409
|
+
*
|
|
410
|
+
* Two overflow shapes are checked, mirroring how the handler itself decides mid-type:
|
|
411
|
+
* - `firstDigitValue`: when `position` is exactly one char past the field's start (the
|
|
412
|
+
* just-typed char is the field's first digit), an out-of-range first digit alone
|
|
413
|
+
* (e.g. month digit `3`, day digit `4`) already triggers the handler's own leadZero
|
|
414
|
+
* pad within this same keystroke — `el.value` hasn't caught up yet at this point in
|
|
415
|
+
* the pipeline, so the raw typed digit (`inputValue`) must be read instead.
|
|
416
|
+
* - `fieldValue`: once both digits of the field are present in `el.value`, an
|
|
417
|
+
* out-of-range 2-digit value (e.g. day `33`, month `13`) triggers the shift directly.
|
|
418
|
+
*/
|
|
419
|
+
private _isFieldOverflowing;
|
|
392
420
|
/**
|
|
393
421
|
* Whether any pattern slot of the current mask expression can accept a letter.
|
|
394
422
|
* IME composition is only meaningful for such masks; for purely numeric masks
|
|
@@ -424,7 +452,7 @@ declare class NgxMaskDirective implements ControlValueAccessor, OnChanges, Valid
|
|
|
424
452
|
*/
|
|
425
453
|
private _restoreControlStateAfterWrite;
|
|
426
454
|
/** It writes the value in the input */
|
|
427
|
-
writeValue(controlValue: unknown):
|
|
455
|
+
writeValue(controlValue: unknown): void;
|
|
428
456
|
/**
|
|
429
457
|
* Mirrors a writeValue-driven render into the DOM synchronously, in the same
|
|
430
458
|
* change-detection pass (#1305). The service's `formElementProperty` setter defers all
|
|
@@ -460,7 +488,21 @@ declare class NgxMaskDirective implements ControlValueAccessor, OnChanges, Valid
|
|
|
460
488
|
focus(): void;
|
|
461
489
|
private _getActiveElement;
|
|
462
490
|
checkSelectionOnDeletion(el: HTMLInputElement): void;
|
|
463
|
-
/**
|
|
491
|
+
/**
|
|
492
|
+
* It disables the input element.
|
|
493
|
+
*
|
|
494
|
+
* Mirrors `_writeElementValueSync` (#1305): the service's `formElementProperty` setter
|
|
495
|
+
* defers ALL DOM writes via `queueMicrotask` to dodge ExpressionChangedAfterItHasBeenChecked
|
|
496
|
+
* and keep FIFO ordering with config-driven re-renders. For `disabled` specifically, that
|
|
497
|
+
* deferral leaves `nativeElement.disabled` stale for at least one microtask after Angular
|
|
498
|
+
* Forms' `setUpControl` calls this method synchronously during init — long enough for a
|
|
499
|
+
* consumer reading the DOM property in the same synchronous phase (or another deferred write
|
|
500
|
+
* racing in FIFO order) to observe the wrong value, e.g. an initially-disabled FormControl
|
|
501
|
+
* whose native input briefly (or, depending on interleaving, persistently) reports
|
|
502
|
+
* `disabled === false` (#1633). Writing synchronously here closes that gap; the deferred
|
|
503
|
+
* write below still runs afterwards and re-applies the same final value, preserving the
|
|
504
|
+
* existing ordering guarantees other call sites rely on.
|
|
505
|
+
*/
|
|
464
506
|
setDisabledState(isDisabled: boolean): void;
|
|
465
507
|
private _applyMask;
|
|
466
508
|
/**
|