@@ -248,7 +248,7 @@ create_table_start_l <- function(data, colwidth_df) {
248248 } else {
249249 n_stub_cols <- length(stub_layout )
250250 }
251-
251+
252252 col_defs [seq_len(n_stub_cols )] <-
253253 paste0(col_defs [seq_len(n_stub_cols )], " |" )
254254 }
@@ -455,8 +455,32 @@ create_heading_component_l <- function(data) {
455455# ' @noRd
456456create_columns_component_l <- function (data , colwidth_df ) {
457457
458+
458459 # Get vector representation of stub layout
459460 stub_layout <- get_stub_layout(data = data )
461+ n_stub_cols <- 0
462+
463+ # if exists, get the length of the stub
464+ if (length(stub_layout )> 0 ){
465+ stub_vars <- dt_boxhead_get_var_stub(data = data )
466+ # Get the actual number of stub columns for the header
467+ # This determines how many columns the stub header should span
468+ if (" group_label" %in% stub_layout && " rowname" %in% stub_layout ) {
469+ n_stub_cols <- length(stub_vars ) + 1 # group_label + all rowname columns
470+ # Get stub_df for width calculations
471+ stub_df <- dplyr :: filter(colwidth_df , type %in% c(" row_group" ," stub" )) %> %
472+ dplyr :: arrange(type )
473+ } else if (" rowname" %in% stub_layout ) {
474+ stub_df <- dplyr :: filter(colwidth_df , type == " stub" )
475+ n_stub_cols <- nrow(stub_df )
476+ } else if (" group_label" %in% stub_layout ) { # stub columns can exist in the dataset with only groups in the stub-layout.
477+ n_stub_cols <- 1
478+ stub_df <- dplyr :: filter(colwidth_df , type == " row_group" )
479+ } else {
480+ n_stub_cols <- length(stub_layout )
481+ stub_df <- dplyr :: filter(colwidth_df , type %in% c(" stub" , " row_group" ))
482+ }
483+ }
460484
461485 styles_tbl <- dt_styles_get(data = data )
462486
@@ -495,28 +519,11 @@ create_columns_component_l <- function(data, colwidth_df) {
495519 vctrs :: vec_slice(styles_tbl , styles_tbl $ locname == " stubhead" )
496520 )
497521
498- # Get the actual number of stub columns for the header
499- # This determines how many columns the stub header should span
500- if (" group_label" %in% stub_layout && " rowname" %in% stub_layout ) {
501- n_stub_cols <- 2 # group_label + rowname
502- # Get stub_df for width calculations
503- stub_df <- dplyr :: filter(colwidth_df , type %in% c(" stub" , " row_group" ))
504- } else if (" rowname" %in% stub_layout ) {
505- stub_df <- dplyr :: filter(colwidth_df , type == " stub" )
506- n_stub_cols <- nrow(stub_df )
507- } else if (" group_label" %in% stub_layout ) {
508- n_stub_cols <- 1
509- stub_df <- dplyr :: filter(colwidth_df , type == " row_group" )
510- } else {
511- n_stub_cols <- length(stub_layout )
512- stub_df <- dplyr :: filter(colwidth_df , type %in% c(" stub" , " row_group" ))
513- }
514-
515522 # Check if we have multiple stubhead labels for multi-column stub
516523 stub_vars <- dt_boxhead_get_var_stub(data = data )
517524 has_multi_column_stub <- length(stub_vars ) > 1 && ! any(is.na(stub_vars ))
518525 has_multiple_labels <- has_multi_column_stub && length(stubh $ label ) > 1
519-
526+
520527 if (has_multiple_labels ) {
521528 # Create individual headers for each stub column
522529 # Process in reverse order since prepend_vec adds to the front
@@ -532,13 +539,13 @@ create_columns_component_l <- function(data, colwidth_df) {
532539 } else {
533540 # Single label spanning all stub columns (current behavior)
534541 headings_vars <- prepend_vec(headings_vars , " ::stub" )
535-
542+
536543 stub_label <- ifelse(
537544 length(stubh $ label ) > 0 ,
538545 apply_cell_styles_l(stubh $ label [1 ], styles_stubhead ),
539546 " "
540547 )
541-
548+
542549 if (n_stub_cols > 1L ) {
543550 # Use multicolumn to span all stub columns
544551 if (any(stub_df $ unspec == 1L )) {
@@ -592,28 +599,10 @@ create_columns_component_l <- function(data, colwidth_df) {
592599 )
593600
594601 if (length(stub_layout ) > 0 ) {
595-
596602 # Get the actual number of stub columns for spanners
597- if (" group_label" %in% stub_layout && " rowname" %in% stub_layout ) {
598-
599- # group_label + rowname
600- n_stub_cols <- 2
601-
602- } else if (" rowname" %in% stub_layout ) {
603-
604- stub_df_cols <- dplyr :: filter(colwidth_df , type == " stub" )
605- n_stub_cols <- nrow(stub_df_cols )
606-
607- } else if (" group_label" %in% stub_layout ) {
608-
609- n_stub_cols <- 1
610-
611- } else {
612-
613- n_stub_cols <- length(stub_layout )
614- }
615-
616603 stub_matrix <- matrix (nrow = nrow(spanners ), ncol = n_stub_cols )
604+ # retain stub names
605+ colnames(stub_matrix ) <- stub_df $ var
617606
618607 spanners <- cbind(stub_matrix , spanners )
619608 spanner_ids <- cbind(stub_matrix , spanner_ids )
@@ -629,6 +618,7 @@ create_columns_component_l <- function(data, colwidth_df) {
629618 # We need a parallel vector of spanner labels and this could
630619 # be part of the `spanners_rle` list
631620 spanners_rle $ labels <- spanners_i [cumsum(spanners_rle $ lengths )]
621+ col_order <- data.frame (var = colnames(spanner_ids ))
632622 spanners_rle <- apply_spanner_styles_l(spanners_rle , styles_tbl )
633623
634624 begins <- (cumsum(utils :: head(c(0 , spanners_rle $ lengths ), - 1 )) + 1 )[! is.na(spanners_rle $ values )]
@@ -637,10 +627,9 @@ create_columns_component_l <- function(data, colwidth_df) {
637627
638628 is_spanner_na <- is.na(spanners_rle $ values )
639629 is_spanner_single <- spanners_rle $ lengths == 1
640-
641630 firsts <- utils :: head(cumsum(c(1L , spanners_rle $ lengths )), - 1L )
642631 lasts <- cumsum(spanners_rle $ lengths )
643- span_widths <- calculate_multicolumn_width_text_l(begins = firsts , ends = lasts , colwidth_df = colwidth_df )
632+ span_widths <- calculate_multicolumn_width_text_l(begins = firsts , ends = lasts , col_order = col_order , colwidth_df = colwidth_df )
644633 tex_widths <-
645634 ifelse(
646635 nzchar(span_widths ),
@@ -665,30 +654,10 @@ create_columns_component_l <- function(data, colwidth_df) {
665654 # If there is a stub we need to tweak the spanners row with a blank multicolumn
666655 # statement that's the same width as that in the columns row; this is to
667656 # prevent the automatic vertical line that would otherwise appear here
668-
669- # Get the actual number of stub columns
670- if (" group_label" %in% stub_layout && " rowname" %in% stub_layout ) {
671-
672- # group_label + rowname
673- n_stub_cols <- 2
674-
675- } else if (" rowname" %in% stub_layout ) {
676-
677- stub_df_cols <- dplyr :: filter(colwidth_df , type == " stub" )
678- n_stub_cols <- nrow(stub_df_cols )
679-
680- } else if (" group_label" %in% stub_layout ) {
681-
682- n_stub_cols <- 1
683-
684- } else {
685-
686- n_stub_cols <- length(stub_layout )
687- }
688-
657+
689658 if (n_stub_cols > 1L ) {
690659
691- tex_stub_width <- calculate_multicolumn_width_text_l(begins = 1 , ends = n_stub_cols , colwidth_df = colwidth_df )
660+ tex_stub_width <- calculate_multicolumn_width_text_l(begins = 1 , ends = n_stub_cols , col_order = col_order , colwidth_df = colwidth_df )
692661 if (tex_stub_width == " " ) {
693662 mc_stub <- " l"
694663 } else {
@@ -754,16 +723,16 @@ create_body_component_l <- function(data, colwidth_df) {
754723 # (hide repeated values in all columns except the rightmost)
755724 if (has_stub_column ) {
756725 stub_vars <- dt_boxhead_get_var_stub(data = data )
757-
726+
758727 if (length(stub_vars ) > 1 && ! any(is.na(stub_vars ))) {
759728
760729 # Get original body data to check for consecutive repeating values
761730 original_body <- dt_data_get(data = data )
762-
731+
763732 # Process all stub columns except the rightmost one
764733 hierarchy_vars <- stub_vars [- length(stub_vars )]
765734 stub_matrix <- as.matrix(original_body [, hierarchy_vars , drop = FALSE ])
766-
735+
767736 # Determine which columns to hide based on hierarchical grouping
768737 for (col_idx in seq_along(hierarchy_vars )) {
769738
@@ -772,18 +741,18 @@ create_body_component_l <- function(data, colwidth_df) {
772741 if (" group_label" %in% stub_layout ) {
773742 matrix_col_idx <- col_idx + 1
774743 }
775-
744+
776745 for (row_idx in 2 : n_rows ) {
777746 should_hide <- TRUE
778-
747+
779748 # Check if current value matches previous value (handle NAs properly)
780749 curr_val <- stub_matrix [row_idx , col_idx ]
781750 prev_val <- stub_matrix [row_idx - 1 , col_idx ]
782-
751+
783752 if (! identical(curr_val , prev_val )) {
784753 should_hide <- FALSE
785754 }
786-
755+
787756 # Also check that all columns to the left match
788757 if (should_hide && col_idx > 1 ) {
789758 for (left_col_idx in 1 : (col_idx - 1 )) {
@@ -795,7 +764,7 @@ create_body_component_l <- function(data, colwidth_df) {
795764 }
796765 }
797766 }
798-
767+
799768 # Hide the value by making it empty if conditions are met
800769 if (should_hide ) {
801770 row_splits_body [[row_idx ]][matrix_col_idx ] <- " "
@@ -1471,7 +1440,7 @@ create_summary_rows_l <- function(
14711440
14721441 # Get vector representation of stub layout
14731442 stub_layout <- get_stub_layout(data = data )
1474-
1443+
14751444 # Get the actual number of stub columns (including multiple rowname columns)
14761445 stub_vars <- dt_boxhead_get_var_stub(data = data )
14771446 stub_width <- if (length(stub_vars ) == 1 && is.na(stub_vars )) 0 else length(stub_vars )
@@ -1902,6 +1871,7 @@ create_colwidth_df_l <- function(data) {
19021871
19031872 n <- dim(boxhead )[1L ]
19041873 width_df <- data.frame (
1874+ var = boxhead $ var ,
19051875 type = boxhead $ type ,
19061876 unspec = rep.int(0L , n ),
19071877 lw = rep.int(0L , n ),
@@ -1975,10 +1945,17 @@ create_colwidth_df_l <- function(data) {
19751945 width_df
19761946}
19771947
1978- calculate_multicolumn_width_text_l <- function (begins , ends , colwidth_df ) {
1979-
1948+ calculate_multicolumn_width_text_l <- function (begins , ends , col_order , colwidth_df ) {
19801949 out_text <- rep(" " , times = length(begins ))
19811950
1951+ # order by column order to ensure correct columns are used
1952+ # this is important if data order has changed, or there are hidden columns etc
1953+ colwidth_df <- col_order %> %
1954+ dplyr :: left_join(colwidth_df , by = " var" ) %> %
1955+ dplyr :: filter(type != " hidden" )
1956+
1957+
1958+
19821959 for (i in seq_along(begins )) {
19831960 ind <- seq(from = begins [i ], to = ends [i ])
19841961
0 commit comments