@evervault/react-native 2.3.0 → 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.
- package/build/Card/Cvc.d.ts +9 -2
- package/build/Card/Cvc.d.ts.map +1 -1
- package/build/Card/Number.d.ts +9 -2
- package/build/Card/Number.d.ts.map +1 -1
- package/build/Card/index.d.ts +2 -2
- package/build/Input.d.ts +1 -0
- package/build/Input.d.ts.map +1 -1
- package/build/index.cjs.js +60 -79
- package/build/index.cjs.js.map +1 -1
- package/build/index.esm.js +60 -79
- package/package.json +1 -1
- package/src/Card/Cvc.tsx +10 -3
- package/src/Card/Number.test.tsx +24 -3
- package/src/Card/Number.tsx +11 -4
- package/src/Input.test.tsx +84 -0
- package/src/Input.tsx +73 -47
package/build/index.esm.js
CHANGED
|
@@ -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],
|
|
@@ -7408,44 +7364,69 @@ function useForwardedInputRef(ref) {
|
|
|
7408
7364
|
return inputRef;
|
|
7409
7365
|
}
|
|
7410
7366
|
function mask(format) {
|
|
7411
|
-
|
|
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;
|
|
7412
7379
|
if (char === "9") {
|
|
7413
|
-
|
|
7380
|
+
value = isObfuscated ? [/\d/] : /\d/;
|
|
7414
7381
|
}
|
|
7415
|
-
|
|
7382
|
+
maskArray.push(value);
|
|
7416
7383
|
});
|
|
7384
|
+
return maskArray;
|
|
7417
7385
|
}
|
|
7418
|
-
const EvervaultInput = forwardRef(function EvervaultInput({ name, mask, ...props }, ref) {
|
|
7386
|
+
const EvervaultInput = forwardRef(function EvervaultInput({ name, mask, obfuscateValue, ...props }, ref) {
|
|
7419
7387
|
const { validationMode } = useContext(EvervaultInputContext);
|
|
7420
7388
|
const inputRef = useForwardedInputRef(ref);
|
|
7421
7389
|
const methods = useFormContext();
|
|
7422
|
-
|
|
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
|
+
, {
|
|
7423
7406
|
// Overridable props
|
|
7424
|
-
,
|
|
7425
|
-
|
|
7426
|
-
|
|
7427
|
-
|
|
7428
|
-
|
|
7429
|
-
|
|
7430
|
-
|
|
7431
|
-
|
|
7432
|
-
|
|
7433
|
-
|
|
7434
|
-
|
|
7435
|
-
|
|
7436
|
-
|
|
7437
|
-
|
|
7438
|
-
|
|
7439
|
-
|
|
7440
|
-
|
|
7441
|
-
|
|
7442
|
-
|
|
7443
|
-
|
|
7444
|
-
|
|
7445
|
-
|
|
7446
|
-
|
|
7447
|
-
// Remove unwanted props
|
|
7448
|
-
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 }));
|
|
7449
7430
|
});
|
|
7450
7431
|
|
|
7451
7432
|
const DEFAULT_ACCEPTED_BRANDS = [];
|
|
@@ -7518,9 +7499,9 @@ const CardExpiry = forwardRef(function CardExpiry(props, ref) {
|
|
|
7518
7499
|
return (jsx(EvervaultInput, { placeholder: "MM / YY", ...props, ref: ref, name: "expiry", mask: CARD_EXPIRY_MASK, inputMode: "numeric", autoComplete: "cc-exp", keyboardType: "number-pad" }));
|
|
7519
7500
|
});
|
|
7520
7501
|
|
|
7521
|
-
const DEFAULT_CARD_CVC_MASK = mask("999");
|
|
7502
|
+
const DEFAULT_CARD_CVC_MASK = mask("[999]");
|
|
7522
7503
|
const CARD_CVC_MASKS = {
|
|
7523
|
-
"american-express": mask("9999"),
|
|
7504
|
+
"american-express": mask("[9999]"),
|
|
7524
7505
|
};
|
|
7525
7506
|
const CardCvc = forwardRef(function CardCvc(props, ref) {
|
|
7526
7507
|
const methods = useFormContext();
|
|
@@ -7538,10 +7519,10 @@ const CardCvc = forwardRef(function CardCvc(props, ref) {
|
|
|
7538
7519
|
return (jsx(EvervaultInput, { placeholder: "CVC", ...props, ref: ref, name: "cvc", mask: mask, inputMode: "numeric", autoComplete: "cc-csc", keyboardType: "number-pad" }));
|
|
7539
7520
|
});
|
|
7540
7521
|
|
|
7541
|
-
const DEFAULT_CARD_NUMBER_MASK = mask("9999
|
|
7522
|
+
const DEFAULT_CARD_NUMBER_MASK = mask("9999 99[99 9999 9999]");
|
|
7542
7523
|
const CARD_NUMBER_MASKS = {
|
|
7543
|
-
unionpay: mask("9999
|
|
7544
|
-
"american-express": mask("9999
|
|
7524
|
+
unionpay: mask("9999 99[99 9999 9999 999]"),
|
|
7525
|
+
"american-express": mask("9999 99[9999 99999]"),
|
|
7545
7526
|
};
|
|
7546
7527
|
const CardNumber = forwardRef(function CardNumber(props, ref) {
|
|
7547
7528
|
const mask = useCallback((text) => {
|
package/package.json
CHANGED
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
|
|
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
|
|
package/src/Card/Number.test.tsx
CHANGED
|
@@ -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
|
});
|
package/src/Card/Number.tsx
CHANGED
|
@@ -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
|
|
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
|
|
12
|
-
"american-express": mask("9999
|
|
11
|
+
unionpay: mask("9999 99[99 9999 9999 999]"),
|
|
12
|
+
"american-express": mask("9999 99[9999 99999]"),
|
|
13
13
|
};
|
|
14
14
|
|
|
15
|
-
export
|
|
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/Input.test.tsx
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
101
|
+
value = isObfuscated ? [/\d/] : /\d/;
|
|
88
102
|
}
|
|
89
|
-
|
|
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
|
-
<
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
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>>(
|