ngx-mask 22.0.2 → 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 +111 -9
- package/package.json +1 -1
- package/types/ngx-mask.d.ts +28 -0
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'),
|
|
@@ -1664,11 +1666,24 @@ class NgxMaskService extends NgxMaskApplierService {
|
|
|
1664
1666
|
return inputValue
|
|
1665
1667
|
.split(MaskExpression.EMPTY_STRING)
|
|
1666
1668
|
.map((curr, index) => {
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
return
|
|
1671
|
-
|
|
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;
|
|
1672
1687
|
}
|
|
1673
1688
|
return curr;
|
|
1674
1689
|
})
|
|
@@ -2973,10 +2988,17 @@ class NgxMaskDirective {
|
|
|
2973
2988
|
inputSymbol +
|
|
2974
2989
|
this._inputValue().slice(position + 1);
|
|
2975
2990
|
}
|
|
2976
|
-
|
|
2977
|
-
|
|
2978
|
-
|
|
2979
|
-
|
|
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())) {
|
|
2980
3002
|
position = position + 2;
|
|
2981
3003
|
}
|
|
2982
3004
|
}
|
|
@@ -3078,6 +3100,86 @@ class NgxMaskDirective {
|
|
|
3078
3100
|
this._maskService.applyValueChanges(el.value.length, this._justPasted(), this._code() === MaskExpression.BACKSPACE || this._code() === MaskExpression.DELETE);
|
|
3079
3101
|
}
|
|
3080
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
|
+
}
|
|
3081
3183
|
/**
|
|
3082
3184
|
* Whether any pattern slot of the current mask expression can accept a letter.
|
|
3083
3185
|
* IME composition is only meaningful for such masks; for purely numeric masks
|
package/package.json
CHANGED
package/types/ngx-mask.d.ts
CHANGED
|
@@ -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
|