/
kan64
/
spreadsheet-lab
Обзор
Документация
Войти
/
kan64
/
spreadsheet-lab
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
develop
client/src/components/FormulaBar.tsx
145 строк
4 KB
Anton Kravchenkov
fix(ui): Formatted text display
25 мар 2026, 00:21
25 мар 2026, 00:21
a355a4d
Код
Авторство
О чём код?
import React, { useRef, useState, useEffect, useImperativeHandle, forwardRef } from 'react'; import type { CellPosition } from '../types/types.ts'; import { cellId } from '../utils/cellUtils'; export interface FormulaBarHandle { flushAndGetValue: () => string; } interface FormulaBarProps { selectedCell: CellPosition | null; editValue: string; isEditing: boolean; cellDisplayValue: string; onStartEditing: () => void; onEditValueChange: (value: string) => void; onConfirm: (finalValue?: string) => void; onCancel: () => void; readOnly?: boolean; } const EDIT_THROTTLE_MS = 32; export const FormulaBar = forwardRef<FormulaBarHandle, FormulaBarProps>(({ selectedCell, editValue, isEditing, cellDisplayValue, onStartEditing, onEditValueChange, onConfirm, onCancel, readOnly = false, }, ref) => { const inputRef = useRef<HTMLInputElement>(null); const focusedByClick = useRef(false); const [localValue, setLocalValue] = useState(editValue); const throttleRef = useRef<ReturnType<typeof setTimeout> | null>(null); const pendingRef = useRef<string | null>(null); useImperativeHandle(ref, () => ({ flushAndGetValue: () => { if (throttleRef.current) { clearTimeout(throttleRef.current); throttleRef.current = null; } if (pendingRef.current !== null) { onEditValueChange(pendingRef.current); const val = pendingRef.current; pendingRef.current = null; return val; } return localValue; }, }), [localValue, onEditValueChange]); const flushThrottle = () => { if (throttleRef.current) { clearTimeout(throttleRef.current); throttleRef.current = null; } if (pendingRef.current !== null) { onEditValueChange(pendingRef.current); pendingRef.current = null; } }; const handleChange = (value: string) => { setLocalValue(value); pendingRef.current = value; if (!throttleRef.current) { throttleRef.current = setTimeout(() => { throttleRef.current = null; if (pendingRef.current !== null) { onEditValueChange(pendingRef.current); pendingRef.current = null; } }, EDIT_THROTTLE_MS); } }; const handleKeyDown = (e: React.KeyboardEvent) => { if (e.key === 'Enter') { e.preventDefault(); focusedByClick.current = false; flushThrottle(); onConfirm(localValue); } else if (e.key === 'Escape') { e.preventDefault(); focusedByClick.current = false; if (throttleRef.current) { clearTimeout(throttleRef.current); throttleRef.current = null; } pendingRef.current = null; onCancel(); } }; const prevIsEditingRef = useRef(false); useEffect(() => { if (isEditing && !prevIsEditingRef.current) { setLocalValue(editValue); } if (isEditing) { setLocalValue(editValue); } prevIsEditingRef.current = isEditing; }, [isEditing, editValue]); const cellName = selectedCell ? cellId(selectedCell.row, selectedCell.col) : ''; const displayValue = isEditing ? editValue : cellDisplayValue; return ( <div className="formula-bar"> <div className="formula-bar-cell-name">{cellName}</div> <div className="formula-bar-separator" /> <div className="formula-bar-fx"> <span className="fx-label">fx</span> </div> <input ref={inputRef} className="formula-bar-input" value={displayValue} readOnly={readOnly} onMouseDown={() => { if (!readOnly) focusedByClick.current = true; }} onClick={() => { if (!readOnly && !isEditing) onStartEditing(); }} onChange={(e) => { if (!readOnly) handleChange(e.target.value); }} onKeyDown={readOnly ? undefined : handleKeyDown} onBlur={(e) => { if (!readOnly && isEditing) { const related = e.relatedTarget as HTMLElement | null; if (related?.closest('.cell-editor-overlay') || related?.closest('.toolbar') || related?.closest('.link-dialog')) return; focusedByClick.current = false; flushThrottle(); onConfirm(localValue); } }} /> </div> ); });