react-input-mask-format 2.0.0 → 2.2.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/dist/index.js CHANGED
@@ -1,3 +1,10 @@
1
+ import {
2
+ getInputSelection,
3
+ isInputFocused,
4
+ setInputSelection,
5
+ setNativeValue
6
+ } from "./chunk-CEZXZ7IJ.js";
7
+
1
8
  // src/index.tsx
2
9
  import React, { useLayoutEffect as useLayoutEffect2, forwardRef } from "react";
3
10
 
@@ -14,27 +21,6 @@ function cancelDefer(deferId) {
14
21
  }
15
22
  }
16
23
 
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
24
  // src/utils/helpers.ts
39
25
  function getElementDocument(element) {
40
26
  return element == null ? void 0 : element.ownerDocument;
@@ -231,6 +217,12 @@ var defaultFormatChars = {
231
217
  a: /[A-Za-z]/,
232
218
  "*": /[A-Za-z0-9]/
233
219
  };
220
+ var extendedFormatChars = {
221
+ ...defaultFormatChars,
222
+ A: /[A-Z]/,
223
+ "\u042F": /[А-Яа-яЁёӘәҒғҚқҢңӨөҰұҮүҺһІі]/,
224
+ "#": /[0-9A-Fa-f]/
225
+ };
234
226
 
235
227
  // src/validate-props.ts
236
228
  function warnDev(condition, message) {
@@ -457,13 +449,14 @@ var MaskUtils = class {
457
449
  if (position >= mask.length) {
458
450
  return value;
459
451
  }
460
- const isAllowed = this.isCharacterAllowedAtPosition(character, position);
452
+ const transformedCharacter = this.transform && this.isPositionEditable(position) ? this.transform(character, position) : character;
453
+ const isAllowed = this.isCharacterAllowedAtPosition(transformedCharacter, position);
461
454
  const isEditable = this.isPositionEditable(position);
462
455
  const nextEditablePosition = this.getRightEditablePosition(position);
463
- const isNextPlaceholder = maskPlaceholder && nextEditablePosition ? character === maskPlaceholder[nextEditablePosition] : null;
456
+ const isNextPlaceholder = maskPlaceholder && nextEditablePosition ? transformedCharacter === maskPlaceholder[nextEditablePosition] : null;
464
457
  const valueBefore = value.slice(0, position);
465
458
  if (isAllowed || !isEditable) {
466
- const insertedCharacter = isAllowed ? character : mask[position];
459
+ const insertedCharacter = isAllowed ? transformedCharacter : mask[position];
467
460
  value = valueBefore + insertedCharacter;
468
461
  }
469
462
  if (!isAllowed && !isEditable && !isNextPlaceholder) {
@@ -558,6 +551,7 @@ var MaskUtils = class {
558
551
  };
559
552
  };
560
553
  this.maskOptions = parseMask(options);
554
+ this.transform = options.transform;
561
555
  }
562
556
  };
563
557
 
@@ -575,15 +569,22 @@ function resolveMaskPlaceholder(maskPlaceholder, maskChar) {
575
569
  }
576
570
  return maskChar;
577
571
  }
578
- function toRegExpFormatChars(formatChars) {
572
+ function normalizeFormatChars(formatChars) {
579
573
  if (!formatChars) {
580
- return void 0;
574
+ return { formatChars: void 0, hasLegacyString: false };
581
575
  }
582
576
  const result = {};
577
+ let hasLegacyString = false;
583
578
  for (const key of Object.keys(formatChars)) {
584
- result[key] = new RegExp(formatChars[key]);
579
+ const value = formatChars[key];
580
+ if (typeof value === "string") {
581
+ hasLegacyString = true;
582
+ result[key] = new RegExp(value);
583
+ } else {
584
+ result[key] = value;
585
+ }
585
586
  }
586
- return result;
587
+ return { formatChars: result, hasLegacyString };
587
588
  }
588
589
  function createBeforeMaskedStateChangeAdapter(beforeMaskedValueChange, maskOptions) {
589
590
  return ({ previousState, currentState, nextState }) => {
@@ -597,6 +598,220 @@ function createBeforeMaskedStateChangeAdapter(beforeMaskedValueChange, maskOptio
597
598
  };
598
599
  }
599
600
 
601
+ // src/utils/transform.ts
602
+ function resolveTransform(transform) {
603
+ if (!transform) {
604
+ return void 0;
605
+ }
606
+ if (transform === "uppercase") {
607
+ return (char) => char.toUpperCase();
608
+ }
609
+ if (transform === "lowercase") {
610
+ return (char) => char.toLowerCase();
611
+ }
612
+ if (typeof transform === "function") {
613
+ return transform;
614
+ }
615
+ if (process.env.NODE_ENV !== "production") {
616
+ console.error(
617
+ `react-input-mask-format: invalid transform "${String(
618
+ transform
619
+ )}", expected "uppercase" | "lowercase" | (char, position) => char`
620
+ );
621
+ }
622
+ return void 0;
623
+ }
624
+
625
+ // src/use-mask.ts
626
+ import { useRef as useRef2, useCallback as useCallback2, useEffect as useEffect2 } from "react";
627
+
628
+ // src/bind-mask.ts
629
+ var IS_JSDOM = typeof navigator !== "undefined" && navigator.userAgent.includes("jsdom");
630
+ function createMaskController(maskUtils, options) {
631
+ let input = null;
632
+ let lastValue = "";
633
+ let lastSelection = { start: null, end: null };
634
+ function getInputState() {
635
+ return { value: input.value, selection: getInputSelection(input) };
636
+ }
637
+ function getLastInputState() {
638
+ return { value: lastValue, selection: lastSelection };
639
+ }
640
+ function setInputState({ value, selection }) {
641
+ setNativeValue(input, value);
642
+ lastValue = value;
643
+ if (input && isInputFocused(input) && selection.start !== null && selection.end !== null) {
644
+ setInputSelection(input, selection.start, selection.end);
645
+ lastSelection = getInputSelection(input);
646
+ } else {
647
+ lastSelection = selection;
648
+ }
649
+ if (IS_JSDOM) {
650
+ const resyncValue = value;
651
+ const resyncSelection = selection;
652
+ queueMicrotask(() => {
653
+ if (!input || input.value !== resyncValue) return;
654
+ input.value = resyncValue;
655
+ if (isInputFocused(input) && resyncSelection.start !== null && resyncSelection.end !== null) {
656
+ setInputSelection(input, resyncSelection.start, resyncSelection.end);
657
+ }
658
+ });
659
+ }
660
+ }
661
+ function handleInput() {
662
+ const currentState = getInputState();
663
+ const previousState = getLastInputState();
664
+ let nextState = maskUtils.processChange(currentState, previousState);
665
+ if (options.beforeMaskedStateChange) {
666
+ nextState = options.beforeMaskedStateChange({ currentState, previousState, nextState });
667
+ }
668
+ setInputState(nextState);
669
+ }
670
+ function handleFocus() {
671
+ const currentValue = getInputState().value;
672
+ if (!maskUtils.isValueFilled(currentValue)) {
673
+ const newValue = maskUtils.formatValue(currentValue);
674
+ const newSelection = maskUtils.getDefaultSelectionForValue(newValue);
675
+ let nextState = { value: newValue, selection: newSelection };
676
+ if (options.beforeMaskedStateChange) {
677
+ nextState = options.beforeMaskedStateChange({ currentState: getInputState(), nextState });
678
+ }
679
+ setInputState(nextState);
680
+ defer(() => {
681
+ if (input && isInputFocused(input)) {
682
+ setInputSelection(input, lastSelection.start, lastSelection.end);
683
+ }
684
+ });
685
+ }
686
+ }
687
+ function handleBlur() {
688
+ const lastValueOnBlur = getLastInputState().value;
689
+ if (!options.alwaysShowMask && maskUtils.isValueEmpty(lastValueOnBlur)) {
690
+ let nextState = { value: "", selection: { start: null, end: null } };
691
+ if (options.beforeMaskedStateChange) {
692
+ nextState = options.beforeMaskedStateChange({ currentState: getInputState(), nextState });
693
+ }
694
+ setInputState(nextState);
695
+ }
696
+ }
697
+ function handleMouseDown(event) {
698
+ if (!input) return;
699
+ const { value } = getInputState();
700
+ const inputDocument = getElementDocument(input);
701
+ if (!isInputFocused(input) && !maskUtils.isValueFilled(value)) {
702
+ const mouseDownX = event.clientX;
703
+ const mouseDownY = event.clientY;
704
+ const mouseDownTime = (/* @__PURE__ */ new Date()).getTime();
705
+ const mouseUpHandler = (mouseUpEvent) => {
706
+ inputDocument.removeEventListener("mouseup", mouseUpHandler);
707
+ if (!input || !isInputFocused(input)) return;
708
+ const deltaX = Math.abs(mouseUpEvent.clientX - mouseDownX);
709
+ const deltaY = Math.abs(mouseUpEvent.clientY - mouseDownY);
710
+ const axisDelta = Math.max(deltaX, deltaY);
711
+ const timeDelta = (/* @__PURE__ */ new Date()).getTime() - mouseDownTime;
712
+ if (axisDelta <= 10 && timeDelta <= 200 || axisDelta <= 5 && timeDelta <= 300) {
713
+ const newSelection = maskUtils.getDefaultSelectionForValue(lastValue);
714
+ if (newSelection.start !== null && newSelection.end !== null) {
715
+ setInputSelection(input, newSelection.start, newSelection.end);
716
+ lastSelection = getInputSelection(input);
717
+ }
718
+ }
719
+ };
720
+ inputDocument.addEventListener("mouseup", mouseUpHandler);
721
+ }
722
+ }
723
+ function bind(el) {
724
+ input = el;
725
+ lastValue = el.value;
726
+ lastSelection = getInputSelection(el);
727
+ const formatted = maskUtils.formatValue(el.value);
728
+ if (!maskUtils.isValueEmpty(formatted) || options.alwaysShowMask) {
729
+ setNativeValue(el, formatted);
730
+ lastValue = formatted;
731
+ }
732
+ el.addEventListener("input", handleInput);
733
+ el.addEventListener("focus", handleFocus);
734
+ el.addEventListener("blur", handleBlur);
735
+ el.addEventListener("mousedown", handleMouseDown);
736
+ }
737
+ function unbind() {
738
+ if (input) {
739
+ input.removeEventListener("input", handleInput);
740
+ input.removeEventListener("focus", handleFocus);
741
+ input.removeEventListener("blur", handleBlur);
742
+ input.removeEventListener("mousedown", handleMouseDown);
743
+ }
744
+ input = null;
745
+ }
746
+ function update(nextMaskUtils, nextOptions) {
747
+ maskUtils = nextMaskUtils;
748
+ options = nextOptions;
749
+ }
750
+ return { bind, unbind, update };
751
+ }
752
+
753
+ // src/use-mask.ts
754
+ function useMask(options) {
755
+ const {
756
+ mask,
757
+ maskPlaceholder: maskPlaceholderProp,
758
+ alwaysShowMask = false,
759
+ formatChars,
760
+ transform,
761
+ beforeMaskedStateChange: beforeMaskedStateChangeProp,
762
+ beforeMaskedValueChange
763
+ } = options;
764
+ if (beforeMaskedValueChange !== void 0) {
765
+ warnDeprecatedOnce(
766
+ "beforeMaskedValueChange",
767
+ "beforeMaskedValueChange is deprecated, use beforeMaskedStateChange."
768
+ );
769
+ }
770
+ const resolvedPlaceholder = resolveMaskPlaceholder(maskPlaceholderProp, void 0);
771
+ const maskPlaceholder = resolvedPlaceholder === void 0 ? "_" : resolvedPlaceholder;
772
+ const { formatChars: normalizedFormatChars, hasLegacyString } = normalizeFormatChars(formatChars);
773
+ if (hasLegacyString) {
774
+ warnDeprecatedOnce(
775
+ "formatChars",
776
+ "string values in formatChars are deprecated, pass RegExp values instead."
777
+ );
778
+ }
779
+ const maskUtils = new MaskUtils({
780
+ mask: mask != null ? mask : null,
781
+ maskPlaceholder,
782
+ formatChars: normalizedFormatChars,
783
+ transform: resolveTransform(transform)
784
+ });
785
+ let beforeMaskedStateChange = beforeMaskedStateChangeProp;
786
+ if (!beforeMaskedStateChange && beforeMaskedValueChange) {
787
+ const v2MaskOptions = {
788
+ mask: mask != null ? mask : void 0,
789
+ maskChar: maskPlaceholder,
790
+ alwaysShowMask,
791
+ formatChars: formatChars != null ? formatChars : { "9": "[0-9]", a: "[A-Za-z]", "*": "[A-Za-z0-9]" },
792
+ permanents: maskUtils.maskOptions.permanents
793
+ };
794
+ beforeMaskedStateChange = createBeforeMaskedStateChangeAdapter(beforeMaskedValueChange, v2MaskOptions);
795
+ }
796
+ const controllerOptions = { alwaysShowMask, beforeMaskedStateChange };
797
+ const controllerRef = useRef2(null);
798
+ useEffect2(() => {
799
+ var _a;
800
+ (_a = controllerRef.current) == null ? void 0 : _a.update(maskUtils, controllerOptions);
801
+ });
802
+ return useCallback2((el) => {
803
+ var _a;
804
+ if (el) {
805
+ const controller = createMaskController(maskUtils, controllerOptions);
806
+ controller.bind(el);
807
+ controllerRef.current = controller;
808
+ } else {
809
+ (_a = controllerRef.current) == null ? void 0 : _a.unbind();
810
+ controllerRef.current = null;
811
+ }
812
+ }, []);
813
+ }
814
+
600
815
  // src/index.tsx
601
816
  var InputMask = forwardRef(function InputMask2(props, forwardedRef) {
602
817
  const {
@@ -605,6 +820,7 @@ var InputMask = forwardRef(function InputMask2(props, forwardedRef) {
605
820
  mask,
606
821
  maskPlaceholder: maskPlaceholderProp,
607
822
  beforeMaskedStateChange: beforeMaskedStateChangeProp,
823
+ transform,
608
824
  maskChar,
609
825
  formatChars,
610
826
  beforeMaskedValueChange,
@@ -613,9 +829,6 @@ var InputMask = forwardRef(function InputMask2(props, forwardedRef) {
613
829
  if (maskChar !== void 0) {
614
830
  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
831
  }
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
832
  if (beforeMaskedValueChange !== void 0) {
620
833
  warnDeprecatedOnce("beforeMaskedValueChange", "beforeMaskedValueChange is deprecated, use beforeMaskedStateChange. See migration guide: https://github.com/Temirtator/react-input-mask-format#migrating-from-react-input-mask");
621
834
  }
@@ -623,10 +836,18 @@ var InputMask = forwardRef(function InputMask2(props, forwardedRef) {
623
836
  const maskPlaceholder = resolvedPlaceholder === void 0 ? "_" : resolvedPlaceholder;
624
837
  validateMaxLength(props);
625
838
  validateMaskPlaceholder({ ...props, maskPlaceholder });
839
+ const { formatChars: normalizedFormatChars, hasLegacyString } = normalizeFormatChars(formatChars);
840
+ if (hasLegacyString) {
841
+ warnDeprecatedOnce(
842
+ "formatChars",
843
+ 'string values in formatChars are deprecated, pass RegExp values instead (e.g. { "#": /[0-9]/ }). See migration guide: https://github.com/Temirtator/react-input-mask-format#migrating-from-react-input-mask'
844
+ );
845
+ }
626
846
  const maskUtils = new MaskUtils({
627
847
  mask,
628
848
  maskPlaceholder,
629
- formatChars: toRegExpFormatChars(formatChars)
849
+ formatChars: normalizedFormatChars,
850
+ transform: resolveTransform(transform)
630
851
  });
631
852
  let beforeMaskedStateChange = beforeMaskedStateChangeProp;
632
853
  if (!beforeMaskedStateChange && beforeMaskedValueChange) {
@@ -847,6 +1068,9 @@ var InputMask = forwardRef(function InputMask2(props, forwardedRef) {
847
1068
  InputMask.displayName = "InputMask";
848
1069
  var src_default = InputMask;
849
1070
  export {
850
- src_default as default
1071
+ src_default as default,
1072
+ defaultFormatChars,
1073
+ extendedFormatChars,
1074
+ useMask
851
1075
  };
852
1076
  //# sourceMappingURL=index.js.map