import React, { memo, useEffect, useRef, useLayoutEffect, useState, useCallback, useMemo } from 'react'; import { isFormula, extractFormula, calculateFormula, formatNumber } from '../../utils/formulaUtils'; import { saveToStorage, loadFromStorage } from '../../utils/localStorageUtils'; import { useRealtime } from '../../contexts/RealtimeContext'; const EDITOR_STYLES = { position: 'absolute', zIndex: 12, top: 0, }; const FORMULA_INDICATOR_STYLES = { position: 'absolute', top: '-20px', left: '4px', fontSize: '10px', color: '#666', background: '#f0f0f0', padding: '2px 6px', borderRadius: '3px', pointerEvents: 'none', }; const TEXTAREA_STYLES = { width: '100%', height: '3rem', fieldSizing: 'content', fontSize: 'inherit', }; const EditCell = memo(({ refCell, onChange, disabled, value, tableId, cellId, table }) => { const { endEditing: contextEndEditing } = useRealtime(); const storageKey = `formula_${tableId}_${cellId}`; const [state, setState] = useState(() => ({ currentValue: value, displayValue: value, isFormulaMode: false, })); // Позиция ячейки const [position, setPosition] = useState(); const textareaRef = useRef(null); const refFinished = useRef(false); // Загрузка сохраненной формулы useEffect(() => { const savedFormula = loadFromStorage(storageKey); if (savedFormula && isFormula(savedFormula)) { try { const formula = extractFormula(savedFormula); const result = calculateFormula(formula); setState({ currentValue: savedFormula, displayValue: formatNumber(result), isFormulaMode: true, }); } catch { setState({ currentValue: savedFormula, displayValue: '#ОШИБКА', isFormulaMode: true, }); } } }, [storageKey]); // Вычисление позиции useLayoutEffect(() => { if (!refCell?.current) return; const td = refCell.current.offsetParent; const tr = td?.offsetParent; if (td && tr) { setPosition({ left: td.offsetLeft, width: td.offsetWidth, translateY: tr.style.transform || '', }); } }, [refCell]); // Auto-resize useLayoutEffect(() => { const el = textareaRef.current; if (!el) return; const resize = () => { el.style.minHeight = '3rem'; el.style.height = '3rem'; el.style.height = el.scrollHeight + 'px'; el.style.maxHeight = '180px'; }; resize(); const observer = new ResizeObserver(resize); observer.observe(el); return () => observer.disconnect(); }, [state.currentValue]); const handleSave = useCallback(() => { const { currentValue } = state; if (isFormula(currentValue)) { saveToStorage(storageKey, currentValue); try { const formula = extractFormula(currentValue); const result = calculateFormula(formula); const formattedResult = formatNumber(result); setState(prev => ({ ...prev, displayValue: formattedResult, isFormulaMode: true, })); onChange?.(formattedResult); } catch { setState(prev => ({ ...prev, displayValue: '#ОШИБКА', isFormulaMode: true, })); onChange?.(currentValue); } } else { localStorage.removeItem(storageKey); setState(prev => ({ ...prev, displayValue: currentValue, isFormulaMode: false, })); onChange?.(currentValue); } }, [state, storageKey, onChange]); const finishEditing = useCallback(() => { if (refFinished.current) return; refFinished.current = true; if (!disabled) { handleSave(); } const editingCell = table.getState().editingCell; if (editingCell) { contextEndEditing?.(editingCell.row, editingCell.column); } }, [disabled, handleSave, table, contextEndEditing]); useEffect(() => { const handleClickOutside = (event) => { const el = textareaRef.current; if (el && !el.contains(event.target)) { finishEditing(); } }; document.addEventListener('mousedown', handleClickOutside); return () => document.removeEventListener('mousedown', handleClickOutside); }, [finishEditing]); const handleChange = useCallback((e) => { const value = e.target.value; setState(prev => ({ ...prev, currentValue: value, displayValue: value, })); }, []); const handleKeyDown = useCallback((e) => { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); finishEditing(); } }, [finishEditing]); const editorStyles = useMemo(() => ({ ...EDITOR_STYLES, left: position?.left || 0, width: position?.width || 0, transform: position?.translateY || 0, }), [position]); const textareaStyles = useMemo(() => ({ ...TEXTAREA_STYLES, backgroundColor: state.isFormulaMode ? '#f8f9fa' : 'white', border: state.isFormulaMode ? '2px solid #4a90d9' : undefined, }), [state.isFormulaMode]); return (