GridTable: support partial colspan (2-of-4 columns)
Some checks failed
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-school (push) Successful in 39s
CI / test-go-edu-search (push) Successful in 40s
CI / test-python-klausur (push) Failing after 2m16s
CI / test-python-agent-core (push) Successful in 28s
CI / test-nodejs-website (push) Successful in 31s

Previously GridTable only supported full-row spanning (one cell across
all columns). Now renders each spanning_header cell with its actual
colspan, positioned at the correct grid column. This allows rows like
"In Britain..." (colspan=2) + "In Germany..." (colspan=2) to render
side by side instead of only showing the first cell.

Also fix box row fields: is_header always set (was undefined for
flowing/bullet_list), y_min_px/y_max_px for header_only rows.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Benjamin Admin
2026-04-13 12:47:14 +02:00
parent 7b3e8c576d
commit 709e41e050

View File

@@ -410,34 +410,40 @@ export function GridTable({
{/* Cells — spanning header or normal columns */} {/* Cells — spanning header or normal columns */}
{isSpanning ? ( {isSpanning ? (
<div <>
className="border-b border-r border-gray-200 dark:border-gray-700 bg-blue-50/50 dark:bg-blue-900/10 flex items-center" {/* Render each spanning cell with its colspan */}
style={{ {zone.cells
gridColumn: `2 / ${numCols + 2}`, .filter((c) => c.row_index === row.index && c.col_type === 'spanning_header')
height: `${rowH}px`, .sort((a, b) => a.col_index - b.col_index)
}} .map((spanCell) => {
> const colspan = spanCell.colspan || numCols
{(() => { const cellId = spanCell.cell_id
const spanCell = zone.cells.find( const isSelected = selectedCell === cellId
(c) => c.row_index === row.index && c.col_type === 'spanning_header', const cellColor = getCellColor(spanCell)
) // Grid column: starts at col_index+2 (1-based, +1 for row number column)
if (!spanCell) return null const gridColStart = spanCell.col_index + 2
const cellId = spanCell.cell_id const gridColEnd = gridColStart + colspan
const isSelected = selectedCell === cellId return (
const cellColor = getCellColor(spanCell) <div
return ( key={cellId}
<div className="flex items-center w-full"> className="border-b border-r border-gray-200 dark:border-gray-700 bg-blue-50/50 dark:bg-blue-900/10 flex items-center"
{cellColor && ( style={{
<span gridColumn: `${gridColStart} / ${gridColEnd}`,
className="flex-shrink-0 w-1.5 self-stretch rounded-l-sm" height: `${rowH}px`,
style={{ backgroundColor: cellColor }} }}
/> >
)} <div className="flex items-center w-full">
<input {cellColor && (
id={`cell-${cellId}`} <span
type="text" className="flex-shrink-0 w-1.5 self-stretch rounded-l-sm"
value={spanCell.text} style={{ backgroundColor: cellColor }}
onChange={(e) => onCellTextChange(cellId, e.target.value)} />
)}
<input
id={`cell-${cellId}`}
type="text"
value={spanCell.text}
onChange={(e) => onCellTextChange(cellId, e.target.value)}
onFocus={() => onSelectCell(cellId)} onFocus={() => onSelectCell(cellId)}
onKeyDown={(e) => handleKeyDown(e, cellId)} onKeyDown={(e) => handleKeyDown(e, cellId)}
className={`w-full px-3 py-1 bg-transparent border-0 outline-none text-center ${ className={`w-full px-3 py-1 bg-transparent border-0 outline-none text-center ${
@@ -447,9 +453,10 @@ export function GridTable({
spellCheck={false} spellCheck={false}
/> />
</div> </div>
) </div>
})()} )
</div> })}
</>
) : ( ) : (
zone.columns.map((col) => { zone.columns.map((col) => {
const cell = cellMap.get(`${row.index}_${col.index}`) const cell = cellMap.get(`${row.index}_${col.index}`)