выделение ячеек
This commit is contained in:
@@ -16,6 +16,7 @@ import { FormulaBar, Spreadsheet } from './components'
|
||||
import { useDraggableDivider } from './hooks/useDraggableDivider'
|
||||
import {
|
||||
CachedCellData,
|
||||
CellSelection,
|
||||
COL_COUNT,
|
||||
DualSpreadsheetProps,
|
||||
HighlightedCell,
|
||||
@@ -28,7 +29,9 @@ import {
|
||||
convertReferencesToHighlights,
|
||||
createCellAddress,
|
||||
createQualifiedCellReference,
|
||||
createSelectionFromRange,
|
||||
extractCellReferences,
|
||||
getAllCellsInSelections,
|
||||
getColumnLabel,
|
||||
} from './utils'
|
||||
|
||||
@@ -75,6 +78,13 @@ const DualSpreadsheet: FC<DualSpreadsheetProps> = ({
|
||||
row: number
|
||||
col: number
|
||||
} | null>(null)
|
||||
// Состояние для множественного выделения
|
||||
const [multiSelection, setMultiSelection] = useState<CellSelection[]>([])
|
||||
const [isDragging, setIsDragging] = useState(false)
|
||||
const [dragStart, setDragStart] = useState<{
|
||||
row: number
|
||||
col: number
|
||||
} | null>(null)
|
||||
const [isEditing, setIsEditing] = useState(false)
|
||||
const [editingValue, setEditingValue] = useState<string>('') // Локальное значение во время редактирования
|
||||
// Источник ввода: 'cell' (редактирование внутри ячейки) или 'formula'
|
||||
@@ -396,6 +406,8 @@ const DualSpreadsheet: FC<DualSpreadsheetProps> = ({
|
||||
|
||||
const startEditing = useCallback(() => {
|
||||
if (!selectedCell) return
|
||||
// Сбрасываем множественное выделение при начале редактирования
|
||||
setMultiSelection([])
|
||||
const sheetName = SHEET_NAMES[activeSheet]
|
||||
const rawValue = multiSheetEngine.getCellRawValue(
|
||||
sheetName,
|
||||
@@ -465,6 +477,8 @@ const DualSpreadsheet: FC<DualSpreadsheetProps> = ({
|
||||
)
|
||||
updateRevision()
|
||||
multiSheetEngine.debouncedRecalc()
|
||||
// Сбрасываем множественное выделение при очистке одиночной ячейки
|
||||
setMultiSelection([])
|
||||
}, [activeSheet, selectedCell, multiSheetEngine, updateRevision])
|
||||
|
||||
const moveSelection = useCallback(
|
||||
@@ -488,10 +502,30 @@ const DualSpreadsheet: FC<DualSpreadsheetProps> = ({
|
||||
)
|
||||
|
||||
setSelectedCell({ row: newRow, col: newCol })
|
||||
// Сбрасываем множественное выделение при навигации
|
||||
setMultiSelection([])
|
||||
},
|
||||
[selectedCell, isEditing, stopEditing]
|
||||
)
|
||||
|
||||
// Функция для очистки множества выделенных ячеек
|
||||
const handleClearMultipleCells = useCallback(() => {
|
||||
if (multiSelection.length === 0) return
|
||||
|
||||
const sheetName = SHEET_NAMES[activeSheet]
|
||||
const cellsToDelete = getAllCellsInSelections(multiSelection)
|
||||
|
||||
cellsToDelete.forEach(({ row, col }: { row: number; col: number }) => {
|
||||
multiSheetEngine.setCellValueWithoutRecalc(sheetName, row, col, '')
|
||||
})
|
||||
|
||||
updateRevision()
|
||||
multiSheetEngine.debouncedRecalc()
|
||||
|
||||
// Очищаем выделение после удаления
|
||||
setMultiSelection([])
|
||||
}, [multiSelection, activeSheet, multiSheetEngine, updateRevision])
|
||||
|
||||
const handleKeyDown = useCallback(
|
||||
(e: React.KeyboardEvent) => {
|
||||
if (isEditing) {
|
||||
@@ -518,8 +552,39 @@ const DualSpreadsheet: FC<DualSpreadsheetProps> = ({
|
||||
setEditingSource('cell')
|
||||
startEditing()
|
||||
},
|
||||
Delete: handleClearCell,
|
||||
Backspace: handleClearCell,
|
||||
Delete: () => {
|
||||
// Если есть множественное выделение, удаляем все выделенные ячейки
|
||||
if (multiSelection.length > 0) {
|
||||
handleClearMultipleCells()
|
||||
} else {
|
||||
handleClearCell()
|
||||
}
|
||||
},
|
||||
Backspace: () => {
|
||||
// Если есть множественное выделение, удаляем все выделенные ячейки
|
||||
if (multiSelection.length > 0) {
|
||||
handleClearMultipleCells()
|
||||
} else {
|
||||
handleClearCell()
|
||||
}
|
||||
},
|
||||
Escape: () => {
|
||||
// Сбрасываем множественное выделение
|
||||
setMultiSelection([])
|
||||
},
|
||||
}
|
||||
|
||||
// Ctrl+A - выделить все ячейки
|
||||
if (e.key === 'a' && (e.ctrlKey || e.metaKey)) {
|
||||
e.preventDefault()
|
||||
const allSelection = createSelectionFromRange(
|
||||
0,
|
||||
0,
|
||||
ROW_COUNT - 1,
|
||||
COL_COUNT - 1
|
||||
)
|
||||
setMultiSelection([allSelection])
|
||||
return
|
||||
}
|
||||
|
||||
if (keyHandlers[e.key]) {
|
||||
@@ -532,11 +597,19 @@ const DualSpreadsheet: FC<DualSpreadsheetProps> = ({
|
||||
setIsEditing(true)
|
||||
}
|
||||
},
|
||||
[selectedCell, isEditing, moveSelection, startEditing, handleClearCell]
|
||||
[
|
||||
selectedCell,
|
||||
isEditing,
|
||||
moveSelection,
|
||||
startEditing,
|
||||
handleClearCell,
|
||||
multiSelection,
|
||||
handleClearMultipleCells,
|
||||
]
|
||||
)
|
||||
|
||||
const handleCellClick = useCallback(
|
||||
(row: number, col: number) => {
|
||||
(row: number, col: number, event?: React.MouseEvent) => {
|
||||
// console.log('🖱️ Обычный клик по ячейке:', {
|
||||
// row,
|
||||
//col,
|
||||
@@ -547,12 +620,93 @@ const DualSpreadsheet: FC<DualSpreadsheetProps> = ({
|
||||
if (isEditing) {
|
||||
stopEditing(true)
|
||||
}
|
||||
|
||||
// Если удерживается Ctrl (или Cmd на Mac), добавляем к множественному выделению
|
||||
if (event && (event.ctrlKey || event.metaKey)) {
|
||||
const newSelection = createSelectionFromRange(row, col, row, col)
|
||||
setMultiSelection(prev => [...prev, newSelection])
|
||||
setSelectedCell({ row, col })
|
||||
return
|
||||
}
|
||||
|
||||
// Если удерживается Shift, расширяем выделение
|
||||
if (event && event.shiftKey && selectedCell) {
|
||||
const newSelection = createSelectionFromRange(
|
||||
selectedCell.row,
|
||||
selectedCell.col,
|
||||
row,
|
||||
col
|
||||
)
|
||||
setMultiSelection([newSelection])
|
||||
return
|
||||
}
|
||||
|
||||
// Обычный клик - очищаем множественное выделение
|
||||
setMultiSelection([])
|
||||
setSelectedCell({ row, col })
|
||||
containerRefs.current[activeSheet]?.focus({ preventScroll: true })
|
||||
},
|
||||
[isEditing, stopEditing, activeSheet]
|
||||
[isEditing, stopEditing, activeSheet, selectedCell]
|
||||
)
|
||||
|
||||
// Новый обработчик для начала drag selection
|
||||
const handleCellMouseDown = useCallback(
|
||||
(row: number, col: number, event: React.MouseEvent) => {
|
||||
// Игнорируем правый клик
|
||||
if (event.button !== 0) return
|
||||
|
||||
// Если удерживаются модификаторы, не начинаем drag
|
||||
if (event.ctrlKey || event.metaKey || event.shiftKey) return
|
||||
|
||||
setIsDragging(true)
|
||||
setDragStart({ row, col })
|
||||
setMultiSelection([])
|
||||
setSelectedCell({ row, col })
|
||||
},
|
||||
[]
|
||||
)
|
||||
|
||||
// Обработчик для drag selection
|
||||
const handleCellMouseEnter = useCallback(
|
||||
(row: number, col: number) => {
|
||||
if (!isDragging || !dragStart) return
|
||||
|
||||
const newSelection = createSelectionFromRange(
|
||||
dragStart.row,
|
||||
dragStart.col,
|
||||
row,
|
||||
col
|
||||
)
|
||||
setMultiSelection([newSelection])
|
||||
},
|
||||
[isDragging, dragStart]
|
||||
)
|
||||
|
||||
// Обработчик завершения drag selection
|
||||
const handleCellMouseUp = useCallback(() => {
|
||||
if (isDragging) {
|
||||
setIsDragging(false)
|
||||
setDragStart(null)
|
||||
}
|
||||
}, [isDragging])
|
||||
|
||||
// Глобальный обработчик для завершения drag selection
|
||||
useEffect(() => {
|
||||
const handleGlobalMouseUp = () => {
|
||||
if (isDragging) {
|
||||
setIsDragging(false)
|
||||
setDragStart(null)
|
||||
}
|
||||
}
|
||||
|
||||
if (isDragging) {
|
||||
document.addEventListener('mouseup', handleGlobalMouseUp)
|
||||
return () => {
|
||||
document.removeEventListener('mouseup', handleGlobalMouseUp)
|
||||
}
|
||||
}
|
||||
}, [isDragging])
|
||||
|
||||
// Новый обработчик для кликов в режиме редактирования - вставляет ссылку
|
||||
const handleCellReferenceClick = useCallback(
|
||||
(targetSheetType: SheetType, row: number, col: number) => {
|
||||
@@ -940,6 +1094,7 @@ const DualSpreadsheet: FC<DualSpreadsheetProps> = ({
|
||||
spreadsheetWidths={spreadsheetWidths}
|
||||
getCachedCellData={getCachedCellData}
|
||||
selectedCell={selectedCell}
|
||||
multiSelection={multiSelection}
|
||||
activeSheet={activeSheet}
|
||||
isEditing={isEditing}
|
||||
editingValue={editingValue}
|
||||
@@ -948,6 +1103,9 @@ const DualSpreadsheet: FC<DualSpreadsheetProps> = ({
|
||||
handleCellDoubleClick={handleCellDoubleClick}
|
||||
handleCellChange={handleCellChange}
|
||||
handleCellReferenceClick={handleCellReferenceClick}
|
||||
handleCellMouseDown={handleCellMouseDown}
|
||||
handleCellMouseEnter={handleCellMouseEnter}
|
||||
handleCellMouseUp={handleCellMouseUp}
|
||||
isInEditMode={isEditing}
|
||||
stopEditing={stopEditing}
|
||||
setActiveSheet={setActiveSheet}
|
||||
|
||||
Reference in New Issue
Block a user