Дизайн таблиц
This commit is contained in:
@@ -12,7 +12,7 @@ import { useMultiSheetEngine } from '../../hooks/useMultiSheetEngine'
|
||||
|
||||
const ROW_COUNT = 90
|
||||
const COL_COUNT = 20
|
||||
const ROW_HEIGHT = 32
|
||||
const ROW_HEIGHT = 28
|
||||
|
||||
const SHEET_NAMES = {
|
||||
report: 'L',
|
||||
@@ -66,9 +66,9 @@ const Cell = memo(
|
||||
setActiveSheet,
|
||||
activeCellRef,
|
||||
}: CellProps) => {
|
||||
const className = `border border-gray-200 ${
|
||||
isSelected ? 'bg-blue-100' : ''
|
||||
} ${isModified ? 'bg-yellow-50' : ''}`
|
||||
const className = `border border-border ${isSelected ? 'bg-accent' : ''} ${
|
||||
isModified ? 'bg-yellow-50' : ''
|
||||
}`
|
||||
|
||||
const style = {
|
||||
minWidth: `${columnWidth}px`,
|
||||
@@ -112,12 +112,16 @@ const Cell = memo(
|
||||
stopEditing(false)
|
||||
}
|
||||
}}
|
||||
className="absolute left-0 top-0 z-20 box-border h-full w-full border-2 border-blue-500 p-2"
|
||||
className="absolute left-0 top-0 z-20 box-border h-full w-full border-2 border-primary px-2 py-1"
|
||||
/>
|
||||
) : (
|
||||
<div
|
||||
className="relative h-full w-full truncate p-2"
|
||||
style={isSelected ? { boxShadow: 'inset 0 0 0 2px #3b82f6' } : {}}
|
||||
className="relative h-full w-full truncate px-2 py-1"
|
||||
style={
|
||||
isSelected
|
||||
? { boxShadow: 'inset 0 0 0 2px hsl(var(--primary))' }
|
||||
: {}
|
||||
}
|
||||
>
|
||||
{displayValue}
|
||||
</div>
|
||||
@@ -157,43 +161,74 @@ const FormulaBar = memo(
|
||||
}: FormulaBarProps) => {
|
||||
if (activeSheet !== sheetType) {
|
||||
return (
|
||||
<div className="flex-shrink-0 border-b border-gray-200 bg-white p-2">
|
||||
<div className="flex h-[34px] items-center" />
|
||||
<div className="flex-shrink-0 border-b bg-background px-3 py-1">
|
||||
<div className="flex h-[28px] items-center" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex-shrink-0 border-b border-gray-200 bg-white p-2">
|
||||
<div className="flex-shrink-0 border-b bg-background px-3 py-1">
|
||||
<div className="flex items-center space-x-2">
|
||||
<div className="min-w-[60px] text-[1rem] text-gray-600">
|
||||
<b>
|
||||
<div className="flex min-w-[50px] items-center justify-center rounded-md border bg-muted px-2 py-1">
|
||||
<span className="text-xs font-medium text-muted-foreground">
|
||||
{selectedCell
|
||||
? `${getColumnLabel(selectedCell.col)}${selectedCell.row + 1}`
|
||||
: ''}
|
||||
</b>
|
||||
: 'A1'}
|
||||
</span>
|
||||
</div>
|
||||
{isCalculating && (
|
||||
<div className="animate-pulse text-xs text-blue-600">
|
||||
Вычисляется...
|
||||
<div className="flex flex-1 items-center space-x-2">
|
||||
<div className="flex items-center text-muted-foreground">
|
||||
<svg
|
||||
className="h-3.5 w-3.5"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
d="M9 7h6m0 10v-3m-3 3h.01M9 17h.01M9 14h.01M12 14h.01M15 11h.01M12 11h.01M9 11h.01M7 21h10a2 2 0 002-2V5a2 2 0 00-2-2H7a2 2 0 00-2 2v14a2 2 0 002 2z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
)}
|
||||
<div className="flex-1">
|
||||
<input
|
||||
ref={formulaBarRef}
|
||||
type="text"
|
||||
className={`w-full rounded border px-2 py-1 text-sm focus:outline-none ${
|
||||
isEditing
|
||||
? 'border-blue-400 focus:ring-2 focus:ring-blue-300'
|
||||
: 'border-gray-300 focus:ring-2 focus:ring-blue-500'
|
||||
}`}
|
||||
placeholder="Введите формулу..."
|
||||
className={`flex-1 rounded-md border border-input bg-background px-2 py-1 text-sm ring-offset-background transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 ${
|
||||
isEditing ? 'ring-2 ring-ring ring-offset-2' : ''
|
||||
} ${isCalculating ? 'animate-pulse' : ''}`}
|
||||
placeholder="Введите формулу или значение..."
|
||||
value={formulaBarValue}
|
||||
onChange={(e) => onValueChange(e.target.value)}
|
||||
onFocus={onFocus}
|
||||
onBlur={onBlur}
|
||||
onKeyDown={onKeyDown}
|
||||
/>
|
||||
{isCalculating && (
|
||||
<div className="flex items-center text-muted-foreground">
|
||||
<svg
|
||||
className="h-3.5 w-3.5 animate-spin"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<circle
|
||||
className="opacity-25"
|
||||
cx="12"
|
||||
cy="12"
|
||||
r="10"
|
||||
stroke="currentColor"
|
||||
strokeWidth="4"
|
||||
></circle>
|
||||
<path
|
||||
className="opacity-75"
|
||||
fill="currentColor"
|
||||
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
|
||||
></path>
|
||||
</svg>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -310,7 +345,7 @@ const DualSpreadsheet: FC<DualSpreadsheetProps> = ({ templateData = {} }) => {
|
||||
}
|
||||
setIsEditing(false)
|
||||
setEditingValue('') // Очищаем временное значение
|
||||
containerRefs.current[activeSheet]?.focus()
|
||||
containerRefs.current[activeSheet]?.focus({ preventScroll: true })
|
||||
},
|
||||
[activeSheet, selectedCell, editingValue, multiSheetEngine],
|
||||
)
|
||||
@@ -404,7 +439,7 @@ const DualSpreadsheet: FC<DualSpreadsheetProps> = ({ templateData = {} }) => {
|
||||
stopEditing(true)
|
||||
}
|
||||
setSelectedCell({ row, col })
|
||||
containerRefs.current[activeSheet]?.focus()
|
||||
containerRefs.current[activeSheet]?.focus({ preventScroll: true })
|
||||
},
|
||||
[isEditing, stopEditing, activeSheet],
|
||||
)
|
||||
@@ -459,10 +494,14 @@ const DualSpreadsheet: FC<DualSpreadsheetProps> = ({ templateData = {} }) => {
|
||||
e.preventDefault()
|
||||
stopEditing(true)
|
||||
moveSelection(1, 0)
|
||||
// Предотвращаем прокрутку страницы
|
||||
setTimeout(() => {
|
||||
containerRefs.current[activeSheet]?.focus({ preventScroll: true })
|
||||
}, 0)
|
||||
} else if (e.key === 'Escape') {
|
||||
e.preventDefault()
|
||||
stopEditing(false)
|
||||
containerRefs.current[activeSheet]?.focus()
|
||||
containerRefs.current[activeSheet]?.focus({ preventScroll: true })
|
||||
}
|
||||
},
|
||||
[stopEditing, moveSelection, activeSheet],
|
||||
@@ -476,22 +515,30 @@ const DualSpreadsheet: FC<DualSpreadsheetProps> = ({ templateData = {} }) => {
|
||||
return (
|
||||
<div
|
||||
ref={(el) => (containerRefs.current[sheetType] = el)}
|
||||
className="overflow-auto rounded-lg border border-gray-300 bg-white shadow-md"
|
||||
style={{
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
position: 'relative',
|
||||
}}
|
||||
className="flex h-full flex-col overflow-hidden rounded-lg border bg-card shadow-sm"
|
||||
onKeyDown={handleKeyDown}
|
||||
tabIndex={0}
|
||||
>
|
||||
<div className="flex-shrink-0 border-b bg-muted/50 px-3 py-1">
|
||||
<div className="flex items-center space-x-2">
|
||||
<div
|
||||
className={`h-2 w-2 rounded-full ${
|
||||
sheetType === 'report' ? 'bg-primary' : 'bg-emerald-500'
|
||||
}`}
|
||||
></div>
|
||||
<h3 className="text-xs font-medium text-muted-foreground">
|
||||
{sheetType === 'report' ? 'Отчет' : 'Расчеты'}
|
||||
</h3>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex-1 overflow-auto">
|
||||
<table
|
||||
className="w-full border-collapse"
|
||||
style={{ tableLayout: 'fixed' }}
|
||||
>
|
||||
<thead>
|
||||
<tr>
|
||||
<th className="h-8 w-12 border border-gray-300 bg-gray-100 text-xs font-medium text-gray-600"></th>
|
||||
<th className="h-7 w-10 border border-border bg-muted text-xs font-medium text-muted-foreground"></th>
|
||||
{Array.from({ length: COL_COUNT }, (_, i) => {
|
||||
const handleMouseDown = (e: React.MouseEvent) => {
|
||||
e.preventDefault()
|
||||
@@ -514,12 +561,12 @@ const DualSpreadsheet: FC<DualSpreadsheetProps> = ({ templateData = {} }) => {
|
||||
return (
|
||||
<th
|
||||
key={i}
|
||||
className="relative h-8 border border-gray-300 bg-gray-100 text-center text-xs font-medium text-gray-600"
|
||||
className="relative h-7 border border-border bg-muted text-center text-xs font-medium text-muted-foreground"
|
||||
style={{ width: widths[i] }}
|
||||
>
|
||||
{getColumnLabel(i)}
|
||||
<div
|
||||
className="absolute right-0 top-0 h-full w-1 cursor-col-resize hover:bg-blue-500 hover:bg-opacity-50"
|
||||
className="absolute right-0 top-0 h-full w-1 cursor-col-resize hover:bg-primary hover:bg-opacity-50"
|
||||
onMouseDown={handleMouseDown}
|
||||
/>
|
||||
</th>
|
||||
@@ -530,7 +577,7 @@ const DualSpreadsheet: FC<DualSpreadsheetProps> = ({ templateData = {} }) => {
|
||||
<tbody>
|
||||
{Array.from({ length: ROW_COUNT }).map((_, rowIndex) => (
|
||||
<tr key={rowIndex}>
|
||||
<td className="h-8 w-12 border border-gray-300 bg-gray-100 text-center text-xs font-medium text-gray-600">
|
||||
<td className="h-7 w-10 border border-border bg-muted text-center text-xs font-medium text-muted-foreground">
|
||||
{rowIndex + 1}
|
||||
</td>
|
||||
{Array.from({ length: COL_COUNT }).map((_, colIndex) => {
|
||||
@@ -591,6 +638,7 @@ const DualSpreadsheet: FC<DualSpreadsheetProps> = ({ templateData = {} }) => {
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -623,22 +671,7 @@ const DualSpreadsheet: FC<DualSpreadsheetProps> = ({ templateData = {} }) => {
|
||||
)
|
||||
|
||||
return (
|
||||
<div className="flex h-full flex-col space-y-4 bg-gray-100 p-4">
|
||||
{/* Двойная панель */}
|
||||
<div className="flex flex-grow space-x-4 overflow-hidden">
|
||||
{/* Левая панель - Отчет */}
|
||||
<div className="flex w-1/2 flex-col">
|
||||
<h2 className="mb-2 text-lg font-semibold">Отчет</h2>
|
||||
{memoizedReportSheet}
|
||||
</div>
|
||||
|
||||
{/* Правая панель - Вычисления */}
|
||||
<div className="flex w-1/2 flex-col">
|
||||
<h2 className="mb-2 text-lg font-semibold">Расчеты</h2>
|
||||
{memoizedCalculationsSheet}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex h-screen flex-col bg-background">
|
||||
<div className="flex-shrink-0">
|
||||
<FormulaBar
|
||||
sheetType={activeSheet}
|
||||
@@ -654,6 +687,11 @@ const DualSpreadsheet: FC<DualSpreadsheetProps> = ({ templateData = {} }) => {
|
||||
formulaBarRef={(el) => (formulaBarRefs.current[activeSheet] = el)}
|
||||
/>
|
||||
</div>
|
||||
{/* Двойная панель */}
|
||||
<div className="flex flex-1 gap-1 overflow-hidden p-1">
|
||||
<div className="min-w-0 flex-1">{memoizedReportSheet}</div>
|
||||
<div className="min-w-0 flex-1">{memoizedCalculationsSheet}</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user