@verifiedinc-public/shared-ui-elements 0.11.6 → 0.12.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.
Files changed (90) hide show
  1. package/README.md +41 -0
  2. package/package.json +6 -24
  3. package/src/components/Alert/Alert.tsx +8 -0
  4. package/src/components/Alert/FullWidthAlert.tsx +27 -0
  5. package/src/components/Alert/index.ts +2 -0
  6. package/src/components/Button/index.tsx +8 -0
  7. package/src/components/CredentialRequestsEditor/CredentialRequestsEditor.context.tsx +98 -0
  8. package/src/components/CredentialRequestsEditor/components/CredentialRequestsField.tsx +103 -0
  9. package/src/components/CredentialRequestsEditor/components/DataFieldAccordion.tsx +337 -0
  10. package/src/components/CredentialRequestsEditor/components/DataFieldDeleteModal.tsx +64 -0
  11. package/src/components/CredentialRequestsEditor/components/DataFieldDescription.tsx +68 -0
  12. package/src/components/CredentialRequestsEditor/components/DataFieldMandatory.tsx +84 -0
  13. package/src/components/CredentialRequestsEditor/components/DataFieldMulti.tsx +74 -0
  14. package/src/components/CredentialRequestsEditor/components/DataFieldOptionType.tsx +84 -0
  15. package/src/components/CredentialRequestsEditor/components/DataFieldSection.tsx +48 -0
  16. package/src/components/CredentialRequestsEditor/components/DataFieldUserInput.tsx +71 -0
  17. package/src/components/CredentialRequestsEditor/components/RadioOption.tsx +89 -0
  18. package/src/components/CredentialRequestsEditor/contexts/CredentialRequestFieldContext.tsx +36 -0
  19. package/src/components/CredentialRequestsEditor/index.tsx +15 -0
  20. package/src/components/CredentialRequestsEditor/types/compositeCredentialSchema.ts +1 -0
  21. package/src/components/CredentialRequestsEditor/types/credentialSchemasDto.ts +3 -0
  22. package/src/components/CredentialRequestsEditor/types/form.ts +28 -0
  23. package/src/components/CredentialRequestsEditor/types/mandatoryEnum.ts +5 -0
  24. package/src/components/CredentialRequestsEditor/utils/buildDataFieldValue.ts +65 -0
  25. package/src/components/CredentialRequestsEditor/utils/prettyField.ts +16 -0
  26. package/src/components/Image.tsx +10 -0
  27. package/src/components/QRCodeDisplay/index.tsx +50 -0
  28. package/src/components/RequiredLabel/index.tsx +15 -0
  29. package/src/components/TextField/index.tsx +8 -0
  30. package/src/components/Tip/index.tsx +18 -0
  31. package/src/components/Typography/index.tsx +8 -0
  32. package/src/components/When.tsx +28 -0
  33. package/src/components/form/CountrySelector.tsx +96 -0
  34. package/src/components/form/DataFieldClearAdornment.tsx +28 -0
  35. package/src/components/form/DateInput.tsx +117 -0
  36. package/src/components/form/DefaultInput.tsx +28 -0
  37. package/src/components/form/InputMask.tsx +41 -0
  38. package/src/components/form/OTPInput.tsx +246 -0
  39. package/src/components/form/PhoneInput.tsx +153 -0
  40. package/src/components/form/SSNInput.tsx +100 -0
  41. package/src/components/form/SelectInput.tsx +89 -0
  42. package/src/components/form/TextMaskCustom.tsx +48 -0
  43. package/src/components/form/index.ts +5 -0
  44. package/src/components/form/styles/input.ts +24 -0
  45. package/src/components/index.ts +10 -0
  46. package/src/components/terms/AcceptTermsNotice.tsx +27 -0
  47. package/src/components/terms/LegalLink.tsx +22 -0
  48. package/src/components/verified/VerifiedImage.tsx +272 -0
  49. package/src/components/verified/VerifiedIncLogo.tsx +11 -0
  50. package/src/components/verified/index.ts +2 -0
  51. package/src/hooks/index.ts +5 -0
  52. package/src/hooks/useCallbackRef.ts +22 -0
  53. package/src/hooks/useCopyToClipboard.ts +76 -0
  54. package/src/hooks/useDisclosure.ts +96 -0
  55. package/src/hooks/useLocalStorage.ts +24 -0
  56. package/src/hooks/usePrevious.ts +17 -0
  57. package/src/hooks/useQRCode.ts +62 -0
  58. package/src/index.ts +5 -0
  59. package/src/stories/components/Alert.stories.tsx +41 -0
  60. package/src/stories/components/Button.stories.ts +49 -0
  61. package/src/stories/components/CredentialRequestsEditor.stories.tsx +98 -0
  62. package/src/stories/components/QRCodeDisplay.stories.tsx +60 -0
  63. package/src/stories/components/TextField.stories.ts +59 -0
  64. package/src/stories/components/Typography.stories.ts +140 -0
  65. package/src/stories/components/VerifiedImage.stories.tsx +32 -0
  66. package/src/stories/components/form/DateInput.stories.ts +39 -0
  67. package/src/stories/components/form/OTPInput.stories.tsx +90 -0
  68. package/src/stories/components/form/PhoneInput.stories.tsx +34 -0
  69. package/src/stories/components/form/SSNInput.stories.ts +30 -0
  70. package/src/stories/components/form/SelectInput.stories.ts +39 -0
  71. package/src/stories/hooks/useCopyToClipboard.stories.tsx +45 -0
  72. package/src/styles/colors.ts +34 -0
  73. package/src/styles/index.ts +2 -0
  74. package/src/styles/theme.ts +209 -0
  75. package/src/utils/date.ts +41 -0
  76. package/src/utils/index.ts +5 -0
  77. package/src/utils/masks/index.ts +6 -0
  78. package/src/utils/omitProperty.ts +19 -0
  79. package/src/utils/phone.ts +76 -0
  80. package/src/utils/wrapPromise.ts +19 -0
  81. package/src/validations/birthDate.schema.ts +54 -0
  82. package/src/validations/date.schema.ts +10 -0
  83. package/src/validations/description.schema.ts +5 -0
  84. package/src/validations/email.schema.ts +3 -0
  85. package/src/validations/field.schema.ts +3 -0
  86. package/src/validations/index.ts +9 -0
  87. package/src/validations/phone.schema.ts +6 -0
  88. package/src/validations/ssn.schema.ts +6 -0
  89. package/src/validations/state.schema.ts +3 -0
  90. package/src/validations/unix.schema.ts +11 -0
@@ -0,0 +1,65 @@
1
+ import _ from 'lodash';
2
+ import { type CompositeCredentialSchema } from '../types/compositeCredentialSchema';
3
+ import { CredentialRequests } from '../types/form';
4
+ import { type CredentialSchemaDto } from '../types/credentialSchemasDto';
5
+ import { MandatoryEnum } from '../types/mandatoryEnum';
6
+
7
+ const isComposed = (schema: unknown): schema is CompositeCredentialSchema =>
8
+ Object.prototype.hasOwnProperty.call(schema || {}, 'anyOf') ||
9
+ Object.prototype.hasOwnProperty.call(schema || {}, 'allOf');
10
+
11
+ function extractTypes(
12
+ record: any,
13
+ result: string[] = [],
14
+ parentKeys: string[] = [],
15
+ ): string[] {
16
+ _.forOwn(record, (value, key) => {
17
+ // Check if the current key is $ref or $id and handle accordingly
18
+ if (key === '$ref' && typeof value === 'string') {
19
+ result.push(value);
20
+ } else if (
21
+ key === '$id' &&
22
+ typeof value === 'string' &&
23
+ _.some(parentKeys, (k) => ['allOf', 'anyOf', 'oneOf'].includes(k))
24
+ ) {
25
+ result.push(value);
26
+ }
27
+
28
+ // If the value is an object or array, recurse into it
29
+ if (_.isObject(value)) {
30
+ extractTypes(value, result, [...parentKeys, key]);
31
+ }
32
+ });
33
+
34
+ return result;
35
+ }
36
+
37
+ export function buildDataFieldValue(
38
+ type: string,
39
+ schema: CredentialSchemaDto['schemas'],
40
+ ): CredentialRequests {
41
+ const selectedSchema = schema[type];
42
+ const isComposedSchema = isComposed(selectedSchema);
43
+
44
+ if (isComposedSchema) {
45
+ return {
46
+ type,
47
+ mandatory: MandatoryEnum.NO,
48
+ description: '',
49
+ allowUserInput: true,
50
+ multi: false,
51
+ children: extractTypes(selectedSchema).map((item) =>
52
+ buildDataFieldValue(item, schema),
53
+ ),
54
+ };
55
+ }
56
+
57
+ return {
58
+ type,
59
+ mandatory: MandatoryEnum.NO,
60
+ description: '',
61
+ allowUserInput: true,
62
+ // We want to default to true if is email credential.
63
+ multi: type === 'EmailCredential',
64
+ };
65
+ }
@@ -0,0 +1,16 @@
1
+ // This pattern will split camel cased string out of the credential,
2
+ // when a camel word contain a sequence of uppercase character it is kept to maintain consistency of that word.
3
+ const schemaNamePattern = /([A-Z][a-z0-9]+)/gm;
4
+
5
+ // Format the camel cased text to a human-readable.
6
+ export const prettyField = (field: string): string =>
7
+ field
8
+ .split(schemaNamePattern)
9
+ .map((word) => {
10
+ if (word === 'Id') return 'ID';
11
+ if (word === 'Zip') return 'ZIP';
12
+ if (word === 'Ssn') return 'SSN';
13
+ return word;
14
+ })
15
+ .filter((e) => e !== 'Credential')
16
+ .join(' ');
@@ -0,0 +1,10 @@
1
+ import { Box, type BoxProps } from '@mui/material';
2
+
3
+ export interface ImageProps extends BoxProps {
4
+ src: string;
5
+ alt: string;
6
+ }
7
+
8
+ export const Image = ({ src, alt, ...props }: ImageProps): JSX.Element => {
9
+ return <Box src={src} alt={alt} {...props} component='img' />;
10
+ };
@@ -0,0 +1,50 @@
1
+ import { Box } from '@mui/material';
2
+
3
+ import { useQRCode } from '../../hooks';
4
+
5
+ export interface QRCodeDisplayProps {
6
+ data: string;
7
+ asset?: string;
8
+ svgSize?: number;
9
+ logoSize?: number;
10
+ fill?: string;
11
+ }
12
+
13
+ export function QRCodeDisplay({
14
+ data,
15
+ asset,
16
+ svgSize = 300,
17
+ logoSize = 0,
18
+ fill = '#000000',
19
+ }: QRCodeDisplayProps) {
20
+ const svg = useQRCode({
21
+ data: data,
22
+ size: svgSize,
23
+ imageSize: logoSize,
24
+ fill,
25
+ });
26
+
27
+ return (
28
+ <Box position='relative'>
29
+ <Box
30
+ display='flex'
31
+ sx={{ '& svg': { width: '100%', height: 'auto', aspectRatio: 1 } }}
32
+ dangerouslySetInnerHTML={{ __html: svg }}
33
+ />
34
+ <Box
35
+ component='img'
36
+ src={asset}
37
+ sx={{
38
+ position: 'absolute',
39
+ width: (logoSize / svgSize) * 100 + '%',
40
+ maxWidth: logoSize + 'px',
41
+ height: 'auto',
42
+ inset: 0,
43
+ aspectRatio: 1,
44
+ m: 'auto',
45
+ p: 0.5,
46
+ }}
47
+ />
48
+ </Box>
49
+ );
50
+ }
@@ -0,0 +1,15 @@
1
+ import { type PropsWithChildren } from 'react';
2
+ import { Box } from '@mui/material';
3
+
4
+ export function RequiredLabel(
5
+ props: Readonly<PropsWithChildren>,
6
+ ): React.JSX.Element {
7
+ return (
8
+ <span>
9
+ {props.children}
10
+ <Box component='span' color='error.main' sx={{ ml: 0.5 }}>
11
+ *
12
+ </Box>
13
+ </span>
14
+ );
15
+ }
@@ -0,0 +1,8 @@
1
+ import MUITextField from '@mui/material/TextField';
2
+ import type { TextFieldProps as MUITextFieldProps } from '@mui/material/TextField';
3
+
4
+ export type TextFieldProps = MUITextFieldProps;
5
+
6
+ export function TextField(props: TextFieldProps): JSX.Element {
7
+ return <MUITextField {...props} />;
8
+ }
@@ -0,0 +1,18 @@
1
+ import { type PropsWithChildren } from 'react';
2
+ import { IconButton, Tooltip } from '@mui/material';
3
+ import { Code } from '@mui/icons-material';
4
+
5
+ export function Tip({ children }: PropsWithChildren): React.JSX.Element {
6
+ return (
7
+ <Tooltip title={children} arrow enterTouchDelay={0}>
8
+ <IconButton
9
+ size='small'
10
+ onClick={(e) => {
11
+ e.stopPropagation();
12
+ }}
13
+ >
14
+ <Code />
15
+ </IconButton>
16
+ </Tooltip>
17
+ );
18
+ }
@@ -0,0 +1,8 @@
1
+ import MUITypography from '@mui/material/Typography';
2
+ import type { TypographyProps as MUITypographyProps } from '@mui/material/Typography';
3
+
4
+ export type TypographyProps = MUITypographyProps;
5
+
6
+ export function Typography(props: TypographyProps): JSX.Element {
7
+ return <MUITypography {...props} />;
8
+ }
@@ -0,0 +1,28 @@
1
+ import { type ReactNode } from 'react';
2
+
3
+ export interface WhenProps<T = unknown> {
4
+ value: T;
5
+ children: ReactNode | ((condition: NonNullable<T>) => ReactNode);
6
+ }
7
+
8
+ /**
9
+ * This component can be used when using value is truthy to render some other component,
10
+ * instead of using ternary operators you can use this.
11
+ *
12
+ * This is particularly useful when you have nested conditional rendering. Also, it allows for safer falsy conditions
13
+ * while in a ternary you can risk rendering falsy values in the react tree if you use `&&`.
14
+ * @param value
15
+ * @param children Children can be direct ReactNode or a function returning ReactNode, when a function,
16
+ * the value is being passed as param and is guarantee to be non-nullable excluding undefined and null from the inferred type.
17
+ * @constructor
18
+ */
19
+ export function When<T>({
20
+ value,
21
+ children,
22
+ }: WhenProps<T>): React.JSX.Element | null {
23
+ if (!value) return null;
24
+
25
+ if (typeof children === 'function') return <>{children(value)}</>;
26
+
27
+ return <>{children}</>;
28
+ }
@@ -0,0 +1,96 @@
1
+ import { useMemo, useState } from 'react';
2
+ import { Button, Menu, MenuItem } from '@mui/material';
3
+
4
+ import { KeyboardArrowDown } from '@mui/icons-material';
5
+ import {
6
+ countries,
7
+ getPhoneDataByFieldName,
8
+ sortByCountryName,
9
+ } from '../../utils/phone';
10
+
11
+ interface CountrySelectorProps {
12
+ value: string;
13
+ onChange: (value: string) => void;
14
+ shouldShowOnlyNorthAmericanCountries?: boolean;
15
+ }
16
+
17
+ /**
18
+ * Component that renders and allows to manage the desired phone country format.
19
+ * @constructor
20
+ */
21
+ export default function CountrySelector({
22
+ shouldShowOnlyNorthAmericanCountries = true,
23
+ ...props
24
+ }: Readonly<CountrySelectorProps>): React.JSX.Element {
25
+ const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
26
+ const open = Boolean(anchorEl);
27
+ const options = useMemo(() => {
28
+ // Remove Brazil from the list of countries, we allow only North American numbers.
29
+ let _countries = [
30
+ ...countries.filter((country) => country.countryCode !== 'BR'),
31
+ ];
32
+
33
+ // Show all countries in development mode.
34
+ if (!shouldShowOnlyNorthAmericanCountries) {
35
+ _countries = [...countries];
36
+ }
37
+
38
+ return [..._countries].sort(sortByCountryName);
39
+ }, [shouldShowOnlyNorthAmericanCountries]);
40
+
41
+ const handleClick = (event: React.MouseEvent<HTMLElement>): void => {
42
+ setAnchorEl(event.currentTarget);
43
+ };
44
+
45
+ const handleClose = (): void => {
46
+ setAnchorEl(null);
47
+ };
48
+
49
+ return (
50
+ <>
51
+ <Button
52
+ id='demo-customized-button'
53
+ aria-controls={open ? 'country-select-button' : undefined}
54
+ aria-haspopup='true'
55
+ aria-expanded={open ? 'true' : undefined}
56
+ variant='text'
57
+ disableElevation
58
+ onClick={handleClick}
59
+ endIcon={<KeyboardArrowDown />}
60
+ >
61
+ {getPhoneDataByFieldName('countryCode', props.value)?.emoji}
62
+ </Button>
63
+ <Menu
64
+ anchorEl={anchorEl}
65
+ open={open}
66
+ onClose={handleClose}
67
+ MenuListProps={{
68
+ 'aria-labelledby': 'country-select-button',
69
+ }}
70
+ slotProps={{
71
+ paper: {
72
+ style: {
73
+ maxHeight: 48 * 4.5,
74
+ width: '20ch',
75
+ minWidth: '300px',
76
+ },
77
+ },
78
+ }}
79
+ >
80
+ {/* spread to avoid mutating the original countries array with sort method */}
81
+ {options.map((country) => (
82
+ <MenuItem
83
+ role='menuitem'
84
+ key={country.countryCode}
85
+ onClick={() => {
86
+ props.onChange(country.countryCode);
87
+ handleClose();
88
+ }}
89
+ >
90
+ {country.emoji} {country.countryName}
91
+ </MenuItem>
92
+ ))}
93
+ </Menu>
94
+ </>
95
+ );
96
+ }
@@ -0,0 +1,28 @@
1
+ import { Close } from '@mui/icons-material';
2
+ import { InputAdornment, IconButton } from '@mui/material';
3
+
4
+ type DataFieldClearAdornmentProps = Readonly<{
5
+ onClick?: () => void;
6
+ handleClear: () => void;
7
+ }>;
8
+
9
+ export function DataFieldClearAdornment({
10
+ onClick,
11
+ handleClear,
12
+ }: DataFieldClearAdornmentProps): React.JSX.Element {
13
+ return (
14
+ <InputAdornment position='end'>
15
+ <IconButton
16
+ aria-label='clear value'
17
+ edge='end'
18
+ size='small'
19
+ onClick={() => {
20
+ handleClear();
21
+ onClick?.();
22
+ }}
23
+ >
24
+ <Close fontSize='small' />
25
+ </IconButton>
26
+ </InputAdornment>
27
+ );
28
+ }
@@ -0,0 +1,117 @@
1
+ import { Box, TextField } from '@mui/material';
2
+ import {
3
+ forwardRef,
4
+ useEffect,
5
+ useState,
6
+ type ChangeEventHandler,
7
+ } from 'react';
8
+ import {
9
+ formatDateMMDDYYYY,
10
+ getMaxDateInstance,
11
+ getMinDateInstance,
12
+ } from '../../utils/date';
13
+ import { masks } from '../../utils/masks';
14
+ import { USDateSchema } from '../../validations';
15
+ import { type TextFieldProps } from '../TextField';
16
+ import { InputMask } from './InputMask';
17
+ import { inputStyle } from './styles/input';
18
+
19
+ interface DateInputProps {
20
+ name?: string;
21
+ value?: string;
22
+ label?: string;
23
+ error?: boolean;
24
+ helperText?: string;
25
+ onChange?: (event: { target: { value: string } }) => void;
26
+ onBlur?: ChangeEventHandler<HTMLInputElement>;
27
+ disabled?: boolean;
28
+ allowFutureDates?: boolean;
29
+ }
30
+
31
+ /**
32
+ * The input with date format.
33
+ * @constructor
34
+ */
35
+ function DateInputComponent(
36
+ {
37
+ label = 'Date of Birth',
38
+ value = '',
39
+ error,
40
+ helperText,
41
+ onChange,
42
+ onBlur,
43
+ disabled,
44
+ allowFutureDates = true,
45
+ ...rest
46
+ }: Readonly<DateInputProps>,
47
+ ref: any,
48
+ ): React.JSX.Element {
49
+ // Arbitrary value to format the timestamp into human-readable date.
50
+ const [localValue, setLocalValue] = useState<string>(
51
+ value ? formatDateMMDDYYYY(value) : '',
52
+ );
53
+
54
+ useEffect(() => {
55
+ if (value === '') {
56
+ setLocalValue('');
57
+ }
58
+ }, [value]);
59
+
60
+ const minDateInstance = getMinDateInstance();
61
+ const maxDateInstance = getMaxDateInstance(allowFutureDates);
62
+
63
+ const textFieldStyle: TextFieldProps = {
64
+ ...inputStyle,
65
+ label,
66
+ error,
67
+ helperText,
68
+ inputProps: {
69
+ // Set the input mode to numeric.
70
+ inputMode: 'numeric',
71
+ // Tab index for each block.
72
+ tabIndex: 0,
73
+ // Mask type date.
74
+ mask: masks.DOB_MASK,
75
+ },
76
+ fullWidth: true,
77
+ };
78
+
79
+ return (
80
+ <Box width='100%'>
81
+ <InputMask
82
+ mask={masks.DOB_MASK}
83
+ maskPlaceholder={null}
84
+ disabled={disabled}
85
+ value={localValue}
86
+ onBlur={onBlur}
87
+ onChange={(e) => {
88
+ const value = e.target.value;
89
+ const valid = USDateSchema.safeParse(value);
90
+
91
+ // Update the facade input value, so it let user input wrong/right values.
92
+ setLocalValue(value);
93
+
94
+ if (!valid.success) {
95
+ // A way to make sure the data field state is invalid is to empty the value.
96
+ return onChange?.({ target: { value: '' } });
97
+ }
98
+
99
+ const date = new Date(value);
100
+ if (date < minDateInstance || date > maxDateInstance) {
101
+ // A way to make sure the data field state is invalid is to empty the value.
102
+ return onChange?.({ target: { value: '' } });
103
+ }
104
+
105
+ date.setUTCHours(12);
106
+
107
+ // The date is valid in the US date format and is in between the valid min-max date range.
108
+ onChange?.({ target: { value: String(+date) } });
109
+ }}
110
+ >
111
+ <TextField {...textFieldStyle} inputRef={ref} {...rest} />
112
+ </InputMask>
113
+ </Box>
114
+ );
115
+ }
116
+
117
+ export const DateInput = forwardRef(DateInputComponent);
@@ -0,0 +1,28 @@
1
+ import { forwardRef } from 'react';
2
+ import { inputStyle } from './styles/input';
3
+ import { TextField, type TextFieldProps } from '../TextField';
4
+
5
+ /**
6
+ * A wrapper around the MUI TextField component to encapsulate some common defaults.
7
+ * Primarily intended for use creating more specific Input components rather than for direct use.
8
+ *
9
+ * @param {TextFieldProps} props DefaultInput takes the same props as the MUI TextField component.
10
+ * It sets default values for variant ('outlined') and margin ('normal').
11
+ */
12
+ const DefaultInput = (
13
+ { variant = 'outlined', margin = 'normal', ...props }: TextFieldProps,
14
+ ref: any,
15
+ ): React.JSX.Element => {
16
+ return (
17
+ <TextField
18
+ inputRef={ref}
19
+ {...inputStyle}
20
+ variant={variant}
21
+ margin={margin}
22
+ fullWidth
23
+ {...props}
24
+ />
25
+ );
26
+ };
27
+
28
+ export default forwardRef(DefaultInput);
@@ -0,0 +1,41 @@
1
+ import { type ChangeEventHandler, type ReactNode } from 'react';
2
+ import RInputMask from '@mona-health/react-input-mask';
3
+
4
+ interface Selection {
5
+ start: string;
6
+ end: string;
7
+ }
8
+
9
+ interface State {
10
+ value: string;
11
+ selection: Selection;
12
+ }
13
+
14
+ interface BeforeMaskedStateChangeOptions {
15
+ previousState: State;
16
+ currentState: State;
17
+ nextState: State;
18
+ }
19
+
20
+ interface InputMaskProps {
21
+ // Custom render function for integration with other input components.
22
+ children: ReactNode;
23
+ // Mask format.
24
+ mask: string | Array<RegExp | string>;
25
+ // Input value.
26
+ value: string;
27
+ // Change event handler.
28
+ onChange: ChangeEventHandler<HTMLInputElement>;
29
+ // Function to modify value and selection before applying mask.
30
+ onBlur?: ChangeEventHandler<HTMLInputElement>;
31
+ beforeMaskedStateChange?: (options: BeforeMaskedStateChangeOptions) => void;
32
+ // Placeholder to cover unfilled parts of the mask, null to remove the default "_" placeholder.
33
+ maskPlaceholder?: string | null;
34
+ // Whether mask prefix and placeholder should be displayed when input is empty and has no focus.
35
+ alwaysShowMask?: boolean;
36
+ disabled?: boolean;
37
+ }
38
+
39
+ export function InputMask(props: Readonly<InputMaskProps>): React.JSX.Element {
40
+ return <RInputMask {...props} />;
41
+ }