All files / src/components MultilineTextField.tsx

0% Statements 0/21
0% Branches 0/16
0% Functions 0/7
0% Lines 0/20

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76                                                                                                                                                       
import { useEffect, useRef, useState } from "react";
import styles from "./TextField.module.css";
 
export function MultilineTextField({
  value = "",
  setValue,
  placeholder,
  className,
  id,
  ariaLabel,
  invalid = false,
  minRows = 3,
}: {
  value: string;
  setValue: (value: string) => void;
  placeholder?: string;
  className?: string;
  id?: string;
  ariaLabel?: string;
  invalid?: boolean;
  minRows?: number;
}) {
  const [readOnly, setReadOnly] = useState(true);
  const [inputValue, setInputValue] = useState(value);
  const textareaRef = useRef<HTMLTextAreaElement>(null);
 
  useEffect(() => {
    setInputValue(value);
  }, [value]);
 
  // Auto-grow logic
  useEffect(() => {
    const el = textareaRef.current;
    if (!el) return;
 
    el.style.height = "auto";
    el.style.height = `${el.scrollHeight}px`;
  }, [inputValue]);
 
  const onCommit = () => {
    setReadOnly(true);
    setValue(inputValue);
  };
 
  const onKeyDown = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {
    if (e.key === "Enter" && (e.ctrlKey || e.metaKey)) {
      e.preventDefault();
      (e.target as HTMLTextAreaElement).blur();
    }
  };
 
  return (
    <textarea
      ref={textareaRef}
      rows={minRows}
      placeholder={placeholder}
      value={inputValue}
      onChange={(e) => setInputValue(e.target.value)}
      onFocus={() => setReadOnly(false)}
      onBlur={onCommit}
      onKeyDown={onKeyDown}
      readOnly={readOnly}
      id={id}
      aria-label={ariaLabel}
      className={`
        ${readOnly ? "drag" : "nodrag"}
        flex-1
        ${styles.textField}
        ${styles.multiline}
        ${invalid ? styles.invalid : ""}
        ${className ?? ""}
      `}
    />
  );
}