feat(EditableCell): improve handling of external updates and user input

This commit is contained in:
ryan 2025-06-27 16:52:46 +03:00
parent afc70a56ba
commit 5f5d8f00d9

View file

@ -26,65 +26,67 @@ export function EditableCell({
className = '' className = ''
}: EditableCellProps) { }: EditableCellProps) {
const [localValue, setLocalValue] = useState(value); const [localValue, setLocalValue] = useState(value);
const [isExternalUpdate, setIsExternalUpdate] = useState(false);
const inputRef = useRef<HTMLInputElement>(null); const inputRef = useRef<HTMLInputElement>(null);
const textareaRef = useRef<HTMLTextAreaElement>(null); const textareaRef = useRef<HTMLTextAreaElement>(null);
const lastExternalValueRef = useRef(value);
const isUserInputRef = useRef(false);
// Update local value when prop changes (including external updates) // Update local value when prop changes (only for genuine external updates)
useEffect(() => { useEffect(() => {
if (value !== localValue) { // Only update if this is a genuine external change (not from user input)
if (value !== lastExternalValueRef.current && !isUserInputRef.current) {
console.log('External value change detected for cell:', cellKey, value);
setLocalValue(value); setLocalValue(value);
setIsExternalUpdate(true);
} }
}, [value, localValue]); lastExternalValueRef.current = value;
// Reset external update flag after a short delay // Reset user input flag after a short delay
useEffect(() => { if (isUserInputRef.current) {
if (isExternalUpdate) {
const timer = setTimeout(() => { const timer = setTimeout(() => {
setIsExternalUpdate(false); isUserInputRef.current = false;
}, 100); }, 100);
return () => clearTimeout(timer); return () => clearTimeout(timer);
} }
}, [isExternalUpdate]); }, [value, cellKey]);
// Handle focus when this cell becomes focused // Handle focus when this cell becomes focused
useEffect(() => { useEffect(() => {
if (focusedCell === cellKey) { if (focusedCell === cellKey) {
const element = inputType === 'textarea' ? textareaRef.current : inputRef.current; const element = inputType === 'textarea' ? textareaRef.current : inputRef.current;
if (element) { if (element && document.activeElement !== element) {
element.focus(); element.focus();
// Select all text when focusing, but not during external updates // Only select text if this is not from user input
if (!isExternalUpdate) { if (!isUserInputRef.current) {
element.select(); element.select();
} }
} }
} }
}, [focusedCell, cellKey, inputType, isExternalUpdate]); }, [focusedCell, cellKey, inputType]);
const handleInput = useCallback((e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => { const handleInput = useCallback((e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
const newValue = e.target.value; const newValue = e.target.value;
console.log('Input event:', newValue); console.log('Input event for cell:', cellKey, newValue);
isUserInputRef.current = true; // Mark as user input
setLocalValue(newValue); setLocalValue(newValue);
}, []); }, [cellKey]);
const commitInput = useCallback(() => { const commitInput = useCallback(() => {
console.log('Committing input:', localValue); console.log('Committing input for cell:', cellKey, localValue);
onInput(localValue); onInput(localValue);
}, [localValue, onInput]); }, [localValue, onInput, cellKey]);
const handleFocus = useCallback(() => { const handleFocus = useCallback(() => {
console.log('Focus gained for key:', cellKey); console.log('Focus gained for cell:', cellKey);
setFocusedCell(cellKey); setFocusedCell(cellKey);
onFocus?.(); onFocus?.();
}, [cellKey, setFocusedCell, onFocus]); }, [cellKey, setFocusedCell, onFocus]);
const handleBlur = useCallback(() => { const handleBlur = useCallback(() => {
console.log('Focus lost'); console.log('Focus lost for cell:', cellKey);
setFocusedCell(null); setFocusedCell(null);
commitInput(); commitInput();
onBlur?.(); onBlur?.();
}, [setFocusedCell, commitInput, onBlur]); }, [setFocusedCell, commitInput, onBlur, cellKey]);
const handleKeyDown = useCallback((e: React.KeyboardEvent) => { const handleKeyDown = useCallback((e: React.KeyboardEvent) => {
if (e.key === 'Enter' && inputType === 'text') { if (e.key === 'Enter' && inputType === 'text') {