react-input-mask-format 1.0.5 → 2.0.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/README.md +94 -44
- package/dist/index.cjs +883 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +56 -0
- package/dist/index.d.ts +49 -49
- package/dist/index.js +852 -0
- package/dist/index.js.map +1 -0
- package/package.json +52 -81
- package/dist/react-input-mask.js +0 -2381
- package/dist/react-input-mask.min.js +0 -1
- package/index.js +0 -7
- package/lib/react-input-mask.development.js +0 -1017
- package/lib/react-input-mask.production.min.js +0 -1
package/dist/index.js
ADDED
|
@@ -0,0 +1,852 @@
|
|
|
1
|
+
// src/index.tsx
|
|
2
|
+
import React, { useLayoutEffect as useLayoutEffect2, forwardRef } from "react";
|
|
3
|
+
|
|
4
|
+
// src/hooks.ts
|
|
5
|
+
import { useCallback, useEffect, useLayoutEffect, useRef } from "react";
|
|
6
|
+
|
|
7
|
+
// src/utils/defer.ts
|
|
8
|
+
function defer(fn) {
|
|
9
|
+
return requestAnimationFrame(fn);
|
|
10
|
+
}
|
|
11
|
+
function cancelDefer(deferId) {
|
|
12
|
+
if (deferId !== null) {
|
|
13
|
+
cancelAnimationFrame(deferId);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// src/utils/input.ts
|
|
18
|
+
function setInputSelection(input, start, end) {
|
|
19
|
+
if (end === void 0) {
|
|
20
|
+
end = start;
|
|
21
|
+
}
|
|
22
|
+
input.setSelectionRange(start, end);
|
|
23
|
+
}
|
|
24
|
+
function getInputSelection(input) {
|
|
25
|
+
const start = input.selectionStart;
|
|
26
|
+
const end = input.selectionEnd;
|
|
27
|
+
return {
|
|
28
|
+
start,
|
|
29
|
+
end,
|
|
30
|
+
length: (end != null ? end : 0) - (start != null ? start : 0)
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
function isInputFocused(input) {
|
|
34
|
+
const inputDocument = input.ownerDocument;
|
|
35
|
+
return inputDocument.hasFocus() && inputDocument.activeElement === input;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// src/utils/helpers.ts
|
|
39
|
+
function getElementDocument(element) {
|
|
40
|
+
return element == null ? void 0 : element.ownerDocument;
|
|
41
|
+
}
|
|
42
|
+
function getElementWindow(element) {
|
|
43
|
+
var _a;
|
|
44
|
+
return (_a = getElementDocument(element)) == null ? void 0 : _a.defaultView;
|
|
45
|
+
}
|
|
46
|
+
function isDOMElement(element) {
|
|
47
|
+
const elementWindow = getElementWindow(element);
|
|
48
|
+
return !!elementWindow && element instanceof elementWindow.HTMLElement;
|
|
49
|
+
}
|
|
50
|
+
function isFunction(value) {
|
|
51
|
+
return typeof value === "function";
|
|
52
|
+
}
|
|
53
|
+
function findLastIndex(array, predicate) {
|
|
54
|
+
for (let i = array.length - 1; i >= 0; i--) {
|
|
55
|
+
const x = array[i];
|
|
56
|
+
if (predicate(x, i)) {
|
|
57
|
+
return i;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return -1;
|
|
61
|
+
}
|
|
62
|
+
function repeat(string, n = 1) {
|
|
63
|
+
let result = "";
|
|
64
|
+
for (let i = 0; i < n; i++) {
|
|
65
|
+
result += string;
|
|
66
|
+
}
|
|
67
|
+
return result;
|
|
68
|
+
}
|
|
69
|
+
function toString(value) {
|
|
70
|
+
return `${value}`;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// src/hooks.ts
|
|
74
|
+
function useInputElement(inputRef) {
|
|
75
|
+
return useCallback(() => {
|
|
76
|
+
let input = inputRef.current;
|
|
77
|
+
const isDOMNode = typeof window !== "undefined" && isDOMElement(input);
|
|
78
|
+
if (!input || !isDOMNode) {
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
if (input.nodeName !== "INPUT") {
|
|
82
|
+
input = input.querySelector("input");
|
|
83
|
+
}
|
|
84
|
+
if (!input) {
|
|
85
|
+
throw new Error("react-input-mask: inputComponent doesn't contain input node");
|
|
86
|
+
}
|
|
87
|
+
return input;
|
|
88
|
+
}, [inputRef]);
|
|
89
|
+
}
|
|
90
|
+
function useDeferLoop(callback) {
|
|
91
|
+
const deferIdRef = useRef(null);
|
|
92
|
+
const runLoop = useCallback(() => {
|
|
93
|
+
if (deferIdRef.current !== null) {
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
function loop() {
|
|
97
|
+
callback();
|
|
98
|
+
deferIdRef.current = defer(loop);
|
|
99
|
+
}
|
|
100
|
+
loop();
|
|
101
|
+
}, [callback]);
|
|
102
|
+
const stopLoop = useCallback(() => {
|
|
103
|
+
cancelDefer(deferIdRef.current);
|
|
104
|
+
deferIdRef.current = null;
|
|
105
|
+
}, []);
|
|
106
|
+
useEffect(() => {
|
|
107
|
+
if (deferIdRef.current) {
|
|
108
|
+
stopLoop();
|
|
109
|
+
runLoop();
|
|
110
|
+
}
|
|
111
|
+
}, [runLoop, stopLoop]);
|
|
112
|
+
useEffect(() => cancelDefer(deferIdRef.current), []);
|
|
113
|
+
return [runLoop, stopLoop];
|
|
114
|
+
}
|
|
115
|
+
function useSelection(inputRef, isMasked) {
|
|
116
|
+
const selectionRef = useRef({ start: null, end: null });
|
|
117
|
+
const getInputElement = useInputElement(inputRef);
|
|
118
|
+
const getSelection = useCallback(() => {
|
|
119
|
+
const input = getInputElement();
|
|
120
|
+
return getInputSelection(input);
|
|
121
|
+
}, [getInputElement]);
|
|
122
|
+
const getLastSelection = useCallback(() => {
|
|
123
|
+
return selectionRef.current;
|
|
124
|
+
}, []);
|
|
125
|
+
const setSelection = useCallback(
|
|
126
|
+
(selection) => {
|
|
127
|
+
const input = getInputElement();
|
|
128
|
+
if (!input || !isInputFocused(input)) {
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
setInputSelection(input, selection.start, selection.end);
|
|
132
|
+
selectionRef.current = getSelection();
|
|
133
|
+
},
|
|
134
|
+
[getInputElement, getSelection]
|
|
135
|
+
);
|
|
136
|
+
const selectionLoop = useCallback(() => {
|
|
137
|
+
selectionRef.current = getSelection();
|
|
138
|
+
}, [getSelection]);
|
|
139
|
+
const [runSelectionLoop, stopSelectionLoop] = useDeferLoop(selectionLoop);
|
|
140
|
+
useLayoutEffect(() => {
|
|
141
|
+
if (!isMasked) {
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
const input = getInputElement();
|
|
145
|
+
if (!input) return;
|
|
146
|
+
input.addEventListener("focus", runSelectionLoop);
|
|
147
|
+
input.addEventListener("blur", stopSelectionLoop);
|
|
148
|
+
if (isInputFocused(input)) {
|
|
149
|
+
runSelectionLoop();
|
|
150
|
+
}
|
|
151
|
+
return () => {
|
|
152
|
+
input.removeEventListener("focus", runSelectionLoop);
|
|
153
|
+
input.removeEventListener("blur", stopSelectionLoop);
|
|
154
|
+
stopSelectionLoop();
|
|
155
|
+
};
|
|
156
|
+
});
|
|
157
|
+
return { getSelection, getLastSelection, setSelection };
|
|
158
|
+
}
|
|
159
|
+
function useValue(inputRef, initialValue) {
|
|
160
|
+
const getInputElement = useInputElement(inputRef);
|
|
161
|
+
const valueRef = useRef(initialValue);
|
|
162
|
+
const getValue = useCallback(() => {
|
|
163
|
+
return getInputElement().value;
|
|
164
|
+
}, [getInputElement]);
|
|
165
|
+
const getLastValue = useCallback(() => {
|
|
166
|
+
return valueRef.current;
|
|
167
|
+
}, []);
|
|
168
|
+
const setValue = useCallback(
|
|
169
|
+
(newValue) => {
|
|
170
|
+
valueRef.current = newValue;
|
|
171
|
+
const input = getInputElement();
|
|
172
|
+
if (input) {
|
|
173
|
+
input.value = newValue;
|
|
174
|
+
}
|
|
175
|
+
},
|
|
176
|
+
[getInputElement]
|
|
177
|
+
);
|
|
178
|
+
return {
|
|
179
|
+
getValue,
|
|
180
|
+
getLastValue,
|
|
181
|
+
setValue
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
function useInputState(initialValue, isMasked) {
|
|
185
|
+
const inputRef = useRef(null);
|
|
186
|
+
const { getSelection, getLastSelection, setSelection } = useSelection(inputRef, isMasked);
|
|
187
|
+
const { getValue, getLastValue, setValue } = useValue(inputRef, initialValue);
|
|
188
|
+
function getLastInputState() {
|
|
189
|
+
return {
|
|
190
|
+
value: getLastValue(),
|
|
191
|
+
selection: getLastSelection()
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
function getInputState() {
|
|
195
|
+
return {
|
|
196
|
+
value: getValue(),
|
|
197
|
+
selection: getSelection()
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
function setInputState({ value, selection }) {
|
|
201
|
+
setValue(value);
|
|
202
|
+
setSelection(selection);
|
|
203
|
+
}
|
|
204
|
+
return {
|
|
205
|
+
inputRef,
|
|
206
|
+
getInputState,
|
|
207
|
+
getLastInputState,
|
|
208
|
+
setInputState
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
function usePrevious(value) {
|
|
212
|
+
const ref = useRef(void 0);
|
|
213
|
+
useEffect(() => {
|
|
214
|
+
ref.current = value;
|
|
215
|
+
});
|
|
216
|
+
return ref.current;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// src/constants.ts
|
|
220
|
+
var CONTROLLED_PROPS = [
|
|
221
|
+
"disabled",
|
|
222
|
+
"onBlur",
|
|
223
|
+
"onChange",
|
|
224
|
+
"onFocus",
|
|
225
|
+
"onMouseDown",
|
|
226
|
+
"readOnly",
|
|
227
|
+
"value"
|
|
228
|
+
];
|
|
229
|
+
var defaultFormatChars = {
|
|
230
|
+
"9": /[0-9]/,
|
|
231
|
+
a: /[A-Za-z]/,
|
|
232
|
+
"*": /[A-Za-z0-9]/
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
// src/validate-props.ts
|
|
236
|
+
function warnDev(condition, message) {
|
|
237
|
+
if (process.env.NODE_ENV !== "production" && !condition) {
|
|
238
|
+
console.error(`react-input-mask-format: ${message}`);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
function invariant(condition, message) {
|
|
242
|
+
if (!condition) {
|
|
243
|
+
throw new Error(`react-input-mask-format: ${message}`);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
function validateMaxLength(props) {
|
|
247
|
+
warnDev(
|
|
248
|
+
!props.maxLength || !props.mask,
|
|
249
|
+
"maxLength property shouldn't be passed to the masked input. It breaks masking and unnecessary because length is limited by the mask length."
|
|
250
|
+
);
|
|
251
|
+
}
|
|
252
|
+
function validateMaskPlaceholder(props) {
|
|
253
|
+
const { mask, maskPlaceholder } = props;
|
|
254
|
+
invariant(
|
|
255
|
+
!mask || !maskPlaceholder || maskPlaceholder.length === 1 || maskPlaceholder.length === mask.length,
|
|
256
|
+
`maskPlaceholder should either be a single character or have the same length as the mask:
|
|
257
|
+
mask: ${mask}
|
|
258
|
+
maskPlaceholder: ${maskPlaceholder}`
|
|
259
|
+
);
|
|
260
|
+
}
|
|
261
|
+
function validateChildren(props, inputElement) {
|
|
262
|
+
const conflictProps = CONTROLLED_PROPS.filter(
|
|
263
|
+
(propId) => inputElement.props[propId] != null && inputElement.props[propId] !== props[propId]
|
|
264
|
+
);
|
|
265
|
+
invariant(
|
|
266
|
+
!conflictProps.length,
|
|
267
|
+
`the following props should be passed to the InputMask component, not to children: ${conflictProps.join(",")}`
|
|
268
|
+
);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// src/utils/parse-mask.ts
|
|
272
|
+
function parseMask({
|
|
273
|
+
mask,
|
|
274
|
+
maskPlaceholder,
|
|
275
|
+
formatChars = defaultFormatChars
|
|
276
|
+
}) {
|
|
277
|
+
const permanents = [];
|
|
278
|
+
if (!mask) {
|
|
279
|
+
return {
|
|
280
|
+
maskPlaceholder: null,
|
|
281
|
+
mask: null,
|
|
282
|
+
prefix: null,
|
|
283
|
+
lastEditablePosition: null,
|
|
284
|
+
permanents: []
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
let parsedMask;
|
|
288
|
+
if (typeof mask === "string") {
|
|
289
|
+
let isPermanent = false;
|
|
290
|
+
let parsedMaskString = "";
|
|
291
|
+
mask.split("").forEach((character) => {
|
|
292
|
+
if (!isPermanent && character === "\\") {
|
|
293
|
+
isPermanent = true;
|
|
294
|
+
} else {
|
|
295
|
+
if (isPermanent || !formatChars[character]) {
|
|
296
|
+
permanents.push(parsedMaskString.length);
|
|
297
|
+
}
|
|
298
|
+
parsedMaskString += character;
|
|
299
|
+
isPermanent = false;
|
|
300
|
+
}
|
|
301
|
+
});
|
|
302
|
+
parsedMask = parsedMaskString.split("").map((character, index) => {
|
|
303
|
+
if (permanents.indexOf(index) === -1) {
|
|
304
|
+
return formatChars[character];
|
|
305
|
+
}
|
|
306
|
+
return character;
|
|
307
|
+
});
|
|
308
|
+
} else {
|
|
309
|
+
mask.forEach((character, index) => {
|
|
310
|
+
if (typeof character === "string") {
|
|
311
|
+
permanents.push(index);
|
|
312
|
+
}
|
|
313
|
+
});
|
|
314
|
+
parsedMask = mask;
|
|
315
|
+
}
|
|
316
|
+
let parsedPlaceholder = maskPlaceholder != null ? maskPlaceholder : null;
|
|
317
|
+
if (parsedPlaceholder) {
|
|
318
|
+
let placeholderChars;
|
|
319
|
+
if (parsedPlaceholder.length === 1) {
|
|
320
|
+
placeholderChars = parsedMask.map((character, index) => {
|
|
321
|
+
if (permanents.indexOf(index) !== -1) {
|
|
322
|
+
return character;
|
|
323
|
+
}
|
|
324
|
+
return parsedPlaceholder;
|
|
325
|
+
});
|
|
326
|
+
} else {
|
|
327
|
+
placeholderChars = parsedPlaceholder.split("");
|
|
328
|
+
}
|
|
329
|
+
permanents.forEach((position) => {
|
|
330
|
+
placeholderChars[position] = parsedMask[position];
|
|
331
|
+
});
|
|
332
|
+
parsedPlaceholder = placeholderChars.join("");
|
|
333
|
+
}
|
|
334
|
+
const prefix = permanents.filter((position, index) => position === index).map((position) => parsedMask[position]).join("");
|
|
335
|
+
let lastEditablePosition = parsedMask.length - 1;
|
|
336
|
+
while (permanents.indexOf(lastEditablePosition) !== -1) {
|
|
337
|
+
lastEditablePosition--;
|
|
338
|
+
}
|
|
339
|
+
return {
|
|
340
|
+
maskPlaceholder: parsedPlaceholder,
|
|
341
|
+
prefix,
|
|
342
|
+
mask: parsedMask,
|
|
343
|
+
lastEditablePosition,
|
|
344
|
+
permanents
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// src/utils/mask.ts
|
|
349
|
+
var MaskUtils = class {
|
|
350
|
+
constructor(options) {
|
|
351
|
+
this.isCharacterAllowedAtPosition = (character, position) => {
|
|
352
|
+
const { maskPlaceholder } = this.maskOptions;
|
|
353
|
+
if (this.isCharacterFillingPosition(character, position)) {
|
|
354
|
+
return true;
|
|
355
|
+
}
|
|
356
|
+
if (!maskPlaceholder) {
|
|
357
|
+
return false;
|
|
358
|
+
}
|
|
359
|
+
return maskPlaceholder[position] === character;
|
|
360
|
+
};
|
|
361
|
+
this.isCharacterFillingPosition = (character, position) => {
|
|
362
|
+
const { mask } = this.maskOptions;
|
|
363
|
+
if (!character || position >= mask.length) {
|
|
364
|
+
return false;
|
|
365
|
+
}
|
|
366
|
+
if (!this.isPositionEditable(position)) {
|
|
367
|
+
return mask[position] === character;
|
|
368
|
+
}
|
|
369
|
+
const charRule = mask[position];
|
|
370
|
+
return new RegExp(charRule).test(character);
|
|
371
|
+
};
|
|
372
|
+
this.isPositionEditable = (position) => {
|
|
373
|
+
const { mask, permanents } = this.maskOptions;
|
|
374
|
+
return position < mask.length && permanents.indexOf(position) === -1;
|
|
375
|
+
};
|
|
376
|
+
this.isValueEmpty = (value) => {
|
|
377
|
+
return value.split("").every((character, position) => {
|
|
378
|
+
return !this.isPositionEditable(position) || !this.isCharacterFillingPosition(character, position);
|
|
379
|
+
});
|
|
380
|
+
};
|
|
381
|
+
this.isValueFilled = (value) => {
|
|
382
|
+
return this.getFilledLength(value) === this.maskOptions.lastEditablePosition + 1;
|
|
383
|
+
};
|
|
384
|
+
this.getDefaultSelectionForValue = (value) => {
|
|
385
|
+
const filledLength = this.getFilledLength(value);
|
|
386
|
+
const cursorPosition = this.getRightEditablePosition(filledLength);
|
|
387
|
+
return { start: cursorPosition, end: cursorPosition };
|
|
388
|
+
};
|
|
389
|
+
this.getFilledLength = (value) => {
|
|
390
|
+
const characters = value.split("");
|
|
391
|
+
const lastFilledIndex = findLastIndex(characters, (character, position) => {
|
|
392
|
+
return this.isPositionEditable(position) && this.isCharacterFillingPosition(character, position);
|
|
393
|
+
});
|
|
394
|
+
return lastFilledIndex + 1;
|
|
395
|
+
};
|
|
396
|
+
this.getStringFillingLengthAtPosition = (string, position) => {
|
|
397
|
+
const characters = string.split("");
|
|
398
|
+
const insertedValue = characters.reduce((value, character) => {
|
|
399
|
+
return this.insertCharacterAtPosition(value, character, value.length);
|
|
400
|
+
}, repeat(" ", position));
|
|
401
|
+
return insertedValue.length - position;
|
|
402
|
+
};
|
|
403
|
+
this.getLeftEditablePosition = (position) => {
|
|
404
|
+
for (let i = position; i >= 0; i--) {
|
|
405
|
+
if (this.isPositionEditable(i)) {
|
|
406
|
+
return i;
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
return null;
|
|
410
|
+
};
|
|
411
|
+
this.getRightEditablePosition = (position) => {
|
|
412
|
+
const { mask } = this.maskOptions;
|
|
413
|
+
for (let i = position; i < mask.length; i++) {
|
|
414
|
+
if (this.isPositionEditable(i)) {
|
|
415
|
+
return i;
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
return null;
|
|
419
|
+
};
|
|
420
|
+
this.formatValue = (value) => {
|
|
421
|
+
const { maskPlaceholder, mask } = this.maskOptions;
|
|
422
|
+
if (!maskPlaceholder) {
|
|
423
|
+
value = this.insertStringAtPosition("", value, 0);
|
|
424
|
+
while (value.length < mask.length && !this.isPositionEditable(value.length)) {
|
|
425
|
+
value += mask[value.length];
|
|
426
|
+
}
|
|
427
|
+
return value;
|
|
428
|
+
}
|
|
429
|
+
return this.insertStringAtPosition(maskPlaceholder, value, 0);
|
|
430
|
+
};
|
|
431
|
+
this.clearRange = (value, start, len) => {
|
|
432
|
+
if (!len) {
|
|
433
|
+
return value;
|
|
434
|
+
}
|
|
435
|
+
const end = start + len;
|
|
436
|
+
const { maskPlaceholder, mask } = this.maskOptions;
|
|
437
|
+
const clearedValue = value.split("").map((character, i) => {
|
|
438
|
+
const isEditable = this.isPositionEditable(i);
|
|
439
|
+
if (!maskPlaceholder && i >= end && !isEditable) {
|
|
440
|
+
return "";
|
|
441
|
+
}
|
|
442
|
+
if (i < start || i >= end) {
|
|
443
|
+
return character;
|
|
444
|
+
}
|
|
445
|
+
if (!isEditable) {
|
|
446
|
+
return mask[i];
|
|
447
|
+
}
|
|
448
|
+
if (maskPlaceholder) {
|
|
449
|
+
return maskPlaceholder[i];
|
|
450
|
+
}
|
|
451
|
+
return "";
|
|
452
|
+
}).join("");
|
|
453
|
+
return this.formatValue(clearedValue);
|
|
454
|
+
};
|
|
455
|
+
this.insertCharacterAtPosition = (value, character, position) => {
|
|
456
|
+
const { mask, maskPlaceholder } = this.maskOptions;
|
|
457
|
+
if (position >= mask.length) {
|
|
458
|
+
return value;
|
|
459
|
+
}
|
|
460
|
+
const isAllowed = this.isCharacterAllowedAtPosition(character, position);
|
|
461
|
+
const isEditable = this.isPositionEditable(position);
|
|
462
|
+
const nextEditablePosition = this.getRightEditablePosition(position);
|
|
463
|
+
const isNextPlaceholder = maskPlaceholder && nextEditablePosition ? character === maskPlaceholder[nextEditablePosition] : null;
|
|
464
|
+
const valueBefore = value.slice(0, position);
|
|
465
|
+
if (isAllowed || !isEditable) {
|
|
466
|
+
const insertedCharacter = isAllowed ? character : mask[position];
|
|
467
|
+
value = valueBefore + insertedCharacter;
|
|
468
|
+
}
|
|
469
|
+
if (!isAllowed && !isEditable && !isNextPlaceholder) {
|
|
470
|
+
value = this.insertCharacterAtPosition(value, character, position + 1);
|
|
471
|
+
}
|
|
472
|
+
return value;
|
|
473
|
+
};
|
|
474
|
+
this.insertStringAtPosition = (value, string, position) => {
|
|
475
|
+
const { mask, maskPlaceholder } = this.maskOptions;
|
|
476
|
+
if (!string || position >= mask.length) {
|
|
477
|
+
return value;
|
|
478
|
+
}
|
|
479
|
+
const characters = string.split("");
|
|
480
|
+
const isFixedLength = this.isValueFilled(value) || !!maskPlaceholder;
|
|
481
|
+
const valueAfter = value.slice(position);
|
|
482
|
+
value = characters.reduce((value2, character) => {
|
|
483
|
+
return this.insertCharacterAtPosition(value2, character, value2.length);
|
|
484
|
+
}, value.slice(0, position));
|
|
485
|
+
if (isFixedLength) {
|
|
486
|
+
value += valueAfter.slice(value.length - position);
|
|
487
|
+
} else if (this.isValueFilled(value)) {
|
|
488
|
+
value += mask.slice(value.length).join("");
|
|
489
|
+
} else {
|
|
490
|
+
const editableCharactersAfter = valueAfter.split("").filter((character, i) => {
|
|
491
|
+
return this.isPositionEditable(position + i);
|
|
492
|
+
});
|
|
493
|
+
value = editableCharactersAfter.reduce((value2, character) => {
|
|
494
|
+
const nextEditablePosition = this.getRightEditablePosition(
|
|
495
|
+
value2.length
|
|
496
|
+
);
|
|
497
|
+
if (nextEditablePosition === null) {
|
|
498
|
+
return value2;
|
|
499
|
+
}
|
|
500
|
+
if (!this.isPositionEditable(value2.length)) {
|
|
501
|
+
value2 += mask.slice(value2.length, nextEditablePosition).join("");
|
|
502
|
+
}
|
|
503
|
+
return this.insertCharacterAtPosition(value2, character, value2.length);
|
|
504
|
+
}, value);
|
|
505
|
+
}
|
|
506
|
+
return value;
|
|
507
|
+
};
|
|
508
|
+
this.processChange = (currentState, previousState) => {
|
|
509
|
+
const { mask, prefix, lastEditablePosition } = this.maskOptions;
|
|
510
|
+
const { value, selection } = currentState;
|
|
511
|
+
const previousValue = previousState.value;
|
|
512
|
+
const previousSelection = previousState.selection;
|
|
513
|
+
let newValue = value;
|
|
514
|
+
let enteredString = "";
|
|
515
|
+
let formattedEnteredStringLength = 0;
|
|
516
|
+
let removedLength = 0;
|
|
517
|
+
let cursorPosition = Math.min(previousSelection.start, selection.start);
|
|
518
|
+
if (selection.end > previousSelection.start) {
|
|
519
|
+
enteredString = newValue.slice(previousSelection.start, selection.end);
|
|
520
|
+
formattedEnteredStringLength = this.getStringFillingLengthAtPosition(
|
|
521
|
+
enteredString,
|
|
522
|
+
cursorPosition
|
|
523
|
+
);
|
|
524
|
+
if (!formattedEnteredStringLength) {
|
|
525
|
+
removedLength = 0;
|
|
526
|
+
} else {
|
|
527
|
+
removedLength = previousSelection.length;
|
|
528
|
+
}
|
|
529
|
+
} else if (newValue.length < previousValue.length) {
|
|
530
|
+
removedLength = previousValue.length - newValue.length;
|
|
531
|
+
}
|
|
532
|
+
newValue = previousValue;
|
|
533
|
+
if (removedLength) {
|
|
534
|
+
if (removedLength === 1 && !previousSelection.length) {
|
|
535
|
+
const deleteFromRight = previousSelection.start === selection.start;
|
|
536
|
+
cursorPosition = deleteFromRight ? this.getRightEditablePosition(selection.start) : this.getLeftEditablePosition(selection.start);
|
|
537
|
+
}
|
|
538
|
+
newValue = this.clearRange(newValue, cursorPosition, removedLength);
|
|
539
|
+
}
|
|
540
|
+
newValue = this.insertStringAtPosition(
|
|
541
|
+
newValue,
|
|
542
|
+
enteredString,
|
|
543
|
+
cursorPosition
|
|
544
|
+
);
|
|
545
|
+
cursorPosition += formattedEnteredStringLength;
|
|
546
|
+
if (cursorPosition >= mask.length) {
|
|
547
|
+
cursorPosition = mask.length;
|
|
548
|
+
} else if (cursorPosition < prefix.length && !formattedEnteredStringLength) {
|
|
549
|
+
cursorPosition = prefix.length;
|
|
550
|
+
} else if (cursorPosition >= prefix.length && cursorPosition < lastEditablePosition && formattedEnteredStringLength) {
|
|
551
|
+
cursorPosition = this.getRightEditablePosition(cursorPosition);
|
|
552
|
+
}
|
|
553
|
+
newValue = this.formatValue(newValue);
|
|
554
|
+
return {
|
|
555
|
+
value: newValue,
|
|
556
|
+
enteredString,
|
|
557
|
+
selection: { start: cursorPosition, end: cursorPosition }
|
|
558
|
+
};
|
|
559
|
+
};
|
|
560
|
+
this.maskOptions = parseMask(options);
|
|
561
|
+
}
|
|
562
|
+
};
|
|
563
|
+
|
|
564
|
+
// src/v2-compat.ts
|
|
565
|
+
var warned = /* @__PURE__ */ new Set();
|
|
566
|
+
function warnDeprecatedOnce(prop, message) {
|
|
567
|
+
if (process.env.NODE_ENV !== "production" && !warned.has(prop)) {
|
|
568
|
+
warned.add(prop);
|
|
569
|
+
console.warn(`react-input-mask-format: ${message}`);
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
function resolveMaskPlaceholder(maskPlaceholder, maskChar) {
|
|
573
|
+
if (maskPlaceholder !== void 0) {
|
|
574
|
+
return maskPlaceholder;
|
|
575
|
+
}
|
|
576
|
+
return maskChar;
|
|
577
|
+
}
|
|
578
|
+
function toRegExpFormatChars(formatChars) {
|
|
579
|
+
if (!formatChars) {
|
|
580
|
+
return void 0;
|
|
581
|
+
}
|
|
582
|
+
const result = {};
|
|
583
|
+
for (const key of Object.keys(formatChars)) {
|
|
584
|
+
result[key] = new RegExp(formatChars[key]);
|
|
585
|
+
}
|
|
586
|
+
return result;
|
|
587
|
+
}
|
|
588
|
+
function createBeforeMaskedStateChangeAdapter(beforeMaskedValueChange, maskOptions) {
|
|
589
|
+
return ({ previousState, currentState, nextState }) => {
|
|
590
|
+
var _a, _b;
|
|
591
|
+
return beforeMaskedValueChange(
|
|
592
|
+
nextState,
|
|
593
|
+
(_a = previousState != null ? previousState : currentState) != null ? _a : null,
|
|
594
|
+
(_b = nextState.enteredString) != null ? _b : null,
|
|
595
|
+
maskOptions
|
|
596
|
+
);
|
|
597
|
+
};
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
// src/index.tsx
|
|
601
|
+
var InputMask = forwardRef(function InputMask2(props, forwardedRef) {
|
|
602
|
+
const {
|
|
603
|
+
alwaysShowMask = false,
|
|
604
|
+
children,
|
|
605
|
+
mask,
|
|
606
|
+
maskPlaceholder: maskPlaceholderProp,
|
|
607
|
+
beforeMaskedStateChange: beforeMaskedStateChangeProp,
|
|
608
|
+
maskChar,
|
|
609
|
+
formatChars,
|
|
610
|
+
beforeMaskedValueChange,
|
|
611
|
+
...restProps
|
|
612
|
+
} = props;
|
|
613
|
+
if (maskChar !== void 0) {
|
|
614
|
+
warnDeprecatedOnce("maskChar", "maskChar is deprecated, use maskPlaceholder instead. See migration guide: https://github.com/Temirtator/react-input-mask-format#migrating-from-react-input-mask");
|
|
615
|
+
}
|
|
616
|
+
if (formatChars !== void 0) {
|
|
617
|
+
warnDeprecatedOnce("formatChars", "formatChars is deprecated, prefer an array mask with RegExps. See migration guide: https://github.com/Temirtator/react-input-mask-format#migrating-from-react-input-mask");
|
|
618
|
+
}
|
|
619
|
+
if (beforeMaskedValueChange !== void 0) {
|
|
620
|
+
warnDeprecatedOnce("beforeMaskedValueChange", "beforeMaskedValueChange is deprecated, use beforeMaskedStateChange. See migration guide: https://github.com/Temirtator/react-input-mask-format#migrating-from-react-input-mask");
|
|
621
|
+
}
|
|
622
|
+
const resolvedPlaceholder = resolveMaskPlaceholder(maskPlaceholderProp, maskChar);
|
|
623
|
+
const maskPlaceholder = resolvedPlaceholder === void 0 ? "_" : resolvedPlaceholder;
|
|
624
|
+
validateMaxLength(props);
|
|
625
|
+
validateMaskPlaceholder({ ...props, maskPlaceholder });
|
|
626
|
+
const maskUtils = new MaskUtils({
|
|
627
|
+
mask,
|
|
628
|
+
maskPlaceholder,
|
|
629
|
+
formatChars: toRegExpFormatChars(formatChars)
|
|
630
|
+
});
|
|
631
|
+
let beforeMaskedStateChange = beforeMaskedStateChangeProp;
|
|
632
|
+
if (!beforeMaskedStateChange && beforeMaskedValueChange) {
|
|
633
|
+
const v2MaskOptions = {
|
|
634
|
+
mask,
|
|
635
|
+
maskChar: maskPlaceholder,
|
|
636
|
+
alwaysShowMask,
|
|
637
|
+
formatChars: formatChars != null ? formatChars : { "9": "[0-9]", a: "[A-Za-z]", "*": "[A-Za-z0-9]" },
|
|
638
|
+
permanents: maskUtils.maskOptions.permanents
|
|
639
|
+
};
|
|
640
|
+
beforeMaskedStateChange = createBeforeMaskedStateChangeAdapter(
|
|
641
|
+
beforeMaskedValueChange,
|
|
642
|
+
v2MaskOptions
|
|
643
|
+
);
|
|
644
|
+
}
|
|
645
|
+
const isMasked = !!mask;
|
|
646
|
+
const isEditable = !restProps.disabled && !restProps.readOnly;
|
|
647
|
+
const isControlled = props.value !== null && props.value !== void 0;
|
|
648
|
+
const previousIsMasked = usePrevious(isMasked);
|
|
649
|
+
const initialValue = toString(
|
|
650
|
+
(isControlled ? props.value : props.defaultValue) || ""
|
|
651
|
+
);
|
|
652
|
+
const {
|
|
653
|
+
inputRef,
|
|
654
|
+
getInputState,
|
|
655
|
+
setInputState,
|
|
656
|
+
getLastInputState
|
|
657
|
+
} = useInputState(initialValue, isMasked);
|
|
658
|
+
const getInputElement = useInputElement(inputRef);
|
|
659
|
+
function onChange(event) {
|
|
660
|
+
const currentState = getInputState();
|
|
661
|
+
const previousState = getLastInputState();
|
|
662
|
+
let newInputState = maskUtils.processChange(currentState, previousState);
|
|
663
|
+
if (beforeMaskedStateChange) {
|
|
664
|
+
newInputState = beforeMaskedStateChange({
|
|
665
|
+
currentState,
|
|
666
|
+
previousState,
|
|
667
|
+
nextState: newInputState
|
|
668
|
+
});
|
|
669
|
+
}
|
|
670
|
+
setInputState(newInputState);
|
|
671
|
+
if (props.onChange) {
|
|
672
|
+
props.onChange(event);
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
function onFocus(event) {
|
|
676
|
+
inputRef.current = event.target;
|
|
677
|
+
const currentValue = getInputState().value;
|
|
678
|
+
if (isMasked && !maskUtils.isValueFilled(currentValue)) {
|
|
679
|
+
let newValue = maskUtils.formatValue(currentValue);
|
|
680
|
+
let newSelection = maskUtils.getDefaultSelectionForValue(newValue);
|
|
681
|
+
let newInputState = {
|
|
682
|
+
value: newValue,
|
|
683
|
+
selection: newSelection
|
|
684
|
+
};
|
|
685
|
+
if (beforeMaskedStateChange) {
|
|
686
|
+
newInputState = beforeMaskedStateChange({
|
|
687
|
+
currentState: getInputState(),
|
|
688
|
+
nextState: newInputState
|
|
689
|
+
});
|
|
690
|
+
newValue = newInputState.value;
|
|
691
|
+
newSelection = newInputState.selection;
|
|
692
|
+
}
|
|
693
|
+
setInputState(newInputState);
|
|
694
|
+
if (newValue !== currentValue && props.onChange) {
|
|
695
|
+
props.onChange(event);
|
|
696
|
+
}
|
|
697
|
+
defer(() => {
|
|
698
|
+
setInputState(getLastInputState());
|
|
699
|
+
});
|
|
700
|
+
}
|
|
701
|
+
if (props.onFocus) {
|
|
702
|
+
props.onFocus(event);
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
function onBlur(event) {
|
|
706
|
+
const currentValue = getInputState().value;
|
|
707
|
+
const lastValue = getLastInputState().value;
|
|
708
|
+
if (isMasked && !alwaysShowMask && maskUtils.isValueEmpty(lastValue)) {
|
|
709
|
+
let newValue = "";
|
|
710
|
+
let newInputState = {
|
|
711
|
+
value: newValue,
|
|
712
|
+
selection: { start: null, end: null }
|
|
713
|
+
};
|
|
714
|
+
if (beforeMaskedStateChange) {
|
|
715
|
+
newInputState = beforeMaskedStateChange({
|
|
716
|
+
currentState: getInputState(),
|
|
717
|
+
nextState: newInputState
|
|
718
|
+
});
|
|
719
|
+
newValue = newInputState.value;
|
|
720
|
+
}
|
|
721
|
+
setInputState(newInputState);
|
|
722
|
+
if (newValue !== currentValue && props.onChange) {
|
|
723
|
+
props.onChange(event);
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
if (props.onBlur) {
|
|
727
|
+
props.onBlur(event);
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
function onMouseDown(event) {
|
|
731
|
+
const input = getInputElement();
|
|
732
|
+
const { value } = getInputState();
|
|
733
|
+
const inputDocument = getElementDocument(input);
|
|
734
|
+
if (!isInputFocused(input) && !maskUtils.isValueFilled(value)) {
|
|
735
|
+
const mouseDownX = event.clientX;
|
|
736
|
+
const mouseDownY = event.clientY;
|
|
737
|
+
const mouseDownTime = (/* @__PURE__ */ new Date()).getTime();
|
|
738
|
+
const mouseUpHandler = (mouseUpEvent) => {
|
|
739
|
+
inputDocument.removeEventListener("mouseup", mouseUpHandler);
|
|
740
|
+
if (!isInputFocused(input)) {
|
|
741
|
+
return;
|
|
742
|
+
}
|
|
743
|
+
const deltaX = Math.abs(mouseUpEvent.clientX - mouseDownX);
|
|
744
|
+
const deltaY = Math.abs(mouseUpEvent.clientY - mouseDownY);
|
|
745
|
+
const axisDelta = Math.max(deltaX, deltaY);
|
|
746
|
+
const timeDelta = (/* @__PURE__ */ new Date()).getTime() - mouseDownTime;
|
|
747
|
+
if (axisDelta <= 10 && timeDelta <= 200 || axisDelta <= 5 && timeDelta <= 300) {
|
|
748
|
+
const lastState = getLastInputState();
|
|
749
|
+
const newSelection = maskUtils.getDefaultSelectionForValue(
|
|
750
|
+
lastState.value
|
|
751
|
+
);
|
|
752
|
+
const newState = {
|
|
753
|
+
...lastState,
|
|
754
|
+
selection: newSelection
|
|
755
|
+
};
|
|
756
|
+
setInputState(newState);
|
|
757
|
+
}
|
|
758
|
+
};
|
|
759
|
+
inputDocument.addEventListener("mouseup", mouseUpHandler);
|
|
760
|
+
}
|
|
761
|
+
if (props.onMouseDown) {
|
|
762
|
+
props.onMouseDown(event);
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
let renderValue = props.value;
|
|
766
|
+
if (isMasked && isControlled) {
|
|
767
|
+
const input = getInputElement();
|
|
768
|
+
const isFocused = !!input && isInputFocused(input);
|
|
769
|
+
let newValue = isFocused || alwaysShowMask || props.value ? maskUtils.formatValue(props.value) : props.value;
|
|
770
|
+
if (beforeMaskedStateChange) {
|
|
771
|
+
newValue = beforeMaskedStateChange({
|
|
772
|
+
nextState: { value: newValue, selection: { start: null, end: null } }
|
|
773
|
+
}).value;
|
|
774
|
+
}
|
|
775
|
+
renderValue = newValue;
|
|
776
|
+
}
|
|
777
|
+
const lastSelection = getLastInputState().selection;
|
|
778
|
+
useLayoutEffect2(() => {
|
|
779
|
+
if (isMasked && isControlled) {
|
|
780
|
+
setInputState({
|
|
781
|
+
...getLastInputState(),
|
|
782
|
+
value: renderValue
|
|
783
|
+
});
|
|
784
|
+
}
|
|
785
|
+
});
|
|
786
|
+
useLayoutEffect2(() => {
|
|
787
|
+
if (!isMasked) {
|
|
788
|
+
return;
|
|
789
|
+
}
|
|
790
|
+
const input = getInputElement();
|
|
791
|
+
const isFocused = isInputFocused(input);
|
|
792
|
+
const previousSelection = lastSelection;
|
|
793
|
+
const currentState = getInputState();
|
|
794
|
+
let newInputState = { ...currentState };
|
|
795
|
+
if (!isControlled) {
|
|
796
|
+
const currentValue = currentState.value;
|
|
797
|
+
const formattedValue = maskUtils.formatValue(currentValue);
|
|
798
|
+
const isValueEmpty = maskUtils.isValueEmpty(formattedValue);
|
|
799
|
+
const shouldFormatValue = !isValueEmpty || isFocused || alwaysShowMask;
|
|
800
|
+
if (shouldFormatValue) {
|
|
801
|
+
newInputState.value = formattedValue;
|
|
802
|
+
} else if (isValueEmpty && !isFocused) {
|
|
803
|
+
newInputState.value = "";
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
if (isFocused && !previousIsMasked) {
|
|
807
|
+
newInputState.selection = maskUtils.getDefaultSelectionForValue(
|
|
808
|
+
newInputState.value
|
|
809
|
+
);
|
|
810
|
+
} else if (isControlled && isFocused && previousSelection) {
|
|
811
|
+
if (previousSelection.start !== null && previousSelection.end !== null) {
|
|
812
|
+
newInputState.selection = previousSelection;
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
if (beforeMaskedStateChange) {
|
|
816
|
+
newInputState = beforeMaskedStateChange({
|
|
817
|
+
currentState,
|
|
818
|
+
nextState: newInputState
|
|
819
|
+
});
|
|
820
|
+
}
|
|
821
|
+
setInputState(newInputState);
|
|
822
|
+
});
|
|
823
|
+
const inputProps = {
|
|
824
|
+
...restProps,
|
|
825
|
+
onFocus,
|
|
826
|
+
onBlur,
|
|
827
|
+
onChange: isMasked && isEditable ? onChange : props.onChange,
|
|
828
|
+
onMouseDown: isMasked && isEditable ? onMouseDown : props.onMouseDown,
|
|
829
|
+
// `ref` may target either the plain <input> DOM node or, when custom
|
|
830
|
+
// children are used, whatever node the child's own ref forwards to.
|
|
831
|
+
ref: (ref) => {
|
|
832
|
+
inputRef.current = ref;
|
|
833
|
+
if (isFunction(forwardedRef)) {
|
|
834
|
+
forwardedRef(ref);
|
|
835
|
+
} else if (forwardedRef !== null && typeof forwardedRef === "object") {
|
|
836
|
+
forwardedRef.current = ref;
|
|
837
|
+
}
|
|
838
|
+
},
|
|
839
|
+
value: isMasked && isControlled ? renderValue : props.value
|
|
840
|
+
};
|
|
841
|
+
if (children) {
|
|
842
|
+
validateChildren(props, children);
|
|
843
|
+
return React.cloneElement(children, inputProps);
|
|
844
|
+
}
|
|
845
|
+
return /* @__PURE__ */ React.createElement("input", { ...inputProps });
|
|
846
|
+
});
|
|
847
|
+
InputMask.displayName = "InputMask";
|
|
848
|
+
var src_default = InputMask;
|
|
849
|
+
export {
|
|
850
|
+
src_default as default
|
|
851
|
+
};
|
|
852
|
+
//# sourceMappingURL=index.js.map
|