Merge branch 'feat/inda-383-daily-stat' into 'main'
[ProtonMail-WebClient.git] / packages / pass / components / Form / Field / TextareaField.tsx
blob914b706014ef910536e7ced21c4195a0e3f5ef42
1 import type { ForwardRefRenderFunction } from 'react';
2 import { type FC, forwardRef, useMemo, useState } from 'react';
4 import { type FieldProps } from 'formik';
6 import { InputFieldTwo, TextAreaTwo } from '@proton/components';
7 import { type InputFieldProps } from '@proton/components/components/v2/field/InputField';
8 import { pipe } from '@proton/pass/utils/fp/pipe';
9 import { isEmptyString } from '@proton/pass/utils/string/is-empty-string';
10 import clsx from '@proton/utils/clsx';
12 import { useFieldControl } from '../../../hooks/useFieldControl';
13 import { useMaxLengthLimiter } from '../../../hooks/useMaxLengthLimiter';
14 import { usePasteLengthLimiter } from '../../../hooks/usePasteLengthLimiter';
15 import { FieldBox, type FieldBoxProps } from './Layout/FieldBox';
17 export type BaseTextAreaFieldProps = FieldProps & InputFieldProps<typeof TextAreaTwo>;
19 const DEFAULT_MIN_ROWS = 1;
20 const DEFAULT_MAX_ROWS = 5;
22 const BaseTextAreaFieldRender: ForwardRefRenderFunction<HTMLTextAreaElement, BaseTextAreaFieldProps> = (
23     { className, form, field, labelContainerClassName, meta, onKeyDown, onPaste, ...props },
24     ref
25 ) => {
26     const { error } = useFieldControl({ form, field, meta });
27     const pasteLengthLimiter = usePasteLengthLimiter();
28     const maxLengthLimiter = useMaxLengthLimiter();
30     const { value } = field;
31     const { minRows, rows } = props;
33     /* the nested `useAutoGrow` doesn't support initial row resolution
34      * on initial mount - we resort to a dynamic `minRows` prop */
35     const dynamicMinRows = useMemo(
36         () => Math.max(minRows ?? DEFAULT_MIN_ROWS, Math.min(value.split('\n').length, rows ?? DEFAULT_MAX_ROWS)),
37         [minRows, rows, value]
38     );
40     return (
41         <InputFieldTwo
42             as={TextAreaTwo}
43             ref={ref}
44             autoGrow
45             unstyled
46             assistContainerClassName="empty:hidden"
47             className={clsx(
48                 'border-none flex p-0 resize-none',
49                 props.disabled ? 'color-disabled' : 'color-norm',
50                 className
51             )}
52             error={error}
53             labelContainerClassName={clsx(
54                 'm-0 text-normal text-sm',
55                 error ? 'color-danger' : 'color-weak',
56                 labelContainerClassName
57             )}
58             rows={DEFAULT_MAX_ROWS}
59             {...field}
60             {...props}
61             minRows={dynamicMinRows}
62             onPaste={props.maxLength ? pasteLengthLimiter(props.maxLength, onPaste) : onPaste}
63             onKeyDown={props.maxLength ? maxLengthLimiter(props.maxLength, onKeyDown) : onKeyDown}
64         />
65     );
68 export const BaseTextAreaField = forwardRef(BaseTextAreaFieldRender);
70 export const BaseMaskedTextAreaField: FC<BaseTextAreaFieldProps> = ({ form, field, ...rest }) => {
71     const { value } = field;
72     const [masked, setMasked] = useState<boolean>(true);
73     const isEmpty = isEmptyString(value);
75     return (
76         <BaseTextAreaField
77             className={clsx(!isEmpty && masked && 'text-monospace')}
78             onFocus={() => setMasked(false)}
79             onBlur={pipe(field.onBlur, () => setMasked(true))}
80             form={form}
81             field={field}
82             rows={masked ? DEFAULT_MIN_ROWS : rest.rows}
83             value={!masked || isEmpty ? value : '•'.repeat(16)}
84             {...rest}
85         />
86     );
89 export type TextAreaFieldProps = FieldBoxProps & BaseTextAreaFieldProps;
91 export const TextAreaField: FC<TextAreaFieldProps> = ({
92     actions,
93     actionsContainerClassName,
94     className,
95     icon,
96     ...props
97 }) => (
98     <FieldBox actions={actions} actionsContainerClassName={actionsContainerClassName} className={className} icon={icon}>
99         <BaseTextAreaField {...props} />
100     </FieldBox>