перепрыгивая текста
This commit is contained in:
@@ -127,7 +127,7 @@ const DualSpreadsheet: FC<DualSpreadsheetProps> = ({
|
||||
|
||||
const getCachedCellData = useCallback(
|
||||
(sheetType: SheetType): Record<string, CachedCellData> => {
|
||||
const cacheKey = `${sheetType}-${revision}`
|
||||
const cacheKey = `${sheetType}-${revision}-${JSON.stringify(columnWidths[sheetType])}`
|
||||
|
||||
if (cellDataCache.current[cacheKey]) {
|
||||
return cellDataCache.current[cacheKey]
|
||||
@@ -147,23 +147,32 @@ const DualSpreadsheet: FC<DualSpreadsheetProps> = ({
|
||||
}
|
||||
}
|
||||
|
||||
const rightContentMap: Record<string, boolean> = {}
|
||||
// 1. Находим ближайшую занятую колонку справа для каждой ячейки строки
|
||||
const nextContentInRow: Record<number, number[]> = {}
|
||||
for (let row = 0; row < ROW_COUNT; row++) {
|
||||
let hasContentOnRight = false
|
||||
nextContentInRow[row] = Array(COL_COUNT).fill(COL_COUNT)
|
||||
let next = COL_COUNT
|
||||
for (let col = COL_COUNT - 1; col >= 0; col--) {
|
||||
const cellKey = `${row}-${col}`
|
||||
rightContentMap[cellKey] = hasContentOnRight
|
||||
nextContentInRow[row][col] = next // сохранить для использования ниже
|
||||
if (allCellValues[cellKey]) {
|
||||
hasContentOnRight = true
|
||||
next = col // текущая колонка занята → "новый" предел
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Заполняем cache
|
||||
for (let row = 0; row < ROW_COUNT; row++) {
|
||||
for (let col = 0; col < COL_COUNT; col++) {
|
||||
const cellKey = `${row}-${col}`
|
||||
const rawValue = multiSheetEngine.getCellRawValue(sheetName, row, col)
|
||||
const displayValue = allCellValues[cellKey]
|
||||
const nextCol = nextContentInRow[row][col]
|
||||
|
||||
// ширина от текущей колонки до nextCol-1 включительно
|
||||
const availablePx = columnWidths[sheetType]
|
||||
.slice(col, nextCol)
|
||||
.reduce((sum, w) => sum + w, 0)
|
||||
|
||||
const cellName = getColumnLabel(col) + (row + 1)
|
||||
const baseFileValue = initialTemplateValues.current[cellName] ?? '' // Базовое значение из файла
|
||||
@@ -209,7 +218,8 @@ const DualSpreadsheet: FC<DualSpreadsheetProps> = ({
|
||||
displayValue,
|
||||
rawValue,
|
||||
isModified,
|
||||
hasRightContent: rightContentMap[cellKey],
|
||||
nextContentCol: nextCol,
|
||||
availablePx,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -226,7 +236,7 @@ const DualSpreadsheet: FC<DualSpreadsheetProps> = ({
|
||||
cellDataCache.current[cacheKey] = cachedData
|
||||
return cachedData
|
||||
},
|
||||
[revision, multiSheetEngine, initialTemplateValues]
|
||||
[revision, multiSheetEngine, initialTemplateValues, columnWidths]
|
||||
)
|
||||
|
||||
const formulaBarValue = useMemo(() => {
|
||||
|
||||
@@ -13,7 +13,8 @@ export const Cell = memo(
|
||||
displayValue,
|
||||
rawValue,
|
||||
isModified,
|
||||
hasRightContent,
|
||||
nextContentCol,
|
||||
availablePx,
|
||||
onCellClick,
|
||||
onCellDoubleClick,
|
||||
onCellValueChange,
|
||||
@@ -28,9 +29,7 @@ export const Cell = memo(
|
||||
return null
|
||||
}
|
||||
|
||||
const className = `border border-border ${isSelected ? 'bg-accent' : ''} ${
|
||||
isModified && !mergedCellInfo?.isMerged ? 'bg-yellow-200' : ''
|
||||
}`
|
||||
const className = `border border-border ${isModified && !mergedCellInfo?.isMerged ? 'bg-yellow-200' : ''}`
|
||||
|
||||
// Отладка стилей для измененных ячеек
|
||||
if (isModified) {
|
||||
@@ -108,6 +107,7 @@ export const Cell = memo(
|
||||
type="text"
|
||||
value={rawValue} // Во время редактирования показываем текущее редактируемое значение
|
||||
onChange={e => onCellValueChange(e.target.value)}
|
||||
style={{ width: columnWidth - 4 }}
|
||||
onBlur={e => {
|
||||
// Проверяем, не переходит ли фокус в formula bar
|
||||
const relatedTarget = e.relatedTarget as HTMLElement
|
||||
@@ -125,13 +125,14 @@ export const Cell = memo(
|
||||
stopEditing(false)
|
||||
}
|
||||
}}
|
||||
className="absolute left-0 top-0 z-20 box-border h-full w-full border-2 border-primary px-2 py-1"
|
||||
className="absolute left-0 top-0 z-20 box-border h-full border-2 border-primary px-2 py-1"
|
||||
/>
|
||||
) : (
|
||||
// Контейнер, который допускает перепрыгивание текста, если впереди есть свободное место
|
||||
<div
|
||||
className="relative flex h-full w-full items-center px-2"
|
||||
style={{
|
||||
overflow: hasRightContent ? 'hidden' : 'visible',
|
||||
overflow: availablePx > columnWidth ? 'visible' : 'hidden',
|
||||
whiteSpace: 'nowrap',
|
||||
...(isSelected
|
||||
? { boxShadow: 'inset 0 0 0 2px hsl(var(--primary))' }
|
||||
@@ -139,12 +140,17 @@ export const Cell = memo(
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className="relative"
|
||||
className="absolute left-1 top-0 flex h-full items-center"
|
||||
style={{
|
||||
overflow: hasRightContent ? 'hidden' : 'visible',
|
||||
textOverflow: hasRightContent ? 'ellipsis' : 'clip',
|
||||
width:
|
||||
availablePx > columnWidth
|
||||
? availablePx - 8
|
||||
: columnWidth - 16,
|
||||
whiteSpace: 'nowrap',
|
||||
maxWidth: hasRightContent ? `${columnWidth - 16}px` : 'none',
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
pointerEvents: 'none', // чтобы клики шли в "родную" ячейку
|
||||
zIndex: 1,
|
||||
}}
|
||||
>
|
||||
{cellDisplayValue}
|
||||
|
||||
@@ -82,7 +82,8 @@ export const CellRenderer = ({
|
||||
displayValue={displayValue}
|
||||
rawValue={rawValue}
|
||||
isModified={cellData?.isModified || false}
|
||||
hasRightContent={cellData?.hasRightContent || false}
|
||||
nextContentCol={cellData?.nextContentCol || 0}
|
||||
availablePx={cellData?.availablePx || widths[columnIndex]}
|
||||
onCellClick={handleCellClick}
|
||||
onCellDoubleClick={handleCellDoubleClick}
|
||||
onCellValueChange={handleCellChange}
|
||||
|
||||
@@ -107,14 +107,18 @@ export const Spreadsheet = ({
|
||||
style={{ height: gridSize.height }}
|
||||
>
|
||||
<div>
|
||||
{Array.from({ length: ROW_COUNT }).map((_, rowIndex) => (
|
||||
{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 text-muted-foreground"
|
||||
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>
|
||||
@@ -127,6 +131,8 @@ export const Spreadsheet = ({
|
||||
>
|
||||
<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
|
||||
@@ -148,7 +154,7 @@ export const Spreadsheet = ({
|
||||
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 text-muted-foreground"
|
||||
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)}
|
||||
@@ -175,6 +181,8 @@ export const Spreadsheet = ({
|
||||
height={gridSize.height}
|
||||
width={gridSize.width}
|
||||
itemData={itemData}
|
||||
overscanColumnCount={20}
|
||||
overscanRowCount={10}
|
||||
onScroll={e => onGridScroll(sheetType, e)}
|
||||
>
|
||||
{CellRenderer}
|
||||
|
||||
@@ -21,7 +21,8 @@ export interface CachedCellData {
|
||||
displayValue: string
|
||||
rawValue: string
|
||||
isModified: boolean
|
||||
hasRightContent: boolean
|
||||
nextContentCol: number // индекс ближайшей занятой колонки (COL_COUNT, если нет)
|
||||
availablePx: number // суммарная ширина, куда можно расширяться
|
||||
}
|
||||
|
||||
export interface DualSpreadsheetProps {
|
||||
@@ -41,7 +42,8 @@ export interface CellProps {
|
||||
displayValue: string // Отображаемое значение (вычисленное)
|
||||
rawValue: string // "Сырое" значение (формула)
|
||||
isModified: boolean
|
||||
hasRightContent: boolean // Есть ли содержимое в ячейках справа
|
||||
nextContentCol: number // индекс ближайшей занятой колонки
|
||||
availablePx: number // суммарная ширина, куда можно расширяться
|
||||
onCellClick: (row: number, col: number) => void
|
||||
onCellDoubleClick: (row: number, col: number) => void
|
||||
onCellValueChange: (newValue: string) => void // Изменение значения во время редактирования
|
||||
|
||||
Reference in New Issue
Block a user