переименования папок
This commit is contained in:
196
src/component/DualSpreadsheet/components/Spreadsheet.tsx
Normal file
196
src/component/DualSpreadsheet/components/Spreadsheet.tsx
Normal file
@@ -0,0 +1,196 @@
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import { VariableSizeGrid } from 'react-window'
|
||||
import { COL_COUNT, ROW_COUNT, SHEET_NAMES, SpreadsheetProps } from '../types'
|
||||
import { getColumnLabel } from '../utils'
|
||||
import { CellRenderer } from './CellRenderer'
|
||||
|
||||
// --- Spreadsheet Component ---
|
||||
export const Spreadsheet = ({
|
||||
sheetType,
|
||||
columnWidths,
|
||||
spreadsheetWidths,
|
||||
getCachedCellData,
|
||||
selectedCell,
|
||||
activeSheet,
|
||||
isEditing,
|
||||
editingValue,
|
||||
handleCellClick,
|
||||
handleCellDoubleClick,
|
||||
handleCellChange,
|
||||
stopEditing,
|
||||
setActiveSheet,
|
||||
activeCellInputRef,
|
||||
containerRefs,
|
||||
handleKeyDown,
|
||||
headerRefs,
|
||||
rowHeaderRefs,
|
||||
gridRefs,
|
||||
onGridScroll,
|
||||
handleColumnResize,
|
||||
mergedCells,
|
||||
}: SpreadsheetProps) => {
|
||||
const widths = columnWidths
|
||||
const cachedData = getCachedCellData(sheetType)
|
||||
|
||||
const itemData = {
|
||||
sheetType,
|
||||
widths,
|
||||
selectedCell,
|
||||
activeSheet,
|
||||
isEditing,
|
||||
editingValue,
|
||||
cachedData,
|
||||
mergedCells,
|
||||
handleCellClick,
|
||||
handleCellDoubleClick,
|
||||
handleCellChange,
|
||||
stopEditing,
|
||||
setActiveSheet,
|
||||
activeCellInputRef,
|
||||
}
|
||||
|
||||
const parentRef = useRef<HTMLDivElement>(null)
|
||||
const [gridSize, setGridSize] = useState({ width: 0, height: 0 })
|
||||
|
||||
useEffect(() => {
|
||||
const parent = parentRef.current
|
||||
if (!parent) return
|
||||
|
||||
const resizeObserver = new ResizeObserver(() => {
|
||||
const { width, height } = parent.getBoundingClientRect()
|
||||
const headerHeight =
|
||||
parent.querySelector('.column-headers')?.clientHeight || 0
|
||||
setGridSize({ width, height: height - headerHeight })
|
||||
})
|
||||
|
||||
resizeObserver.observe(parent)
|
||||
return () => resizeObserver.disconnect()
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={parentRef}
|
||||
className="flex min-w-0 flex-col overflow-hidden"
|
||||
style={{
|
||||
flexBasis: `${spreadsheetWidths[SHEET_NAMES[sheetType]] * 100}%`,
|
||||
transition: 'none',
|
||||
}}
|
||||
onClick={() => setActiveSheet(sheetType)}
|
||||
>
|
||||
<div
|
||||
ref={el => (containerRefs.current[sheetType] = el)}
|
||||
className="flex h-full flex-col overflow-hidden border bg-card shadow-sm"
|
||||
onKeyDown={handleKeyDown}
|
||||
tabIndex={0}
|
||||
>
|
||||
{/* Spreadsheet Body */}
|
||||
<div className="flex flex-1">
|
||||
{/* Row Headers */}
|
||||
<div className="flex-shrink-0 bg-muted">
|
||||
<div className="relative flex h-7 w-10 items-center justify-center border-b border-r border-border">
|
||||
{/* Компактное обозначение листа */}
|
||||
<div className="absolute inset-0 flex items-end justify-end p-0.5">
|
||||
<span
|
||||
className={`flex h-3 w-3 items-center justify-center rounded-sm text-[9px] font-medium text-white ${
|
||||
sheetType === 'report'
|
||||
? 'bg-primary/70'
|
||||
: 'bg-emerald-500/70'
|
||||
}`}
|
||||
>
|
||||
{SHEET_NAMES[sheetType]}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
ref={el => (rowHeaderRefs.current[sheetType] = el)}
|
||||
className="overflow-y-hidden"
|
||||
style={{ height: gridSize.height }}
|
||||
>
|
||||
<div>
|
||||
{Array.from({ length: ROW_COUNT }).map((_, rowIndex) => {
|
||||
const isActiveRow =
|
||||
selectedCell?.row === rowIndex && activeSheet === sheetType
|
||||
return (
|
||||
<div
|
||||
key={rowIndex}
|
||||
className={`flex h-7 w-10 items-center justify-center border-b border-r border-border text-xs font-medium ${isActiveRow ? 'bg-primary/15 font-medium text-foreground' : 'text-muted-foreground'}`}
|
||||
>
|
||||
{rowIndex + 1}
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex-1 overflow-hidden">
|
||||
{/* Column Headers */}
|
||||
<div
|
||||
ref={el => (headerRefs.current[sheetType] = el)}
|
||||
className="column-headers overflow-x-hidden"
|
||||
>
|
||||
<div className="flex">
|
||||
{Array.from({ length: COL_COUNT }).map((_, i) => {
|
||||
const isActiveColumn =
|
||||
selectedCell?.col === i && activeSheet === sheetType
|
||||
const handleMouseDown = (e: React.MouseEvent) => {
|
||||
e.preventDefault()
|
||||
const startX = e.clientX
|
||||
const startWidth = widths[i]
|
||||
|
||||
const handleMouseMove = (me: MouseEvent) => {
|
||||
const newWidth = startWidth + (me.clientX - startX)
|
||||
handleColumnResize(sheetType, i, newWidth)
|
||||
}
|
||||
|
||||
const handleMouseUp = () => {
|
||||
document.removeEventListener('mousemove', handleMouseMove)
|
||||
document.removeEventListener('mouseup', handleMouseUp)
|
||||
}
|
||||
|
||||
document.addEventListener('mousemove', handleMouseMove)
|
||||
document.addEventListener('mouseup', handleMouseUp)
|
||||
}
|
||||
return (
|
||||
<div
|
||||
key={i}
|
||||
className={`relative flex h-7 flex-shrink-0 items-center justify-center border-b border-r border-border bg-muted text-center text-xs font-medium ${isActiveColumn ? 'bg-primary/15 font-medium text-foreground' : 'text-muted-foreground'}`}
|
||||
style={{ width: widths[i] }}
|
||||
>
|
||||
{getColumnLabel(i)}
|
||||
<div
|
||||
className="resizer absolute right-0 top-0 h-full w-1 cursor-col-resize hover:bg-primary hover:bg-opacity-50"
|
||||
onMouseDown={handleMouseDown}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Grid */}
|
||||
<div className="min-h-0 flex-1">
|
||||
<VariableSizeGrid
|
||||
ref={(el: VariableSizeGrid | null) =>
|
||||
(gridRefs.current[sheetType] = el)
|
||||
}
|
||||
columnCount={COL_COUNT}
|
||||
rowCount={ROW_COUNT}
|
||||
columnWidth={(index: number) => widths[index]}
|
||||
rowHeight={() => 28}
|
||||
height={gridSize.height}
|
||||
width={gridSize.width}
|
||||
itemData={itemData}
|
||||
overscanColumnCount={20}
|
||||
overscanRowCount={10}
|
||||
onScroll={e => onGridScroll(sheetType, e)}
|
||||
>
|
||||
{CellRenderer}
|
||||
</VariableSizeGrid>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user