// This program has been developed by students from the bachelor Computer Science at Utrecht // University within the Software Project course. // © Copyright Utrecht University (Department of Information and Computing Sciences) import {useEffect, useState} from "react"; import styles from "./TextField.module.css"; /** * A styled text input that updates its value **in real time** at every keystroke. * * Automatically toggles between read-only and editable modes to integrate with * drag-based UIs (like React Flow). Calls `onCommit` when editing is completed. * * @param props - Component properties. * @param props.value - The current text input value. * @param props.setValue - Callback invoked on every keystroke to update the value. * @param props.onCommit - Callback invoked when editing is finalized (on blur or Enter). * @param props.placeholder - Optional placeholder text displayed when the input is empty. * @param props.className - Optional additional CSS class names. * @param props.id - Optional unique HTML `id` for the input element. * @param props.ariaLabel - Optional ARIA label for accessibility. * @param props.invalid - If true, applies error styling to indicate invalid input. * * @returns A styled `` element that updates its value in real time. */ export function RealtimeTextField({ value = "", setValue, onCommit, placeholder, className, id, ariaLabel, invalid = false, } : { value: string, setValue: (value: string) => void, onCommit: () => void, placeholder?: string, className?: string, id?: string, ariaLabel?: string, invalid?: boolean, }) { /** Tracks whether the input is currently read-only (for drag compatibility). */ const [readOnly, setReadOnly] = useState(true); /** Finalizes editing and calls `onCommit` when the user exits the field. */ const updateData = () => { setReadOnly(true); onCommit(); }; /** Handles the Enter key — commits the input by triggering a blur event. */ const updateOnEnter = (event: React.KeyboardEvent) => { if (event.key === "Enter") (event.target as HTMLInputElement).blur(); }; return setValue(e.target.value)} onFocus={() => setReadOnly(false)} onBlur={updateData} onKeyDown={updateOnEnter} readOnly={readOnly} id={id} // ReactFlow uses the "drag" / "nodrag" classes to enable / disable dragging of nodes className={`${readOnly ? "drag" : "nodrag"} flex-1 ${styles.textField} ${invalid ? styles.invalid : ""} ${className}`} aria-label={ariaLabel} />; } /** * A styled text input that updates its value **only on commit** (when the user * presses Enter or clicks outside the input). * * Internally wraps `RealtimeTextField` and buffers input changes locally, * calling `setValue` only once editing is complete. * * @param props - Component properties. * @param props.value - The current text input value. * @param props.setValue - Callback invoked when the user commits the change. * @param props.placeholder - Optional placeholder text displayed when the input is empty. * @param props.className - Optional additional CSS class names. * @param props.id - Optional unique HTML `id` for the input element. * @param props.ariaLabel - Optional ARIA label for accessibility. * @param props.invalid - If true, applies error styling to indicate invalid input. * * @returns A styled `` element that updates its parent state only on commit. */ export function TextField({ value = "", setValue, placeholder, className, id, ariaLabel, invalid = false, } : { value: string, setValue: (value: string) => void, placeholder?: string, className?: string, id?: string, ariaLabel?: string, invalid?: boolean, }) { const [inputValue, setInputValue] = useState(value); useEffect(() => { setInputValue(value); }, [value]); const onCommit = () => setValue(inputValue); return ; }