переименования папок

This commit is contained in:
2025-07-21 15:02:10 +03:00
parent d0c79bd334
commit 84f0c9c6aa
72 changed files with 245 additions and 249 deletions

View File

@@ -0,0 +1,335 @@
import { useCallback, useEffect, useRef, useState } from 'react'
import {
CellUtils,
CellValue,
Workbook,
} from '../lib/spreadsheet-engine/spreadsheet-engine'
interface CellData {
value: string
isSelected: boolean
}
interface UseSpreadsheetEngineProps {
rows?: number
cols?: number
sheetName?: string
initialData?: string[][]
}
export function useSpreadsheetEngine({
rows = 50,
cols = 20,
sheetName = 'Sheet1',
initialData,
}: UseSpreadsheetEngineProps = {}) {
const workbookRef = useRef<Workbook | null>(null)
const [cells, setCells] = useState<CellData[][]>(() => {
return Array(rows)
.fill(null)
.map(() =>
Array(cols)
.fill(null)
.map(() => ({ value: '', isSelected: false }))
)
})
const [isCalculating, setIsCalculating] = useState(false)
// Инициализация workbook
useEffect(() => {
if (!workbookRef.current) {
workbookRef.current = new Workbook()
workbookRef.current.addSheet(sheetName)
// addCustomFunctions(workbookRef.current)
}
// Загружаем начальные данные если они есть
if (initialData) {
loadDataFromArray(initialData)
}
}, [sheetName, initialData])
// Конвертация данных из движка в React состояние
const syncFromEngine = useCallback(() => {
if (!workbookRef.current) return
const sheet = workbookRef.current.getSheet(sheetName)
if (!sheet) return
const newCells = Array(rows)
.fill(null)
.map(() =>
Array(cols)
.fill(null)
.map(() => ({ value: '', isSelected: false }))
)
// Получаем все ячейки из движка
for (let row = 0; row < rows; row++) {
for (let col = 0; col < cols; col++) {
const cellName = CellUtils.createCellName(col, row + 1)
const cell = sheet.getCell(cellName)
// Показываем сырое значение (raw) в UI, а не вычисленное
if (cell.raw !== null && cell.raw !== undefined) {
newCells[row][col].value = String(cell.raw)
}
}
}
setCells(newCells)
}, [rows, cols, sheetName])
// Установка значения ячейки
const setCellValue = useCallback(
(row: number, col: number, value: string) => {
if (!workbookRef.current) return
const cellName = CellUtils.createCellName(col, row + 1)
const qualifiedName = `${sheetName}!${cellName}`
setIsCalculating(true)
try {
// Конвертируем пустую строку в null
const cellValue: CellValue = value === '' ? null : value
// Если значение начинается с =, то это формула
if (typeof cellValue === 'string' && cellValue.startsWith('=')) {
workbookRef.current.setCell(qualifiedName, cellValue)
} else {
// Пытаемся конвертировать в число, если возможно
const numValue = Number(cellValue)
if (!isNaN(numValue) && cellValue !== null && cellValue !== '') {
workbookRef.current.setCell(qualifiedName, numValue)
} else {
workbookRef.current.setCell(qualifiedName, cellValue)
}
}
// Пересчитываем все формулы
workbookRef.current.recalc()
// Синхронизируем с React состоянием
syncFromEngine()
} catch (error) {
console.error('Error setting cell value:', error)
} finally {
setIsCalculating(false)
}
},
[sheetName, syncFromEngine]
)
// Получение вычисленного значения ячейки
const getCellValue = useCallback(
(row: number, col: number): CellValue => {
if (!workbookRef.current) return null
const cellName = CellUtils.createCellName(col, row + 1)
const qualifiedName = `${sheetName}!${cellName}`
try {
return workbookRef.current.getValue(qualifiedName)
} catch (error) {
console.error('Error getting cell value:', error)
return '#ERROR!'
}
},
[sheetName]
)
// Получение сырого значения ячейки (для отображения в строке формул)
const getCellRawValue = useCallback(
(row: number, col: number): string => {
if (!workbookRef.current) return ''
const cellName = CellUtils.createCellName(col, row + 1)
const sheet = workbookRef.current.getSheet(sheetName)
if (!sheet) return ''
const cell = sheet.getCell(cellName)
return cell.raw !== null && cell.raw !== undefined ? String(cell.raw) : ''
},
[sheetName]
)
// Загрузка данных из массива
const loadDataFromArray = useCallback(
(data: string[][]) => {
if (!workbookRef.current) return
const sheet = workbookRef.current.getSheet(sheetName)
if (!sheet) return
setIsCalculating(true)
try {
// Очищаем лист
sheet.cells.clear()
// Загружаем данные
for (let row = 0; row < data.length && row < rows; row++) {
for (let col = 0; col < data[row].length && col < cols; col++) {
const value = data[row][col]
if (value !== null && value !== undefined && value !== '') {
const cellName = CellUtils.createCellName(col, row + 1)
sheet.setCell(cellName, value)
}
}
}
// Пересчитываем все формулы
workbookRef.current.recalc()
// Синхронизируем с React состоянием
syncFromEngine()
} catch (error) {
console.error('Error loading data:', error)
} finally {
setIsCalculating(false)
}
},
[rows, cols, sheetName, syncFromEngine]
)
// Экспорт данных в массив (для сохранения)
const exportToArray = useCallback((): string[][] => {
if (!workbookRef.current) return []
const sheet = workbookRef.current.getSheet(sheetName)
if (!sheet) return []
const result: string[][] = Array(rows)
.fill(null)
.map(() => Array(cols).fill(''))
for (let row = 0; row < rows; row++) {
for (let col = 0; col < cols; col++) {
const cellName = CellUtils.createCellName(col, row + 1)
const cell = sheet.getCell(cellName)
if (cell.raw !== null && cell.raw !== undefined) {
result[row][col] = String(cell.raw)
}
}
}
return result
}, [rows, cols, sheetName])
// Экспорт в JSON для бэкенда
const exportToJSON = useCallback((): string => {
if (!workbookRef.current) return '{}'
return JSON.stringify(workbookRef.current.toJSON())
}, [])
// Импорт из JSON с бэкенда
const importFromJSON = useCallback(
(jsonData: string) => {
if (!workbookRef.current) return
setIsCalculating(true)
try {
const data = JSON.parse(jsonData)
workbookRef.current.fromJSON(data)
// addCustomFunctions(workbookRef.current)
workbookRef.current.recalc()
syncFromEngine()
} catch (error) {
console.error('Error importing from JSON:', error)
} finally {
setIsCalculating(false)
}
},
[syncFromEngine]
)
// Добавление пользовательской функции
const addFunction = useCallback(
(name: string, func: (...args: any[]) => any) => {
if (!workbookRef.current) return
workbookRef.current.addFunction(name, func)
},
[]
)
// Пересчёт всех формул
const recalculate = useCallback(() => {
if (!workbookRef.current) return
setIsCalculating(true)
try {
workbookRef.current.recalc()
syncFromEngine()
} catch (error) {
console.error('Error recalculating:', error)
} finally {
setIsCalculating(false)
}
}, [syncFromEngine])
// Получение информации о зависимостях ячейки
const getCellDependencies = useCallback(
(row: number, col: number) => {
if (!workbookRef.current) return { precedents: [], dependents: [] }
const cellName = CellUtils.createCellName(col, row + 1)
const sheet = workbookRef.current.getSheet(sheetName)
if (!sheet) return { precedents: [], dependents: [] }
const cell = sheet.getCell(cellName)
return {
precedents: Array.from(cell.precedents),
dependents: Array.from(cell.dependents),
}
},
[sheetName]
)
// Получение статистики workbook
const getWorkbookStats = useCallback(() => {
if (!workbookRef.current) return { sheets: 0, cells: 0, formulas: 0 }
let totalCells = 0
let totalFormulas = 0
for (const sheet of workbookRef.current.sheets.values()) {
totalCells += sheet.cells.size
for (const cell of sheet.cells.values()) {
if (typeof cell.raw === 'string' && cell.raw.startsWith('=')) {
totalFormulas++
}
}
}
return {
sheets: workbookRef.current.sheets.size,
cells: totalCells,
formulas: totalFormulas,
}
}, [])
return {
cells,
setCellValue,
getCellValue,
getCellRawValue,
loadDataFromArray,
exportToArray,
exportToJSON,
importFromJSON,
addFunction,
recalculate,
getCellDependencies,
getWorkbookStats,
isCalculating,
workbook: workbookRef.current,
}
}