Add spanning (multi-row) column headers to tfl_table#43
Merged
Conversation
`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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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).