import * as React from "react";
import classNames from "classnames";
import { AriaTextFieldProps, useTextField } from "react-aria";
import { useSetRef } from "#gdq/utils/RefUtils";
import { Control, ControlInputProps } from "../Control/Control";
import { useInputStyleClasses } from "../Input/Input";
import styles from "./TextArea.module.css";
function renderMaxLengthIndicator(length?: number, maxLength?: number) {
if (length == null || maxLength == null) return null;
const limitRatio = length / maxLength;
const nearLimit = limitRatio > 0.8 && limitRatio < 1.0;
const overLimit = limitRatio >= 1.0;
return (
{length} /
{maxLength}
);
}
const RESIZE_CLASSES = {
horizontal: styles.resizeHorizontal,
vertical: styles.resizeVertical,
both: styles.resizeBoth,
none: styles.resizeNone,
};
type TextAreaResizeType = keyof typeof RESIZE_CLASSES;
export interface TextAreaProps extends AriaTextFieldProps, ControlInputProps {
rows?: number;
resize?: TextAreaResizeType;
inputClassName?: string;
}
export const TextArea = React.forwardRef(function TextArea(
props: TextAreaProps,
ref: React.ForwardedRef,
) {
const { value, maxLength, rows = 3, resize = "vertical" } = props;
const inputStyles = useInputStyleClasses(props);
const areaRef = React.useRef(null);
const { labelProps, inputProps, descriptionProps, errorMessageProps } = useTextField(
{ ...props, inputElementType: "textarea" },
areaRef,
);
const setRef = useSetRef(areaRef, ref);
const [length, setLength] = React.useState(
() => areaRef.current?.value.length ?? value?.toString().length ?? 0,
);
React.useEffect(() => {
// For controlled inputs, just use the value's serialized length.
if (value != null) {
setLength(value.toString().length);
return;
}
// Otherwise, for uncontrolled inputs, set up a direct event handler to
// measure the length on change.
const area = areaRef.current;
if (area == null) return;
function measureLength(this: HTMLTextAreaElement) {
setLength(this.value.length);
}
area.addEventListener("input", measureLength);
return () => area.removeEventListener("input", measureLength);
}, [value]);
return (
{renderMaxLengthIndicator(length, maxLength)}
);
});