Skip to content

Add spanning (multi-row) column headers to tfl_table#43

Merged
billdenney merged 2 commits into
mainfrom
claude/export-tfl-page-na-null-1a2f5f
Jul 9, 2026
Merged

Add spanning (multi-row) column headers to tfl_table#43
billdenney merged 2 commits into
mainfrom
claude/export-tfl-page-na-null-1a2f5f

Conversation

@billdenney

Copy link
Copy Markdown
Member

tfl_table(col_header_sep = "|||") (default on; a single NA disables) splits each column label into stacked, bottom-aligned header rows and auto-merges adjacent equal cells into spanning headers. A spanner's width is the sum of the columns beneath it, and a spanned block is atomic across column-continuation pages (an over-wide block routes to overflow_action). When no label contains the separator the header is a single row and output is byte-identical to before (R == 1 short-circuit).

Span detection (.compute_header_spans) uses a hierarchical closed-boundary algorithm: merges happen only within a shared parent span, empty cells are transparent singletons, and the row-header/data divide is a hard boundary. Merge comparison uses raw text while display right-trims, so a trailing space prevents an unwanted merge.

Width is handled by measuring only each column's leaf segment, then distributing each spanner's deficit across its member columns (.apply_header_span_widths) on both the natural and minimum width vectors; the existing water-fill / pagination / reconcile machinery then runs unchanged. paginate_cols packs by atoms so spans never split. Also fixed wrap auto-detection to key off the leaf segment (a super-header's spaces must not make a leaf column wrap-eligible). The row/column span logic shares a .span_deficit() kernel.

Adds col_header_span_rule (default TRUE): each multi-column spanner is underlined, with adjacent groups' rules separated by a gap equal to the cell's horizontal padding; styled via gp$col_header_span_rule (falls back to gp$col_header_rule).

Tests in test-span_header.R cover the algorithm, slicing, validation, spanner x-extent (= sum of member columns), atomic pagination, over-wide error, underline gap/toggle, and the R == 1 regression lock. Adds a "Spanning column headers" vignette section; updates design docs (D-53).

billdenney and others added 2 commits July 8, 2026 22:14
`tfl_table(col_header_sep = "|||")` (default on; a single NA disables)
splits each column label into stacked, bottom-aligned header rows and
auto-merges adjacent equal cells into spanning headers. A spanner's width
is the sum of the columns beneath it, and a spanned block is atomic across
column-continuation pages (an over-wide block routes to overflow_action).
When no label contains the separator the header is a single row and output
is byte-identical to before (R == 1 short-circuit).

Span detection (.compute_header_spans) uses a hierarchical closed-boundary
algorithm: merges happen only within a shared parent span, empty cells are
transparent singletons, and the row-header/data divide is a hard boundary.
Merge comparison uses raw text while display right-trims, so a trailing
space prevents an unwanted merge.

Width is handled by measuring only each column's leaf segment, then
distributing each spanner's deficit across its member columns
(.apply_header_span_widths) on both the natural and minimum width vectors;
the existing water-fill / pagination / reconcile machinery then runs
unchanged. paginate_cols packs by atoms so spans never split. Also fixed
wrap auto-detection to key off the leaf segment (a super-header's spaces
must not make a leaf column wrap-eligible). The row/column span logic
shares a .span_deficit() kernel.

Adds col_header_span_rule (default TRUE): each multi-column spanner is
underlined, with adjacent groups' rules separated by a gap equal to the
cell's horizontal padding; styled via gp$col_header_span_rule (falls back
to gp$col_header_rule).

Tests in test-span_header.R cover the algorithm, slicing, validation,
spanner x-extent (= sum of member columns), atomic pagination, over-wide
error, underline gap/toggle, and the R == 1 regression lock. Adds a
"Spanning column headers" vignette section; updates design docs (D-53).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The new spanning-header chunks used fig.height = 4, which leaves a content
area (~2.55 in) below the default min_content_height (3 in), so
export_tfl() aborted during vignette re-building and failed R CMD check on
every CI job (and pkgdown). Use fig.height = 8.5 to match the other table
chunks in this vignette, giving ample content height.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@billdenney
billdenney merged commit 3c6cf1c into main Jul 9, 2026
9 checks passed
@billdenney
billdenney deleted the claude/export-tfl-page-na-null-1a2f5f branch July 9, 2026 13:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant