@spaced-out/ui-design-system
Version:
Sense UI components library
140 lines (128 loc) • 3.57 kB
Flow
// @flow strict
import * as React from 'react';
import {classify} from '../../utils/classify';
import {BodySmall, FormLabelSmall} from '../Text';
import css from './Textarea.module.css';
type ClassNames = $ReadOnly<{box?: string, textarea?: string}>;
export type TextareaProps = {
value?: string,
onChange?: (
evt: SyntheticInputEvent<HTMLInputElement>,
isEnter?: boolean,
) => mixed,
onFocus?: (e: SyntheticInputEvent<HTMLInputElement>) => mixed,
onBlur?: (e: SyntheticInputEvent<HTMLInputElement>) => mixed,
onKeyDown?: (e: SyntheticKeyboardEvent<HTMLInputElement>) => mixed,
name?: string,
disabled?: boolean,
placeholder?: string,
locked?: boolean,
error?: boolean,
errorText?: string,
label?: string | React.Node,
helperText?: string | React.Node,
classNames?: ClassNames,
size?: 'medium' | 'small',
required?: boolean,
textCountLimit?: number,
...
};
const Textarea_ = (props: TextareaProps, ref): React.Node => {
const {
value,
onChange,
onFocus,
onBlur,
name,
disabled,
placeholder,
error,
locked,
errorText,
label,
helperText,
classNames,
size = 'medium',
required,
textCountLimit,
...textareaProps
} = props;
const [textCountError, setTextCountError] = React.useState(false);
React.useEffect(() => {
if (textCountLimit) {
if (value && value.length > textCountLimit) {
setTextCountError(true);
} else {
setTextCountError(false);
}
}
}, [value]);
const controlledTextareaFilled = value !== '';
return (
<div
className={classify(css.wrapper, {
[css.filled]: controlledTextareaFilled ?? false,
[css.withError]: (error || textCountError) ?? false,
})}
>
{Boolean(label) && (
<div className={css.info}>
<div className={css.infoContent}>
<FormLabelSmall color="secondary">{label ?? ''}</FormLabelSmall>
{required && <FormLabelSmall color="danger">{'*'}</FormLabelSmall>}
</div>
<FormLabelSmall
color={error || textCountError ? 'danger' : 'secondary'}
>
{!!textCountLimit &&
((value && value.length) || 0) + '/' + textCountLimit}
</FormLabelSmall>
</div>
)}
<div
className={classify(
css.box,
{
[css.inputDisabled]: disabled ?? false,
[css.medium]: size === 'medium',
[css.small]: size === 'small',
[css.locked]: locked,
},
classNames?.box,
)}
>
<textarea
{...textareaProps}
disabled={locked || disabled}
name={name}
ref={ref}
placeholder={placeholder}
value={value}
onChange={onChange}
onFocus={onFocus}
onBlur={onBlur}
className={classNames?.textarea}
></textarea>
</div>
{(Boolean(helperText) || error) && (
<div className={css.info}>
<BodySmall
color={
error && errorText
? 'danger'
: disabled
? 'disabled'
: 'secondary'
}
>
{error && errorText ? errorText : helperText ?? ''}
</BodySmall>
</div>
)}
</div>
);
};
export const Textarea = (React.forwardRef<TextareaProps, HTMLTextAreaElement>(
Textarea_,
): React$AbstractComponent<TextareaProps, HTMLTextAreaElement>);