'use client' import { useCallback, useRef } from 'react' import type { GridZone } from './types' interface GridTableProps { zone: GridZone selectedCell: string | null onSelectCell: (cellId: string) => void onCellTextChange: (cellId: string, text: string) => void onToggleColumnBold: (zoneIndex: number, colIndex: number) => void onToggleRowHeader: (zoneIndex: number, rowIndex: number) => void onNavigate: (cellId: string, direction: 'up' | 'down' | 'left' | 'right') => void } export function GridTable({ zone, selectedCell, onSelectCell, onCellTextChange, onToggleColumnBold, onToggleRowHeader, onNavigate, }: GridTableProps) { const tableRef = useRef(null) const handleKeyDown = useCallback( (e: React.KeyboardEvent, cellId: string) => { if (e.key === 'Tab') { e.preventDefault() onNavigate(cellId, e.shiftKey ? 'left' : 'right') } else if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault() onNavigate(cellId, 'down') } else if (e.key === 'ArrowUp' && e.altKey) { e.preventDefault() onNavigate(cellId, 'up') } else if (e.key === 'ArrowDown' && e.altKey) { e.preventDefault() onNavigate(cellId, 'down') } else if (e.key === 'Escape') { ;(e.target as HTMLElement).blur() } }, [onNavigate], ) // Build row→col cell lookup const cellMap = new Map() for (const cell of zone.cells) { cellMap.set(`${cell.row_index}_${cell.col_index}`, cell) } /** Dominant non-black color from a cell's word_boxes, or null. */ const getCellColor = (cell: (typeof zone.cells)[0] | undefined): string | null => { if (!cell?.word_boxes?.length) return null for (const wb of cell.word_boxes) { if (wb.color_name && wb.color_name !== 'black' && wb.color) { return wb.color } } return null } const isBoxZone = zone.zone_type === 'box' return (
{/* Zone label */}
{isBoxZone ? 'Box' : 'Inhalt'} Zone {zone.zone_index} {zone.columns.length} Spalten, {zone.rows.length} Zeilen, {zone.cells.length} Zellen
{/* Column headers */} {/* Row number header */} ))} {zone.rows.map((row) => ( {/* Row number */} {/* Cells */} {zone.columns.map((col) => { const cell = cellMap.get(`${row.index}_${col.index}`) const cellId = cell?.cell_id ?? `Z${zone.zone_index}_R${String(row.index).padStart(2, '0')}_C${col.index}` const isSelected = selectedCell === cellId const isBold = col.bold || cell?.is_bold const isLowConf = cell && cell.confidence > 0 && cell.confidence < 60 const cellColor = getCellColor(cell) return ( ) })} ))}
{zone.columns.map((col) => ( onToggleColumnBold(zone.zone_index, col.index)} title={`Spalte ${col.index + 1} — Klick fuer Fett-Toggle`} >
{col.label} {col.bold && ( B )}
onToggleRowHeader(zone.zone_index, row.index)} title={`Zeile ${row.index + 1} — Klick fuer Header-Toggle`} > {row.index + 1} {row.is_header && H}
{cellColor && ( wb.color_name !== 'black')?.color_name}`} /> )} { if (cell) onCellTextChange(cellId, e.target.value) }} onFocus={() => onSelectCell(cellId)} onKeyDown={(e) => handleKeyDown(e, cellId)} className={`w-full px-2 py-1.5 bg-transparent border-0 outline-none ${ isBold ? 'font-bold' : 'font-normal' } ${row.is_header ? 'text-base' : 'text-sm'}`} style={cellColor ? { color: cellColor } : undefined} spellCheck={false} />
) }