import React, { forwardRef, useEffect, useImperativeHandle, useRef } from 'react'; import { cn } from '~/lib/utils'; export interface TextBoxProps extends Omit, 'onChange' | 'value'> { value: string; onChange: (value: string) => void; placeholder?: string; wrapperClassName?: string; inputClassName?: string; disabled?: boolean; autoFocus?: boolean; spellCheck?: boolean; } export const TextBox = forwardRef( ( { value, onChange, placeholder, wrapperClassName, inputClassName, disabled = false, autoFocus = false, spellCheck = true, onInput, onBlur, onFocus, ...rest }, ref ) => { const localRef = useRef(null); useImperativeHandle(ref, () => localRef.current as HTMLDivElement); // Function to handle DOM updates const updateDOM = (newValue: string) => { if (localRef.current) { // Only update if different to avoid selection issues if (localRef.current.textContent !== newValue) { localRef.current.textContent = newValue; // Clear any
elements if the content is empty if (!newValue && localRef.current.innerHTML.includes('
')) { localRef.current.innerHTML = ''; } } } }; // Update DOM when value prop changes useEffect(() => { updateDOM(value); }, [value]); useEffect(() => { if (autoFocus && localRef.current) { localRef.current.focus(); } }, [autoFocus]); const handleInput = (event: React.FormEvent) => { const newValue = event.currentTarget.textContent || ''; // Handle the case where the content is empty but contains a
if (!newValue && event.currentTarget.innerHTML.includes('
')) { event.currentTarget.innerHTML = ''; } onChange(newValue); onInput?.(event); }; const handlePaste = (event: React.ClipboardEvent) => { event.preventDefault(); const text = event.clipboardData.getData('text/plain'); // Use document.execCommand to maintain undo stack document.execCommand('insertText', false, text); // Manually trigger input event const inputEvent = new Event('input', { bubbles: true }); event.currentTarget.dispatchEvent(inputEvent); }; return (
localRef.current?.focus()} >
); } ); TextBox.displayName = 'TextBox'; export default TextBox;