195 lines
6.0 KiB
JavaScript
195 lines
6.0 KiB
JavaScript
// EditCell.js - обновленная версия
|
||
import { useEffect, useRef, useLayoutEffect, useState } from 'react';
|
||
import { isFormula, extractFormula, calculateFormula, formatNumber } from '../../utils/formulaUtils';
|
||
import { saveToStorage, loadFromStorage } from '../../utils/localStorageUtils';
|
||
import { useRealtime } from '../../contexts/RealtimeContext';
|
||
|
||
const EditCell = ({ refCell, onChange, disabled, value, tableId, cellId, table }) => {
|
||
const { endEditing: contextEndEditing } = useRealtime();
|
||
|
||
const [curValue, setCurValue] = useState(value);
|
||
const [displayValue, setDisplayValue] = useState(value);
|
||
const [isFormulaMode, setIsFormulaMode] = useState(false);
|
||
|
||
// Ключ для хранения формул в localStorage
|
||
const storageKey = `formula_${tableId}_${cellId}`;
|
||
|
||
const trFrag = refCell.current.offsetParent.offsetParent;
|
||
const tdFrag = refCell.current.offsetParent;
|
||
const left = tdFrag.offsetLeft;
|
||
const width = refCell.current.offsetParent.offsetWidth;
|
||
const translateY = trFrag.style.transform;
|
||
|
||
// Загружаем сохраненную формулу при монтировании
|
||
useEffect(() => {
|
||
const savedFormula = loadFromStorage(storageKey);
|
||
if (savedFormula && isFormula(savedFormula)) {
|
||
setIsFormulaMode(true);
|
||
setCurValue(savedFormula);
|
||
// Вычисляем и отображаем результат
|
||
try {
|
||
const formula = extractFormula(savedFormula);
|
||
const result = calculateFormula(formula);
|
||
setDisplayValue(formatNumber(result));
|
||
} catch (error) {
|
||
setDisplayValue('#ОШИБКА');
|
||
}
|
||
} else {
|
||
setDisplayValue(value);
|
||
}
|
||
}, [storageKey, value]);
|
||
|
||
function useAutoResize(value) {
|
||
const ref = useRef(null);
|
||
|
||
useLayoutEffect(() => {
|
||
const el = ref.current;
|
||
if (!el) return;
|
||
|
||
el.style.height = 'auto';
|
||
el.style.minHeight = '3rem';
|
||
el.style.height = el.scrollHeight + 'px';
|
||
el.style.maxHeight = '180px';
|
||
}, [value]);
|
||
|
||
return ref;
|
||
}
|
||
|
||
const ref = useAutoResize(value);
|
||
|
||
useEffect(() => {
|
||
if (ref.current && !disabled) {
|
||
ref.current.focus();
|
||
ref.current.setSelectionRange(
|
||
ref.current.value.length,
|
||
ref.current.value.length,
|
||
);
|
||
}
|
||
}, [disabled, ref]);
|
||
|
||
const handleSave = () => {
|
||
// Проверяем, является ли введенное значение формулой
|
||
if (isFormula(curValue)) {
|
||
// Сохраняем формулу в localStorage
|
||
saveToStorage(storageKey, curValue);
|
||
// Вычисляем и сохраняем результат
|
||
try {
|
||
const formula = extractFormula(curValue);
|
||
const result = calculateFormula(formula);
|
||
const formattedResult = formatNumber(result);
|
||
setDisplayValue(formattedResult);
|
||
onChange?.(formattedResult);
|
||
setIsFormulaMode(true);
|
||
return;
|
||
} catch (error) {
|
||
setDisplayValue('#ОШИБКА');
|
||
onChange?.(curValue); // Сохраняем как текст, если ошибка
|
||
return;
|
||
}
|
||
} else {
|
||
// Если это не формула, удаляем сохраненную формулу
|
||
localStorage.removeItem(storageKey);
|
||
setIsFormulaMode(false);
|
||
setDisplayValue(curValue);
|
||
onChange?.(curValue);
|
||
}
|
||
};
|
||
|
||
const handleChangeValue = (event) => {
|
||
const input = event.target.value;
|
||
setCurValue(input);
|
||
// При редактировании показываем введенный текст
|
||
setDisplayValue(input);
|
||
};
|
||
|
||
// Функция для отображения значения в ячейке
|
||
const getDisplayValue = () => {
|
||
if (isFormulaMode && isFormula(curValue)) {
|
||
return displayValue;
|
||
}
|
||
return displayValue;
|
||
};
|
||
|
||
const finishEditing = () => {
|
||
if (!disabled) {
|
||
handleSave();
|
||
}
|
||
const editingCell = table.getState().editingCell || null;
|
||
if (editingCell) {
|
||
contextEndEditing?.(editingCell.row, editingCell.column);
|
||
}
|
||
};
|
||
|
||
useEffect(() => {
|
||
const handleClickOutside = (event) => {
|
||
// Проверяем, был ли клик внутри элемента редактирования
|
||
const editElement = ref.current;
|
||
if (editElement && !editElement.contains(event.target)) {
|
||
finishEditing();
|
||
}
|
||
};
|
||
|
||
document.addEventListener('mousedown', handleClickOutside);
|
||
|
||
return () => {
|
||
document.removeEventListener('mousedown', handleClickOutside);
|
||
};
|
||
}, [ref, finishEditing]);
|
||
|
||
return (
|
||
<tr>
|
||
<td>
|
||
<div
|
||
key="blockForEdit"
|
||
style={{
|
||
position: 'absolute',
|
||
left: left,
|
||
zIndex: 12,
|
||
width: width,
|
||
transform: translateY,
|
||
top: 0,
|
||
}}
|
||
>
|
||
{isFormulaMode && (
|
||
<div style={{
|
||
position: 'absolute',
|
||
top: '-20px',
|
||
left: '4px',
|
||
fontSize: '10px',
|
||
color: '#666',
|
||
background: '#f0f0f0',
|
||
padding: '2px 6px',
|
||
borderRadius: '3px',
|
||
pointerEvents: 'none'
|
||
}}>
|
||
fx
|
||
</div>
|
||
)}
|
||
<textarea
|
||
ref={ref}
|
||
disabled={disabled}
|
||
style={{
|
||
width: '100%',
|
||
height: 'auto',
|
||
fieldSizing: 'content',
|
||
fontSize: 'inherit',
|
||
backgroundColor: isFormulaMode ? '#f8f9fa' : 'white',
|
||
border: isFormulaMode ? '2px solid #4a90d9' : undefined,
|
||
}}
|
||
onChange={handleChangeValue}
|
||
value={curValue}
|
||
onKeyDown={(e) => {
|
||
if (e.key === 'Enter' && !e.shiftKey) {
|
||
e.preventDefault();
|
||
finishEditing();
|
||
}
|
||
}}
|
||
placeholder={isFormulaMode ? 'Введите формулу (начинается с =)' : ''}
|
||
/>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
);
|
||
};
|
||
|
||
export default EditCell; |