@evervault/react-native 2.2.1 → 2.4.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.
@@ -588,50 +588,6 @@ function useController(props) {
588
588
  }), [field, formState, fieldState]);
589
589
  }
590
590
 
591
- /**
592
- * Component based on `useController` hook to work with controlled component.
593
- *
594
- * @remarks
595
- * [API](https://react-hook-form.com/docs/usecontroller/controller) • [Demo](https://codesandbox.io/s/react-hook-form-v6-controller-ts-jwyzw) • [Video](https://www.youtube.com/watch?v=N2UNk_UCVyA)
596
- *
597
- * @param props - the path name to the form field value, and validation rules.
598
- *
599
- * @returns provide field handler functions, field and form state.
600
- *
601
- * @example
602
- * ```tsx
603
- * function App() {
604
- * const { control } = useForm<FormValues>({
605
- * defaultValues: {
606
- * test: ""
607
- * }
608
- * });
609
- *
610
- * return (
611
- * <form>
612
- * <Controller
613
- * control={control}
614
- * name="test"
615
- * render={({ field: { onChange, onBlur, value, ref }, formState, fieldState }) => (
616
- * <>
617
- * <input
618
- * onChange={onChange} // send value to hook form
619
- * onBlur={onBlur} // notify when input is touched
620
- * value={value} // return updated value
621
- * ref={ref} // set ref for focus management
622
- * />
623
- * <p>{formState.isSubmitted ? "submitted" : ""}</p>
624
- * <p>{fieldState.isTouched ? "touched" : ""}</p>
625
- * </>
626
- * )}
627
- * />
628
- * </form>
629
- * );
630
- * }
631
- * ```
632
- */
633
- const Controller = (props) => props.render(useController(props));
634
-
635
591
  var appendErrors = (name, validateAllFieldCriteria, errors, type, message) => validateAllFieldCriteria
636
592
  ? {
637
593
  ...errors[name],
@@ -6774,7 +6730,7 @@ const h = [
6774
6730
  isLocal: false,
6775
6731
  numberValidationRules: {
6776
6732
  luhnCheck: true,
6777
- ranges: [[300, 305], 36, 38, 39],
6733
+ ranges: [[300, 305], 3095, 36, 38, 39],
6778
6734
  lengths: [14, 16, 19]
6779
6735
  },
6780
6736
  securityCodeValidationRules: {
@@ -6786,7 +6742,7 @@ const h = [
6786
6742
  isLocal: false,
6787
6743
  numberValidationRules: {
6788
6744
  luhnCheck: true,
6789
- ranges: [6011, [644, 649], 65],
6745
+ ranges: [6011, [644, 649], [65e4, 651999], [653150, 659999], 622],
6790
6746
  lengths: [16, 19]
6791
6747
  },
6792
6748
  securityCodeValidationRules: {
@@ -6798,7 +6754,16 @@ const h = [
6798
6754
  isLocal: false,
6799
6755
  numberValidationRules: {
6800
6756
  luhnCheck: true,
6801
- ranges: [2131, 1800, [3528, 3589]],
6757
+ ranges: [
6758
+ 2131,
6759
+ 1800,
6760
+ [3088, 3094],
6761
+ [3096, 3102],
6762
+ [3112, 3120],
6763
+ [3158, 3159],
6764
+ [3337, 3349],
6765
+ [3528, 3589]
6766
+ ],
6802
6767
  lengths: [16, 17, 18, 19]
6803
6768
  },
6804
6769
  securityCodeValidationRules: {
@@ -6846,16 +6811,20 @@ const h = [
6846
6811
  numberValidationRules: {
6847
6812
  luhnCheck: true,
6848
6813
  ranges: [
6814
+ 5018,
6815
+ 5020,
6816
+ 5038,
6817
+ 5893,
6818
+ 6101,
6819
+ 6304,
6820
+ 6759,
6821
+ 6761,
6822
+ 6762,
6823
+ 6763,
6849
6824
  493698,
6850
6825
  [5e5, 504174],
6851
6826
  [504176, 506698],
6852
6827
  [506779, 508999],
6853
- [56, 59],
6854
- 60,
6855
- 61,
6856
- 63,
6857
- 64,
6858
- 67,
6859
6828
  69
6860
6829
  ],
6861
6830
  lengths: [12, 13, 14, 15, 16, 17, 18, 19]
@@ -6961,6 +6930,18 @@ const h = [
6961
6930
  securityCodeValidationRules: {
6962
6931
  lengths: [0]
6963
6932
  }
6933
+ },
6934
+ {
6935
+ name: "rupay",
6936
+ isLocal: false,
6937
+ numberValidationRules: {
6938
+ luhnCheck: true,
6939
+ ranges: [60, 81, 82, 508, [652100, 653149], [817200, 819899]],
6940
+ lengths: [16]
6941
+ },
6942
+ securityCodeValidationRules: {
6943
+ lengths: [3]
6944
+ }
6964
6945
  }
6965
6946
  ];
6966
6947
  function g(n, e, t) {
@@ -7027,7 +7008,7 @@ function C(n, e) {
7027
7008
  isValid: l
7028
7009
  };
7029
7010
  }
7030
- function b(n) {
7011
+ function R(n) {
7031
7012
  var r;
7032
7013
  const e = /^(0[1-9]|1[[0-2]).*$/, t = n.match(e), s = t ? parseInt(t[1].toString(), 10) : null, l = /^(0[1-9]|1[[0-2])(\d{2})$/, i = n.match(l), a = i ? parseInt(i[2].toString(), 10) : null;
7033
7014
  if (s) {
@@ -7093,7 +7074,7 @@ function areValuesComplete(values) {
7093
7074
  if ("number" in values && !f(values.number ?? "").isValid) {
7094
7075
  return false;
7095
7076
  }
7096
- if ("expiry" in values && !b(values.expiry ?? "").isValid) {
7077
+ if ("expiry" in values && !R(values.expiry ?? "").isValid) {
7097
7078
  return false;
7098
7079
  }
7099
7080
  if ("cvc" in values &&
@@ -7114,7 +7095,7 @@ function isAcceptedBrand(acceptedBrands, cardNumberValidationResult) {
7114
7095
  return isBrandAccepted || isLocalBrandAccepted;
7115
7096
  }
7116
7097
  function formatExpiry(expiry) {
7117
- const parsedExpiry = b(expiry);
7098
+ const parsedExpiry = R(expiry);
7118
7099
  if (!parsedExpiry.isValid) {
7119
7100
  return null;
7120
7101
  }
@@ -7137,7 +7118,7 @@ function getCardFormSchema(acceptedBrands) {
7137
7118
  expiry: z
7138
7119
  .string()
7139
7120
  .min(1, "Required")
7140
- .refine((value) => b(value).isValid, {
7121
+ .refine((value) => R(value).isValid, {
7141
7122
  message: "Invalid expiry",
7142
7123
  }),
7143
7124
  cvc: z
@@ -7383,44 +7364,69 @@ function useForwardedInputRef(ref) {
7383
7364
  return inputRef;
7384
7365
  }
7385
7366
  function mask(format) {
7386
- return format.split("").map((char) => {
7367
+ const maskArray = [];
7368
+ let isObfuscated = false;
7369
+ format.split("").forEach((char) => {
7370
+ if (char === "[") {
7371
+ isObfuscated = true;
7372
+ return;
7373
+ }
7374
+ else if (char === "]") {
7375
+ isObfuscated = false;
7376
+ return;
7377
+ }
7378
+ let value = char;
7387
7379
  if (char === "9") {
7388
- return /\d/;
7380
+ value = isObfuscated ? [/\d/] : /\d/;
7389
7381
  }
7390
- return char;
7382
+ maskArray.push(value);
7391
7383
  });
7384
+ return maskArray;
7392
7385
  }
7393
- const EvervaultInput = forwardRef(function EvervaultInput({ name, mask, ...props }, ref) {
7386
+ const EvervaultInput = forwardRef(function EvervaultInput({ name, mask, obfuscateValue, ...props }, ref) {
7394
7387
  const { validationMode } = useContext(EvervaultInputContext);
7395
7388
  const inputRef = useForwardedInputRef(ref);
7396
7389
  const methods = useFormContext();
7397
- return (jsx(Controller, { control: methods.control, name: name, shouldUnregister: true, render: ({ field, fieldState }) => (jsx(MaskInput
7390
+ const { field, fieldState } = useController({
7391
+ control: methods.control,
7392
+ name,
7393
+ shouldUnregister: true,
7394
+ });
7395
+ const obfuscationCharacter = useMemo(() => {
7396
+ if (typeof obfuscateValue === "string") {
7397
+ return obfuscateValue;
7398
+ }
7399
+ else {
7400
+ return "•";
7401
+ }
7402
+ }, [obfuscateValue]);
7403
+ return (jsx(MaskInput
7404
+ // Overridable props
7405
+ , {
7398
7406
  // Overridable props
7399
- , {
7400
- // Overridable props
7401
- id: field.name, ...props,
7402
- // Strict props
7403
- ref: mergeRefs(inputRef, field.ref), editable: !field.disabled && (props.editable ?? true), onBlur: (evt) => {
7404
- const shouldValidate = validationMode === "onBlur" ||
7405
- validationMode === "onTouched" ||
7406
- validationMode === "all";
7407
- methods.setValue(field.name, field.value, {
7408
- shouldDirty: true,
7409
- shouldTouch: true,
7410
- shouldValidate,
7411
- });
7412
- props.onBlur?.(evt);
7413
- }, mask: mask, maskAutoComplete: !!mask, value: field.value, onChangeText: (masked, unmasked) => {
7414
- const shouldValidate = (validationMode === "onTouched" && fieldState.isTouched) ||
7415
- ((validationMode === "onChange" || validationMode === "all") &&
7416
- (!!fieldState.error || fieldState.isTouched));
7417
- methods.setValue(field.name, unmasked, {
7418
- shouldDirty: true,
7419
- shouldValidate,
7420
- });
7421
- },
7422
- // Remove unwanted props
7423
- defaultValue: undefined, onChange: undefined })) }));
7407
+ id: field.name, ...props,
7408
+ // Strict props
7409
+ ref: mergeRefs(inputRef, field.ref), editable: !field.disabled && (props.editable ?? true), onBlur: (evt) => {
7410
+ const shouldValidate = validationMode === "onBlur" ||
7411
+ validationMode === "onTouched" ||
7412
+ validationMode === "all";
7413
+ methods.setValue(field.name, field.value, {
7414
+ shouldDirty: true,
7415
+ shouldTouch: true,
7416
+ shouldValidate,
7417
+ });
7418
+ props.onBlur?.(evt);
7419
+ }, mask: mask, maskAutoComplete: !!mask, obfuscationCharacter: obfuscationCharacter, showObfuscatedValue: !!obfuscateValue, value: field.value, onChangeText: (masked, unmasked) => {
7420
+ const shouldValidate = (validationMode === "onTouched" && fieldState.isTouched) ||
7421
+ ((validationMode === "onChange" || validationMode === "all") &&
7422
+ (!!fieldState.error || fieldState.isTouched));
7423
+ methods.setValue(field.name, unmasked, {
7424
+ shouldDirty: true,
7425
+ shouldValidate,
7426
+ });
7427
+ },
7428
+ // Remove unwanted props
7429
+ defaultValue: undefined, onChange: undefined }));
7424
7430
  });
7425
7431
 
7426
7432
  const DEFAULT_ACCEPTED_BRANDS = [];
@@ -7493,9 +7499,9 @@ const CardExpiry = forwardRef(function CardExpiry(props, ref) {
7493
7499
  return (jsx(EvervaultInput, { placeholder: "MM / YY", ...props, ref: ref, name: "expiry", mask: CARD_EXPIRY_MASK, inputMode: "numeric", autoComplete: "cc-exp", keyboardType: "number-pad" }));
7494
7500
  });
7495
7501
 
7496
- const DEFAULT_CARD_CVC_MASK = mask("999");
7502
+ const DEFAULT_CARD_CVC_MASK = mask("[999]");
7497
7503
  const CARD_CVC_MASKS = {
7498
- "american-express": mask("9999"),
7504
+ "american-express": mask("[9999]"),
7499
7505
  };
7500
7506
  const CardCvc = forwardRef(function CardCvc(props, ref) {
7501
7507
  const methods = useFormContext();
@@ -7513,10 +7519,10 @@ const CardCvc = forwardRef(function CardCvc(props, ref) {
7513
7519
  return (jsx(EvervaultInput, { placeholder: "CVC", ...props, ref: ref, name: "cvc", mask: mask, inputMode: "numeric", autoComplete: "cc-csc", keyboardType: "number-pad" }));
7514
7520
  });
7515
7521
 
7516
- const DEFAULT_CARD_NUMBER_MASK = mask("9999 9999 9999 9999");
7522
+ const DEFAULT_CARD_NUMBER_MASK = mask("9999 99[99 9999 9999]");
7517
7523
  const CARD_NUMBER_MASKS = {
7518
- unionpay: mask("9999 9999 9999 9999 999"),
7519
- "american-express": mask("9999 999999 99999"),
7524
+ unionpay: mask("9999 99[99 9999 9999 999]"),
7525
+ "american-express": mask("9999 99[9999 99999]"),
7520
7526
  };
7521
7527
  const CardNumber = forwardRef(function CardNumber(props, ref) {
7522
7528
  const mask = useCallback((text) => {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@evervault/react-native",
3
3
  "description": "Evervault SDK for React Native",
4
- "version": "2.2.1",
4
+ "version": "2.4.0",
5
5
  "source": "./src/index.ts",
6
6
  "main": "./build/index.cjs.js",
7
7
  "module": "./build/index.esm.js",
@@ -73,7 +73,7 @@
73
73
  "react-hook-form": "^7.54.2",
74
74
  "react-native-mask-input": "^1.2.3",
75
75
  "zod": "^3.24.2",
76
- "@evervault/card-validator": "1.3.0"
76
+ "@evervault/card-validator": "1.4.0"
77
77
  },
78
78
  "scripts": {
79
79
  "prebuild": "pnpm codegen",
package/src/Card/Cvc.tsx CHANGED
@@ -6,13 +6,20 @@ import { validateNumber } from "@evervault/card-validator";
6
6
  import { useFormContext } from "react-hook-form";
7
7
  import { CardBrandName } from "./types";
8
8
 
9
- const DEFAULT_CARD_CVC_MASK = mask("999");
9
+ const DEFAULT_CARD_CVC_MASK = mask("[999]");
10
10
 
11
11
  const CARD_CVC_MASKS: Partial<Record<CardBrandName, Mask>> = {
12
- "american-express": mask("9999"),
12
+ "american-express": mask("[9999]"),
13
13
  };
14
14
 
15
- export type CardCvcProps = BaseEvervaultInputProps;
15
+ export interface CardCvcProps extends BaseEvervaultInputProps {
16
+ /**
17
+ * Whether to obfuscate the entire CVC value.
18
+ *
19
+ * If a string is provided, it will be used to obfuscate the value.
20
+ */
21
+ obfuscateValue?: boolean | string;
22
+ }
16
23
 
17
24
  export type CardCvc = EvervaultInput;
18
25
 
@@ -13,7 +13,7 @@ function wrapper({ children }: PropsWithChildren) {
13
13
  }
14
14
 
15
15
  it("uses 16 digits for mask by default", async () => {
16
- const { getByTestId } = render(
16
+ const { rerender, getByTestId } = render(
17
17
  <Card>
18
18
  <CardNumber testID="number" />
19
19
  </Card>,
@@ -24,10 +24,17 @@ it("uses 16 digits for mask by default", async () => {
24
24
  const user = userEvent.setup();
25
25
  await user.type(number, "4242424242424242");
26
26
  expect(number).toHaveProp("value", "4242 4242 4242 4242");
27
+
28
+ rerender(
29
+ <Card>
30
+ <CardNumber testID="number" obfuscateValue />
31
+ </Card>
32
+ );
33
+ expect(number).toHaveProp("value", "4242 42•• •••• ••••");
27
34
  });
28
35
 
29
36
  it("uses 19 digits for mask for unionpay", async () => {
30
- const { getByTestId } = render(
37
+ const { rerender, getByTestId } = render(
31
38
  <Card>
32
39
  <CardNumber testID="number" />
33
40
  </Card>,
@@ -38,10 +45,17 @@ it("uses 19 digits for mask for unionpay", async () => {
38
45
  const user = userEvent.setup();
39
46
  await user.type(number, "6205500000000000004");
40
47
  expect(number).toHaveProp("value", "6205 5000 0000 0000 004");
48
+
49
+ rerender(
50
+ <Card>
51
+ <CardNumber testID="number" obfuscateValue />
52
+ </Card>
53
+ );
54
+ expect(number).toHaveProp("value", "6205 50•• •••• •••• •••");
41
55
  });
42
56
 
43
57
  it("uses 15 digits for mask for american express", async () => {
44
- const { getByTestId } = render(
58
+ const { rerender, getByTestId } = render(
45
59
  <Card>
46
60
  <CardNumber testID="number" />
47
61
  </Card>,
@@ -52,4 +66,11 @@ it("uses 15 digits for mask for american express", async () => {
52
66
  const user = userEvent.setup();
53
67
  await user.type(number, "371449635398431");
54
68
  expect(number).toHaveProp("value", "3714 496353 98431");
69
+
70
+ rerender(
71
+ <Card>
72
+ <CardNumber testID="number" obfuscateValue />
73
+ </Card>
74
+ );
75
+ expect(number).toHaveProp("value", "3714 49•••• •••••");
55
76
  });
@@ -5,14 +5,21 @@ import { MaskArray } from "react-native-mask-input";
5
5
  import { validateNumber } from "@evervault/card-validator";
6
6
  import { CardBrandName } from "./types";
7
7
 
8
- const DEFAULT_CARD_NUMBER_MASK = mask("9999 9999 9999 9999");
8
+ const DEFAULT_CARD_NUMBER_MASK = mask("9999 99[99 9999 9999]");
9
9
 
10
10
  const CARD_NUMBER_MASKS: Partial<Record<CardBrandName, MaskArray>> = {
11
- unionpay: mask("9999 9999 9999 9999 999"),
12
- "american-express": mask("9999 999999 99999"),
11
+ unionpay: mask("9999 99[99 9999 9999 999]"),
12
+ "american-express": mask("9999 99[9999 99999]"),
13
13
  };
14
14
 
15
- export type CardNumberProps = BaseEvervaultInputProps;
15
+ export interface CardNumberProps extends BaseEvervaultInputProps {
16
+ /**
17
+ * Whether to obfuscate the card number value (excluding the last 4 digits).
18
+ *
19
+ * If a string is provided, it will be used to obfuscate the value.
20
+ */
21
+ obfuscateValue?: boolean | string;
22
+ }
16
23
 
17
24
  export type CardNumber = EvervaultInput;
18
25
 
package/src/Card/types.ts CHANGED
@@ -13,6 +13,7 @@ export const CARD_BRAND_NAMES = [
13
13
  "hiper",
14
14
  "szep",
15
15
  "uatp",
16
+ "rupay",
16
17
  ] as const;
17
18
 
18
19
  export type CardBrandName = (typeof CARD_BRAND_NAMES)[number];
@@ -27,6 +27,25 @@ describe("mask", () => {
27
27
  /\d/,
28
28
  ]);
29
29
  });
30
+
31
+ it("should account for obfuscation", () => {
32
+ expect(mask("[9999 9999] 9999")).toEqual([
33
+ [/\d/],
34
+ [/\d/],
35
+ [/\d/],
36
+ [/\d/],
37
+ " ",
38
+ [/\d/],
39
+ [/\d/],
40
+ [/\d/],
41
+ [/\d/],
42
+ " ",
43
+ /\d/,
44
+ /\d/,
45
+ /\d/,
46
+ /\d/,
47
+ ]);
48
+ });
30
49
  });
31
50
 
32
51
  describe("EvervaultInput", () => {
@@ -333,4 +352,69 @@ describe("EvervaultInput", () => {
333
352
  { shouldDirty: true, shouldValidate: true }
334
353
  );
335
354
  });
355
+
356
+ it("should obfuscate the value when obfuscateValue=true", async () => {
357
+ const phoneMask = mask("[(999) 999]-9999");
358
+ const { rerender } = render(
359
+ <EvervaultInput testID="phone" mask={phoneMask} name="phone" />,
360
+ {
361
+ wrapper: Form,
362
+ }
363
+ );
364
+
365
+ const input = screen.getByTestId("phone");
366
+ const user = userEvent.setup();
367
+
368
+ await user.type(input, "1234567890");
369
+ expect(input).toHaveProp("value", "(123) 456-7890");
370
+
371
+ rerender(
372
+ <EvervaultInput
373
+ testID="phone"
374
+ mask={phoneMask}
375
+ name="phone"
376
+ obfuscateValue
377
+ />
378
+ );
379
+
380
+ await user.type(input, "1234567890");
381
+ expect(input).toHaveProp("value", "(•••) •••-7890");
382
+
383
+ rerender(
384
+ <EvervaultInput
385
+ testID="phone"
386
+ mask={phoneMask}
387
+ name="phone"
388
+ obfuscateValue="#"
389
+ />
390
+ );
391
+
392
+ await user.type(input, "1234567890");
393
+ expect(input).toHaveProp("value", "(###) ###-7890");
394
+
395
+ rerender(
396
+ <EvervaultInput
397
+ testID="phone"
398
+ mask={phoneMask}
399
+ name="phone"
400
+ obfuscateValue="🤔"
401
+ />
402
+ );
403
+
404
+ await user.type(input, "1234567890");
405
+ expect(input).toHaveProp("value", "(🤔🤔🤔) 🤔🤔🤔-7890");
406
+
407
+ const unobfuscatedMask = mask("(999) 999-9999");
408
+ rerender(
409
+ <EvervaultInput
410
+ testID="phone"
411
+ mask={unobfuscatedMask}
412
+ name="phone"
413
+ obfuscateValue
414
+ />
415
+ );
416
+
417
+ await user.type(input, "1234567890");
418
+ expect(input).toHaveProp("value", "(123) 456-7890");
419
+ });
336
420
  });
package/src/Input.tsx CHANGED
@@ -8,11 +8,13 @@ import {
8
8
  useCallback,
9
9
  useContext,
10
10
  useImperativeHandle,
11
+ useMemo,
11
12
  useRef,
13
+ useState,
12
14
  } from "react";
13
15
  import { TextInput, TextInputProps } from "react-native";
14
16
  import { mergeRefs } from "./utils";
15
- import { Controller, useFormContext } from "react-hook-form";
17
+ import { Controller, useController, useFormContext } from "react-hook-form";
16
18
  import MaskInput, { Mask, MaskArray } from "react-native-mask-input";
17
19
 
18
20
  export interface EvervaultInputContextValue {
@@ -82,73 +84,97 @@ export type BaseEvervaultInputProps = Omit<
82
84
  >;
83
85
 
84
86
  export function mask(format: string): MaskArray {
85
- return format.split("").map((char) => {
87
+ const maskArray: MaskArray = [];
88
+
89
+ let isObfuscated = false;
90
+ format.split("").forEach((char) => {
91
+ if (char === "[") {
92
+ isObfuscated = true;
93
+ return;
94
+ } else if (char === "]") {
95
+ isObfuscated = false;
96
+ return;
97
+ }
98
+
99
+ let value: string | RegExp | [RegExp] = char;
86
100
  if (char === "9") {
87
- return /\d/;
101
+ value = isObfuscated ? [/\d/] : /\d/;
88
102
  }
89
- return char;
103
+ maskArray.push(value);
90
104
  });
105
+
106
+ return maskArray;
91
107
  }
92
108
 
93
109
  export interface EvervaultInputProps<Values extends Record<string, unknown>>
94
110
  extends BaseEvervaultInputProps {
95
111
  name: keyof Values;
96
112
  mask?: Mask;
113
+ obfuscateValue?: boolean | string;
97
114
  }
98
115
 
99
116
  export const EvervaultInput = forwardRef<
100
117
  EvervaultInput,
101
118
  EvervaultInputProps<Record<string, unknown>>
102
- >(function EvervaultInput({ name, mask, ...props }, ref) {
119
+ >(function EvervaultInput({ name, mask, obfuscateValue, ...props }, ref) {
103
120
  const { validationMode } = useContext(EvervaultInputContext);
104
121
 
105
122
  const inputRef = useForwardedInputRef(ref);
106
123
 
107
124
  const methods = useFormContext();
108
125
 
126
+ const { field, fieldState } = useController({
127
+ control: methods.control,
128
+ name,
129
+ shouldUnregister: true,
130
+ });
131
+
132
+ const obfuscationCharacter = useMemo(() => {
133
+ if (typeof obfuscateValue === "string") {
134
+ return obfuscateValue;
135
+ } else {
136
+ return "•";
137
+ }
138
+ }, [obfuscateValue]);
139
+
109
140
  return (
110
- <Controller
111
- control={methods.control}
112
- name={name}
113
- shouldUnregister
114
- render={({ field, fieldState }) => (
115
- <MaskInput
116
- // Overridable props
117
- id={field.name}
118
- {...props}
119
- // Strict props
120
- ref={mergeRefs(inputRef, field.ref)}
121
- editable={!field.disabled && (props.editable ?? true)}
122
- onBlur={(evt) => {
123
- const shouldValidate =
124
- validationMode === "onBlur" ||
125
- validationMode === "onTouched" ||
126
- validationMode === "all";
127
- methods.setValue(field.name, field.value, {
128
- shouldDirty: true,
129
- shouldTouch: true,
130
- shouldValidate,
131
- });
132
- props.onBlur?.(evt);
133
- }}
134
- mask={mask}
135
- maskAutoComplete={!!mask}
136
- value={field.value}
137
- onChangeText={(masked, unmasked) => {
138
- const shouldValidate =
139
- (validationMode === "onTouched" && fieldState.isTouched) ||
140
- ((validationMode === "onChange" || validationMode === "all") &&
141
- (!!fieldState.error || fieldState.isTouched));
142
- methods.setValue(field.name, unmasked, {
143
- shouldDirty: true,
144
- shouldValidate,
145
- });
146
- }}
147
- // Remove unwanted props
148
- defaultValue={undefined}
149
- onChange={undefined}
150
- />
151
- )}
141
+ <MaskInput
142
+ // Overridable props
143
+ id={field.name}
144
+ {...props}
145
+ // Strict props
146
+ ref={mergeRefs(inputRef, field.ref)}
147
+ editable={!field.disabled && (props.editable ?? true)}
148
+ onBlur={(evt) => {
149
+ const shouldValidate =
150
+ validationMode === "onBlur" ||
151
+ validationMode === "onTouched" ||
152
+ validationMode === "all";
153
+ methods.setValue(field.name, field.value, {
154
+ shouldDirty: true,
155
+ shouldTouch: true,
156
+ shouldValidate,
157
+ });
158
+ props.onBlur?.(evt);
159
+ }}
160
+ mask={mask}
161
+ maskAutoComplete={!!mask}
162
+ obfuscationCharacter={obfuscationCharacter}
163
+ showObfuscatedValue={!!obfuscateValue}
164
+ value={field.value}
165
+ onChangeText={(masked, unmasked) => {
166
+ const shouldValidate =
167
+ (validationMode === "onTouched" && fieldState.isTouched) ||
168
+ ((validationMode === "onChange" || validationMode === "all") &&
169
+ (!!fieldState.error || fieldState.isTouched));
170
+ methods.setValue(field.name, unmasked, {
171
+ shouldDirty: true,
172
+ shouldValidate,
173
+ });
174
+ }}
175
+ // Remove unwanted props
176
+ defaultValue={undefined}
177
+ onChange={undefined}
152
178
  />
153
179
  );
154
180
  }) as <Values extends Record<string, unknown>>(