diff --git a/DESCRIPTION b/DESCRIPTION index 9dd15a2..9d5f2d2 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,7 +1,7 @@ Package: tablespan Type: Package Title: Create Satisficing 'Excel', 'HTML', 'LaTeX', and 'RTF' Tables using a Simple Formula -Version: 0.1.8 +Version: 0.2.0 Authors@R: c(person(given = "Jannik H.", family = "Orzek", role = c("aut", "cre", "cph"), email = "jannik.orzek@mailbox.org", diff --git a/NAMESPACE b/NAMESPACE index 68a1bbb..c6c56c8 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -5,11 +5,13 @@ export(as_excel) export(as_gt) export(cell_style) export(create_data_styles) +export(style_color) export(tablespan) export(tbl_styles) import(gt) import(openxlsx) importFrom(dplyr,all_of) +importFrom(grDevices,col2rgb) importFrom(gt,tab_header) importFrom(methods,is) importFrom(openxlsx,createStyle) diff --git a/R/as_excel.R b/R/as_excel.R index 17d83a0..1ca16a5 100644 --- a/R/as_excel.R +++ b/R/as_excel.R @@ -92,12 +92,6 @@ as_excel <- function(tbl, locations = locations, styles = styles) - create_outlines(tbl = tbl, - workbook = workbook, - sheet = sheet, - locations = locations, - styles = styles) - write_title(tbl = tbl, workbook = workbook, sheet = sheet, @@ -124,6 +118,13 @@ as_excel <- function(tbl, locations = locations, styles = styles) + # We create the outlines last as we may have to overwrite some border colors. + create_outlines(tbl = tbl, + workbook = workbook, + sheet = sheet, + locations = locations, + styles = styles) + return(workbook) } @@ -145,22 +146,67 @@ fill_background <- function(tbl, sheet, locations, styles){ - # To fill the background, we have to find the dimensions - # of the tabel first. - min_row <- locations$row |> unlist() |> min() - max_row <- locations$row |> unlist() |> max() - min_col <- locations$col |> unlist() |> min() - max_col <- locations$col |> unlist() |> max() - - max_row <- max_row - is.null(tbl$footnote) - + # Title + if(!is.null(tbl$title)) + openxlsx::addStyle(wb = workbook, + sheet = sheet, + style = styles$bg_title, + rows = locations$row$start_row_title:locations$row$end_row_title, + cols = locations$col$start_col_title:locations$col$end_col_title, + gridExpand = TRUE, + stack = TRUE) + # Subtitle + if(!is.null(tbl$subtitle)) + openxlsx::addStyle(wb = workbook, + sheet = sheet, + style = styles$bg_subtitle, + rows = locations$row$start_row_subtitle:locations$row$end_row_subtitle, + cols = locations$col$start_col_subtitle:locations$col$end_col_subtitle, + gridExpand = TRUE, + stack = TRUE) + # Header LHS + if(!is.null(tbl$header$lhs)) + openxlsx::addStyle(wb = workbook, + sheet = sheet, + style = styles$bg_header_lhs, + rows = locations$row$start_row_header:locations$row$end_row_header, + cols = locations$col$start_col_header_lhs:locations$col$end_col_header_lhs, + gridExpand = TRUE, + stack = TRUE) + # Header RHS openxlsx::addStyle(wb = workbook, sheet = sheet, - style = styles$background_style, - rows = min_row:max_row, - cols = min_col:max_col, + style = styles$bg_header_lhs, + rows = locations$row$start_row_header:locations$row$end_row_header, + cols = locations$col$start_col_header_rhs:locations$col$end_col_header_rhs, gridExpand = TRUE, stack = TRUE) + # Rownames + if(!is.null(tbl$header$lhs)) + openxlsx::addStyle(wb = workbook, + sheet = sheet, + style = styles$bg_rownames, + rows = locations$row$start_row_data:locations$row$end_row_data, + cols = locations$col$start_col_header_lhs:locations$col$end_col_header_lhs, + gridExpand = TRUE, + stack = TRUE) + # Data + openxlsx::addStyle(wb = workbook, + sheet = sheet, + style = styles$bg_data, + rows = locations$row$start_row_data:locations$row$end_row_data, + cols = locations$col$start_col_header_rhs:locations$col$end_col_header_rhs, + gridExpand = TRUE, + stack = TRUE) + # Footnote + if(!is.null(tbl$footnote)) + openxlsx::addStyle(wb = workbook, + sheet = sheet, + style = styles$bg_data, + rows = locations$row$start_row_footnote:locations$row$end_row_footnote, + cols = locations$col$start_col_footnote:locations$col$end_col_footnote, + gridExpand = TRUE, + stack = TRUE) } #' create_outlines @@ -190,23 +236,15 @@ create_outlines <- function(tbl, # top line openxlsx::addStyle(wb = workbook, sheet = sheet, - style = styles$hline_style, + style = styles$hline, rows = locations$row$start_row_header, cols = left_most:locations$col$end_col_header_rhs, stack = TRUE) - # line between header and data - openxlsx::addStyle(wb = workbook, - sheet = sheet, - style = styles$hline_style, - rows = locations$row$end_row_header + 1, - cols = left_most:locations$col$end_col_header_rhs, - stack = TRUE) - # bottom line openxlsx::addStyle(wb = workbook, sheet = sheet, - style = styles$hline_style, + style = styles$hline, rows = locations$row$end_row_data + 1, cols = left_most:locations$col$end_col_header_rhs, stack = TRUE) @@ -214,7 +252,7 @@ create_outlines <- function(tbl, # left line openxlsx::addStyle(wb = workbook, sheet = sheet, - style = styles$vline_style, + style = styles$vline, rows = locations$row$start_row_header:locations$row$end_row_data, cols = left_most, stack = TRUE) @@ -222,7 +260,7 @@ create_outlines <- function(tbl, # right line openxlsx::addStyle(wb = workbook, sheet = sheet, - style = styles$vline_style, + style = styles$vline, rows = locations$row$start_row_header:locations$row$end_row_data, cols = locations$col$end_col_header_rhs + 1, stack = TRUE) @@ -230,7 +268,7 @@ create_outlines <- function(tbl, # row name separator openxlsx::addStyle(wb = workbook, sheet = sheet, - style = styles$vline_style, + style = styles$vline, rows = locations$row$start_row_header:locations$row$end_row_data, cols = locations$col$start_col_header_rhs, stack = TRUE) @@ -265,7 +303,7 @@ write_title <- function(tbl, rowNames = FALSE) openxlsx::addStyle(wb = workbook, sheet = sheet, - style = styles$title_style, + style = styles$cell_title, rows = locations$row$start_row_title, cols = locations$col$start_col_title, stack = TRUE) @@ -285,7 +323,7 @@ write_title <- function(tbl, rowNames = FALSE) openxlsx::addStyle(wb = workbook, sheet = sheet, - style = styles$subtitle_style, + style = styles$cell_subtitle, rows = locations$row$start_row_subtitle, cols = locations$col$start_col_subtitle, stack = TRUE) @@ -327,8 +365,8 @@ write_header <- function(workbook, max_level = max_level, start_row = locations$row$start_row_header, start_col = locations$col$start_col_header_lhs, - header_style = styles$header_style, - vline_style = styles$vline_style) + header_style = styles$cell_header_lhs, + vline = styles$vline) }else{ max_level <- header$rhs$level @@ -340,8 +378,8 @@ write_header <- function(workbook, max_level = max_level, start_row = locations$row$start_row_header, start_col = locations$col$start_col_header_rhs, - header_style = styles$header_style, - vline_style = styles$vline_style) + header_style = styles$cell_header_rhs, + vline = styles$vline) } @@ -357,7 +395,7 @@ write_header <- function(workbook, #' @param start_row integer specifying row to write to #' @param start_col integer specifying column to write to #' @param header_style openxlsx style for the header -#' @param vline_style openxlsx style for the vertical lines in the header +#' @param vline openxlsx style for the vertical lines in the header #' @import openxlsx #' @noRd write_header_entry <- function(workbook, @@ -367,7 +405,7 @@ write_header_entry <- function(workbook, start_row, start_col, header_style, - vline_style){ + vline){ # write current entry name into table if(header_entry$name != "_BASE_LEVEL_"){ @@ -389,23 +427,6 @@ write_header_entry <- function(workbook, cols = start_col:(start_col + header_entry$width - 1), gridExpand = TRUE, stack = TRUE) - - # add vertical line to the left - openxlsx::addStyle(wb = workbook, - sheet = sheet, - style = vline_style, - rows = (start_row + (max_level - header_entry$level) - 1):(start_row + max_level - 2), - cols = start_col, - gridExpand = TRUE, - stack = TRUE) - # add vertical line to the right - openxlsx::addStyle(wb = workbook, - sheet = sheet, - style = vline_style, - rows = (start_row + (max_level - header_entry$level) - 1):(start_row + max_level - 2), - cols = (start_col + header_entry$width), - gridExpand = TRUE, - stack = TRUE) } # entries may have sub-entries, that also have to be written down @@ -418,7 +439,7 @@ write_header_entry <- function(workbook, start_row = start_row, start_col = start_col_entry, header_style = header_style, - vline_style = vline_style) + vline = vline) start_col_entry <- start_col_entry + entry$width } } @@ -453,6 +474,13 @@ write_data <- function(workbook, startRow = locations$row$end_row_header + 1, rowNames = FALSE, colNames = FALSE) + openxlsx::addStyle(wb = workbook, + sheet = sheet, + style = styles$cell_rownames, + rows = locations$row$start_row_data : (locations$row$end_row_data), + cols = locations$col$start_col_header_lhs : locations$col$end_col_header_lhs, + stack = TRUE, + gridExpand = TRUE) for(sty in styles$data_styles){ for(j in seq_len(ncol(table_data$row_data))){ @@ -483,6 +511,13 @@ write_data <- function(workbook, startRow = locations$row$end_row_header + 1, rowNames = FALSE, colNames = FALSE) + openxlsx::addStyle(wb = workbook, + sheet = sheet, + style = styles$cell_data, + rows = locations$row$start_row_data : locations$row$end_row_data, + cols = locations$col$start_col_header_rhs : locations$col$end_col_header_rhs, + stack = TRUE, + gridExpand = TRUE) for(sty in styles$data_styles){ for(j in seq_len(ncol(table_data$col_data))){ @@ -549,7 +584,7 @@ write_footnote <- function(tbl, rowNames = FALSE) openxlsx::addStyle(wb = workbook, sheet = sheet, - style = styles$footnote_style, + style = styles$cell_footnote, rows = locations$row$start_row_footnote, cols = locations$col$start_col_footnote, stack = TRUE) diff --git a/R/create_test_files.R b/R/create_test_files.R index baa8d02..af0946c 100644 --- a/R/create_test_files.R +++ b/R/create_test_files.R @@ -29,21 +29,34 @@ create_test_files_cars <- function(){ file = paste0(target_dir, "cars.xlsx"), overwrite = TRUE) + # Different colors + wb <- as_excel(tbl = tbl, styles = style_color(primary_color = "#987349")) + + openxlsx::saveWorkbook(wb, + file = paste0(target_dir, "cars_987349.xlsx"), + overwrite = TRUE) + + wb <- as_excel(tbl = tbl, styles = style_color(primary_color = "#893485")) + + openxlsx::saveWorkbook(wb, + file = paste0(target_dir, "cars_893485.xlsx"), + overwrite = TRUE) + # Complex merging of rownames summarized_table_merge <- summarized_table summarized_table_merge[ ,"vs"] <- 1 summarized_table_merge[1,"vs"] <- 0 summarized_table_merge[ , "N"] <- 1 - tbl <- tablespan(data = summarized_table_merge, - formula = Cylinder:cyl + Engine:vs + N ~ - (`Horse Power` = Mean:mean_hp + SD:sd_hp) + - (`Weight` = Mean:mean_wt + SD:sd_wt), - title = "Motor Trend Car Road Tests", - subtitle = "A table created with tablespan", - footnote = "Data from the infamous mtcars data set.") + tbl_merge <- tablespan(data = summarized_table_merge, + formula = Cylinder:cyl + Engine:vs + N ~ + (`Horse Power` = Mean:mean_hp + SD:sd_hp) + + (`Weight` = Mean:mean_wt + SD:sd_wt), + title = "Motor Trend Car Road Tests", + subtitle = "A table created with tablespan", + footnote = "Data from the infamous mtcars data set.") - wb <- as_excel(tbl = tbl) + wb <- as_excel(tbl = tbl_merge) openxlsx::saveWorkbook(wb, file = paste0(target_dir, "cars_complex_merge.xlsx"), overwrite = TRUE) diff --git a/R/tab_styles.R b/R/tab_styles.R index b37dedf..17311d3 100644 --- a/R/tab_styles.R +++ b/R/tab_styles.R @@ -2,73 +2,93 @@ #' #' Define styles for different elements of the table. #' -#' @param background_style color etc. for the entire background of the table -#' @param hline_style style for the horizontal lines used in the table. Note: -#' the style for the lines under spanners is defined in the title_style. -#' @param vline_style style for the vertical lines used in the table. Note: -#' the style for the lines under spanners is defined in the title_style. -#' @param title_style style applied to the table title -#' @param subtitle_style style applied to the table subtitle -#' @param header_style style applied to the table header (column names) #' @param merge_rownames boolean: Should adjacent rows with identical names be merged? #' @param merged_rownames_style style applied to the merged rownames #' @param footnote_style style applied to the table footnote #' @param data_styles styles applied to the columns in the data set based on their #' classes (e.g., numeric, character, etc.). data_styles must be a list of lists. #' Each inner list must have two elements: a "test" that is used to determine the -#' class of a data colum (e.g., is.double) and a style that is then applied to +#' class of a data column (e.g., is.double) and a style that is then applied to #' the columns where the test returns TRUE. Note that styles will be applied in the #' order of the list, meaning that a later style may overwrite an earlier style. #' @param cell_styles an optional list with styles for selected cells in the -#' data frame. +#' data frame. See ?cell_style. +#' @param bg_default default color for the background of the table +#' @param bg_title background color for the title +#' @param bg_subtitle background color for the subtitle +#' @param bg_header_lhs background color for the left hand side of the table header +#' @param bg_header_rhs background color for the right hand side of the table header +#' @param bg_rownames background color for the row names +#' @param bg_data background color for the data +#' @param bg_footnote background color for the footnote +#' @param vline styling for all vertical lines added to the table +#' @param hline styling for all horizontal lines added to the table +#' @param cell_default default style added to cells in the table +#' @param cell_title style added to title cells in the table +#' @param cell_subtitle style added to subtitle cells in the table +#' @param cell_header_lhs style added to the left hand side of the header cells in the table +#' @param cell_header_rhs style added to the right hand side of the header cells in the table +#' @param cell_rownames style added to row name cells in the table +#' @param cell_data style added to data cells in the table +#' @param cell_footnote style added to footnote cells in the table #' @importFrom openxlsx createStyle #' @returns a list with styles for different elements of the table #' @export #' @examples #' tbl_styles() tbl_styles <- function( - background_style = openxlsx::createStyle(fgFill = "#ffffff"), - hline_style = openxlsx::createStyle(border = "Top", - borderColour = openxlsx::openxlsx_getOp("borderColour", "black"), - borderStyle = openxlsx::openxlsx_getOp("borderStyle", "double")), - vline_style = openxlsx::createStyle(border = "Left", - borderColour = openxlsx::openxlsx_getOp("borderColour", "black"), - borderStyle = openxlsx::openxlsx_getOp("borderStyle", "double")), - title_style = openxlsx::createStyle(fontSize = 14, - halign = "left", - textDecoration = "bold"), - subtitle_style = openxlsx::createStyle(fontSize = 11, - halign = "left", - textDecoration = "bold"), - header_style = openxlsx::createStyle(fontSize = 11, - halign = "center", - border = "BottomLeftRight", - borderColour = openxlsx::openxlsx_getOp("borderColour", "black"), - borderStyle = openxlsx::openxlsx_getOp("borderStyle", "double"), - textDecoration = "bold"), + bg_default = openxlsx::createStyle(fgFill = "#ffffff"), + bg_title = bg_default, + bg_subtitle = bg_default, + bg_header_lhs = bg_default, + bg_header_rhs = bg_default, + bg_rownames = bg_default, + bg_data = bg_default, + bg_footnote = bg_default, + + vline = openxlsx::createStyle(border = "Left", + borderColour = "#000000", + borderStyle = "thin"), + + hline = openxlsx::createStyle(border = "Top", + borderColour = "#000000", + borderStyle = "thin"), + + cell_default = openxlsx::createStyle(fontSize = 11), + cell_title = openxlsx::createStyle(fontSize = 14, + halign = "left", + textDecoration = "bold"), + cell_subtitle = openxlsx::createStyle(fontSize = 11, + halign = "left", + textDecoration = "bold"), + cell_header_lhs = openxlsx::createStyle(fontSize = 11, + halign = "center", + border = "BottomLeftRight", + borderColour = "#000000", + borderStyle = "thin", + textDecoration = "bold"), + cell_header_rhs = openxlsx::createStyle(fontSize = 11, + halign = "center", + border = "BottomLeftRight", + borderColour = "#000000", + borderStyle = "thin", + textDecoration = "bold"), + cell_rownames = cell_default, + cell_data = cell_default, + cell_footnote = openxlsx::createStyle(fontSize = 11, + halign = "left"), + merge_rownames = TRUE, - merged_rownames_style = createStyle(valign = "top"), + merged_rownames_style = openxlsx::createStyle(valign = "top"), footnote_style = openxlsx::createStyle(fontSize = 11, halign = "left"), data_styles = create_data_styles(), cell_styles = NULL){ - if(!is.null(cell_styles)){ if(!is.list(cell_styles)) stop("cell_styles must be a list.") } - - return(list(background_style = background_style, - hline_style = hline_style, - vline_style = vline_style, - title_style = title_style, - subtitle_style = subtitle_style, - header_style = header_style, - merge_rownames = merge_rownames, - merged_rownames_style = merged_rownames_style, - footnote_style = footnote_style, - data_styles = data_styles, - cell_styles = cell_styles)) + return(as.list(environment())) } #' cell_style @@ -78,7 +98,7 @@ tbl_styles <- function( #' @param style style created with openxlsx::createStyle() that will be applied to #' the selected cells #' @param gridExpand see ?openxlsx::addStyle: Apply style only to the selected -#' elements (set gridExpand = FALSE) or to all combinations? +#' elements (gridExpand = FALSE, default) or to all combinations? #' @param stack should the style be added to existing styles (TRUE) or overwrite #' existing styles (FALSE) #' @returns list with specified styles @@ -107,9 +127,12 @@ tbl_styles <- function( #' style = bold)))) #' # saveWorkbook(wb, "iris.xlsx") cell_style <- function(rows, - colnames, style, gridExpand = TRUE, stack = TRUE){ + colnames, + style, + gridExpand = FALSE, + stack = TRUE){ if(!is(style, "Style")) - stop("style must be a Style created with openxlsx::createStyle") + stop("style must be a Style created with openxlsx::createStyle.") if(!is.numeric(rows)) stop("rows must be numeric") if(!is.character(colnames)) @@ -154,3 +177,152 @@ create_data_styles <- function(double = list(test = is.double, integer = integer, ...)) } + +#' style_color +#' +#' Provides a simple way to define a color scheme for tables. By default, tables +#' have a "light" theme, where the background is white and text / lines are black. +#' Based on a primary color, style_color will create tables that use the primary +#' color as background for all title, header, and row name cells and adapts the +#' text color based on the primary color. The automatic adaption of the +#' background color is implemented based on Mark Ransom and SudoPlz at +#' +#' +#' @param primary_color color to be used for the title, header, and row names +#' background. This must be a hex code for the color. +#' +#' @return a list with styling options +#' @export +#' +#' @examples +#' library(tablespan) +#' library(dplyr) +#' data("mtcars") +#' +#' # First summarize the data: +#' summarized_table <- mtcars |> +#' group_by(cyl, vs) |> +#' summarise(N = n(), +#' mean_hp = mean(hp), +#' sd_hp = sd(hp), +#' mean_wt = mean(wt), +#' sd_wt = sd(wt)) +#' +#' # Now, we want to create a table, where we show the grouping variables +#' # as row names and also create spanners for the horse power (hp) and the +#' # weight (wt) variables: +#' tbl <- tablespan(data = summarized_table, +#' formula = Cylinder:cyl + Engine:vs ~ +#' N + +#' (`Horse Power` = Mean:mean_hp + SD:sd_hp) + +#' (`Weight` = Mean:mean_wt + SD:sd_wt), +#' title = "Motor Trend Car Road Tests", +#' subtitle = "A table created with tablespan", +#' footnote = "Data from the infamous mtcars data set.") +#' +#' # We can save this table with the default color scheme: +#' wb <- as_excel(tbl = tbl) +#' +#' # Or adapt the color scheme to our liking: +#' wb <- as_excel(tbl = tbl, +#' styles = style_color(primary_color = "#2e9199")) +#' +#' # Create the excel table: +#' # openxlsx::saveWorkbook(wb, +#' # file = "cars.xlsx", +#' # overwrite = TRUE) +style_color <- function(primary_color ="#ffffff"){ + if(!grepl(pattern = "^#?([a-f0-9]{3}|[a-f0-9]{6})$", + x = primary_color)) + stop("primary_color must be a hex code") + text_color <- get_text_color(primary_color = primary_color) + + line_color <- ifelse(text_color == "#000000", + "#000000", + primary_color) + + return( + tbl_styles( + bg_default = openxlsx::createStyle(fgFill = "#ffffff"), + bg_title = openxlsx::createStyle(fgFill = primary_color), + bg_subtitle = openxlsx::createStyle(fgFill = primary_color), + bg_header_lhs = openxlsx::createStyle(fgFill = primary_color), + bg_header_rhs = openxlsx::createStyle(fgFill = primary_color), + bg_rownames = openxlsx::createStyle(fgFill = primary_color), + bg_data = openxlsx::createStyle(fgFill = "#ffffff"), + bg_footnote = openxlsx::createStyle(fgFill = "#ffffff"), + + vline = openxlsx::createStyle(border = "Left", + borderColour = line_color, + borderStyle = "thin"), + + hline = openxlsx::createStyle(border = "Top", + borderColour = line_color, + borderStyle = "thin"), + + cell_default = openxlsx::createStyle(fontSize = 11), + cell_title = openxlsx::createStyle(fontSize = 14, + halign = "left", + textDecoration = "bold", + fontColour = text_color), + cell_subtitle = openxlsx::createStyle(fontSize = 11, + halign = "left", + textDecoration = "bold", + fontColour = text_color), + cell_header_lhs = openxlsx::createStyle(fontSize = 11, + halign = "center", + border = "BottomLeftRight", + borderColour = text_color, + borderStyle = "thin", + textDecoration = "bold", + fontColour = text_color), + cell_header_rhs = openxlsx::createStyle(fontSize = 11, + halign = "center", + border = "BottomLeftRight", + borderColour = text_color, + borderStyle = "thin", + textDecoration = "bold", + fontColour = text_color), + cell_rownames = openxlsx::createStyle(fontSize = 11, + fontColour = text_color), + cell_data = openxlsx::createStyle(fontSize = 11), + cell_footnote = openxlsx::createStyle(fontSize = 11, + halign = "left"), + + merge_rownames = TRUE, + merged_rownames_style = openxlsx::createStyle(valign = "top"), + footnote_style = openxlsx::createStyle(fontSize = 11, + halign = "left"), + data_styles = create_data_styles(), + cell_styles = NULL) + ) +} + +#' get_text_color +#' +#' Determines if the text should be black or white based on the formula +#' from Mark Ransom and SudoPlz at +# +#' +#' @param primary_color color to be used for the title, header, and row names +#' background. +#' +#' @return back or white as hex code +#' @importFrom grDevices col2rgb +#' @noRd +#' +#' @examples +#' tablespan:::get_text_color("#ffffff") +get_text_color <- function(primary_color){ + rgb_colors <- as.vector(col2rgb(primary_color)) + # scale colors to be between 0 and 1: + rgb_colors <- rgb_colors/255 + rgb_colors <- sapply(rgb_colors, function(x) ifelse(x <= 0.03928, + x/12.92, + ((x + .055) / 1.055)^2.4)) + luminance <- (0.2126 * rgb_colors[1]) + (0.7152 * rgb_colors[2]) + (0.0722 * rgb_colors[3]) + + if(luminance <= .1769) + return("#ffffff") + return("#000000") +} diff --git a/R/tablespan.R b/R/tablespan.R index 64cf2d8..43bfa86 100644 --- a/R/tablespan.R +++ b/R/tablespan.R @@ -105,7 +105,7 @@ #' wb <- as_excel(tbl = tbl) #' #' # Save using openxlsx -#' # openxlsx::saveWorkbook(wb, "iris.xlsx") +#' # openxlsx::saveWorkbook(wb, "cars.xlsx") #' #' # Export as gt: #' gt_tbl <- as_gt(tbl = tbl) diff --git a/README.Rmd b/README.Rmd index 9e8e921..1596957 100644 --- a/README.Rmd +++ b/README.Rmd @@ -187,6 +187,37 @@ In `tablespan`, styling happens when translating the table to an `openxlsx` work with `as_excel`. To this end, `tablespan` provides a `styles` argument. +#### Changing the Overall Look + +The easiest way to customize tables is to change the default color scheme. +The function `tbl_styles` provides control over most elements in the table, +but in many cases `style_color` may be sufficient. The following creates a +table with teal-colored backgrounds for the title, header, and row names: + +```{r} +wb <- as_excel(tbl = tbl, + styles = style_color(primary_color = "#008080")) + +# Save the workbook as an xlsx file: +# openxlsx::saveWorkbook(wb, +# file = "cars.xlsx", +# overwrite = TRUE) +``` + +![](man/figures/tablespan_example_cars_color.png) + +Similarly, a dark background can be defined as follows: + +```{r} +wb <- as_excel(tbl = tbl, + styles = style_color(primary_color = "#000000")) + +# Save the workbook as an xlsx file: +# openxlsx::saveWorkbook(wb, +# file = "cars.xlsx", +# overwrite = TRUE) +``` + #### Formatting Cells Let's assume we want all `mean_hp` values with a value $\geq 100$ to be printed diff --git a/README.md b/README.md index eb5b7a7..85f3714 100644 --- a/README.md +++ b/README.md @@ -237,6 +237,38 @@ format numbers differently. In `tablespan`, styling happens when translating the table to an `openxlsx` workbook with `as_excel`. To this end, `tablespan` provides a `styles` argument. +#### Changing the Overall Look + +The easiest way to customize tables is to change the default color +scheme. The function `tbl_styles` provides control over most elements in +the table, but in many cases `style_color` may be sufficient. The +following creates a table with teal-colored backgrounds for the title, +header, and row names: + +``` r +wb <- as_excel(tbl = tbl, + styles = style_color(primary_color = "#008080")) + +# Save the workbook as an xlsx file: +# openxlsx::saveWorkbook(wb, +# file = "cars.xlsx", +# overwrite = TRUE) +``` + +![](man/figures/tablespan_example_cars_color.png) + +Similarly, a dark background can be defined as follows: + +``` r +wb <- as_excel(tbl = tbl, + styles = style_color(primary_color = "#000000")) + +# Save the workbook as an xlsx file: +# openxlsx::saveWorkbook(wb, +# file = "cars.xlsx", +# overwrite = TRUE) +``` + #### Formatting Cells Let’s assume we want all `mean_hp` values with a value $\geq 100$ to be diff --git a/man/cell_style.Rd b/man/cell_style.Rd index 7d8f419..a5be9a3 100644 --- a/man/cell_style.Rd +++ b/man/cell_style.Rd @@ -4,7 +4,7 @@ \alias{cell_style} \title{cell_style} \usage{ -cell_style(rows, colnames, style, gridExpand = TRUE, stack = TRUE) +cell_style(rows, colnames, style, gridExpand = FALSE, stack = TRUE) } \arguments{ \item{rows}{indices of the rows to which the style should be applied} @@ -15,7 +15,7 @@ cell_style(rows, colnames, style, gridExpand = TRUE, stack = TRUE) the selected cells} \item{gridExpand}{see ?openxlsx::addStyle: Apply style only to the selected -elements (set gridExpand = FALSE) or to all combinations?} +elements (gridExpand = FALSE, default) or to all combinations?} \item{stack}{should the style be added to existing styles (TRUE) or overwrite existing styles (FALSE)} diff --git a/man/figures/tablespan_example_cars_color.png b/man/figures/tablespan_example_cars_color.png new file mode 100644 index 0000000..04d4adb Binary files /dev/null and b/man/figures/tablespan_example_cars_color.png differ diff --git a/man/style_color.Rd b/man/style_color.Rd new file mode 100644 index 0000000..1cd4bb8 --- /dev/null +++ b/man/style_color.Rd @@ -0,0 +1,62 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/tab_styles.R +\name{style_color} +\alias{style_color} +\title{style_color} +\usage{ +style_color(primary_color = "#ffffff") +} +\arguments{ +\item{primary_color}{color to be used for the title, header, and row names +background. This must be a hex code for the color.} +} +\value{ +a list with styling options +} +\description{ +Provides a simple way to define a color scheme for tables. By default, tables +have a "light" theme, where the background is white and text / lines are black. +Based on a primary color, style_color will create tables that use the primary +color as background for all title, header, and row name cells and adapts the +text color based on the primary color. The automatic adaption of the +background color is implemented based on Mark Ransom and SudoPlz at + +} +\examples{ +library(tablespan) +library(dplyr) +data("mtcars") + +# First summarize the data: +summarized_table <- mtcars |> + group_by(cyl, vs) |> + summarise(N = n(), + mean_hp = mean(hp), + sd_hp = sd(hp), + mean_wt = mean(wt), + sd_wt = sd(wt)) + +# Now, we want to create a table, where we show the grouping variables +# as row names and also create spanners for the horse power (hp) and the +# weight (wt) variables: +tbl <- tablespan(data = summarized_table, + formula = Cylinder:cyl + Engine:vs ~ + N + + (`Horse Power` = Mean:mean_hp + SD:sd_hp) + + (`Weight` = Mean:mean_wt + SD:sd_wt), + title = "Motor Trend Car Road Tests", + subtitle = "A table created with tablespan", + footnote = "Data from the infamous mtcars data set.") + +# We can save this table with the default color scheme: +wb <- as_excel(tbl = tbl) + +# Or adapt the color scheme to our liking: +wb <- as_excel(tbl = tbl, + styles = style_color(primary_color = "#2e9199")) + +# Create the excel table: +# openxlsx::saveWorkbook(wb, +# file = "cars.xlsx", +# overwrite = TRUE) +} diff --git a/man/tablespan.Rd b/man/tablespan.Rd index 2ea9ad8..1032750 100644 --- a/man/tablespan.Rd +++ b/man/tablespan.Rd @@ -121,7 +121,7 @@ tbl wb <- as_excel(tbl = tbl) # Save using openxlsx -# openxlsx::saveWorkbook(wb, "iris.xlsx") +# openxlsx::saveWorkbook(wb, "cars.xlsx") # Export as gt: gt_tbl <- as_gt(tbl = tbl) diff --git a/man/tbl_styles.Rd b/man/tbl_styles.Rd index 9b437dd..bb91a7f 100644 --- a/man/tbl_styles.Rd +++ b/man/tbl_styles.Rd @@ -5,42 +5,75 @@ \title{tbl_styles} \usage{ tbl_styles( - background_style = openxlsx::createStyle(fgFill = "#ffffff"), - hline_style = openxlsx::createStyle(border = "Top", borderColour = - openxlsx::openxlsx_getOp("borderColour", "black"), borderStyle = - openxlsx::openxlsx_getOp("borderStyle", "double")), - vline_style = openxlsx::createStyle(border = "Left", borderColour = - openxlsx::openxlsx_getOp("borderColour", "black"), borderStyle = - openxlsx::openxlsx_getOp("borderStyle", "double")), - title_style = openxlsx::createStyle(fontSize = 14, halign = "left", textDecoration = + bg_default = openxlsx::createStyle(fgFill = "#ffffff"), + bg_title = bg_default, + bg_subtitle = bg_default, + bg_header_lhs = bg_default, + bg_header_rhs = bg_default, + bg_rownames = bg_default, + bg_data = bg_default, + bg_footnote = bg_default, + vline = openxlsx::createStyle(border = "Left", borderColour = "#000000", borderStyle = + "thin"), + hline = openxlsx::createStyle(border = "Top", borderColour = "#000000", borderStyle = + "thin"), + cell_default = openxlsx::createStyle(fontSize = 11), + cell_title = openxlsx::createStyle(fontSize = 14, halign = "left", textDecoration = "bold"), - subtitle_style = openxlsx::createStyle(fontSize = 11, halign = "left", textDecoration = + cell_subtitle = openxlsx::createStyle(fontSize = 11, halign = "left", textDecoration = "bold"), - header_style = openxlsx::createStyle(fontSize = 11, halign = "center", border = - "BottomLeftRight", borderColour = openxlsx::openxlsx_getOp("borderColour", "black"), - borderStyle = openxlsx::openxlsx_getOp("borderStyle", "double"), textDecoration = + cell_header_lhs = openxlsx::createStyle(fontSize = 11, halign = "center", border = + "BottomLeftRight", borderColour = "#000000", borderStyle = "thin", textDecoration = "bold"), + cell_header_rhs = openxlsx::createStyle(fontSize = 11, halign = "center", border = + "BottomLeftRight", borderColour = "#000000", borderStyle = "thin", textDecoration = + "bold"), + cell_rownames = cell_default, + cell_data = cell_default, + cell_footnote = openxlsx::createStyle(fontSize = 11, halign = "left"), merge_rownames = TRUE, - merged_rownames_style = createStyle(valign = "top"), + merged_rownames_style = openxlsx::createStyle(valign = "top"), footnote_style = openxlsx::createStyle(fontSize = 11, halign = "left"), data_styles = create_data_styles(), cell_styles = NULL ) } \arguments{ -\item{background_style}{color etc. for the entire background of the table} +\item{bg_default}{default color for the background of the table} + +\item{bg_title}{background color for the title} + +\item{bg_subtitle}{background color for the subtitle} + +\item{bg_header_lhs}{background color for the left hand side of the table header} + +\item{bg_header_rhs}{background color for the right hand side of the table header} + +\item{bg_rownames}{background color for the row names} + +\item{bg_data}{background color for the data} + +\item{bg_footnote}{background color for the footnote} + +\item{vline}{styling for all vertical lines added to the table} + +\item{hline}{styling for all horizontal lines added to the table} + +\item{cell_default}{default style added to cells in the table} + +\item{cell_title}{style added to title cells in the table} + +\item{cell_subtitle}{style added to subtitle cells in the table} -\item{hline_style}{style for the horizontal lines used in the table. Note: -the style for the lines under spanners is defined in the title_style.} +\item{cell_header_lhs}{style added to the left hand side of the header cells in the table} -\item{vline_style}{style for the vertical lines used in the table. Note: -the style for the lines under spanners is defined in the title_style.} +\item{cell_header_rhs}{style added to the right hand side of the header cells in the table} -\item{title_style}{style applied to the table title} +\item{cell_rownames}{style added to row name cells in the table} -\item{subtitle_style}{style applied to the table subtitle} +\item{cell_data}{style added to data cells in the table} -\item{header_style}{style applied to the table header (column names)} +\item{cell_footnote}{style added to footnote cells in the table} \item{merge_rownames}{boolean: Should adjacent rows with identical names be merged?} @@ -51,12 +84,12 @@ the style for the lines under spanners is defined in the title_style.} \item{data_styles}{styles applied to the columns in the data set based on their classes (e.g., numeric, character, etc.). data_styles must be a list of lists. Each inner list must have two elements: a "test" that is used to determine the -class of a data colum (e.g., is.double) and a style that is then applied to +class of a data column (e.g., is.double) and a style that is then applied to the columns where the test returns TRUE. Note that styles will be applied in the order of the list, meaning that a later style may overwrite an earlier style.} \item{cell_styles}{an optional list with styles for selected cells in the -data frame.} +data frame. See ?cell_style.} } \value{ a list with styles for different elements of the table diff --git a/tests/testthat/test-excel-cars.R b/tests/testthat/test-excel-cars.R index 631d83e..b0e6ab4 100644 --- a/tests/testthat/test-excel-cars.R +++ b/tests/testthat/test-excel-cars.R @@ -41,6 +41,92 @@ test_that("cars", { }) +test_that("cars - color", { + library(tablespan) + library(testthat) + library(dplyr) + library(openxlsx) + + comp_file_dir <- paste0(testthat::test_path(), "/xlsx_files/") + + summarized_table <- mtcars |> + group_by(cyl, vs) |> + summarise(N = n(), + mean_hp = mean(hp), + sd_hp = sd(hp), + mean_wt = mean(wt), + sd_wt = sd(wt)) + + tbl <- tablespan(data = summarized_table, + formula = Cylinder:cyl + Engine:vs ~ + N + + (`Horse Power` = Mean:mean_hp + SD:sd_hp) + + (`Weight` = Mean:mean_wt + SD:sd_wt), + title = "Motor Trend Car Road Tests", + subtitle = "A table created with tablespan", + footnote = "Data from the infamous mtcars data set.") + + wb <- as_excel(tbl = tbl, styles = style_color(primary_color = "#987349")) + + # Compare just the data + xlsx_compare <- openxlsx::read.xlsx(xlsxFile = paste0(comp_file_dir, "cars_987349.xlsx")) + testthat::expect_true(all.equal(openxlsx::read.xlsx(wb), xlsx_compare)) + + # to compare workbooks, we have to write and reload the xlsx file + tmp_dir <- tempdir() + openxlsx::saveWorkbook(wb, + file = paste0(tmp_dir, "/cars_test.xlsx"), + overwrite = TRUE) + + wb <- openxlsx::loadWorkbook(file = paste0(tmp_dir, "/cars_test.xlsx")) + wb_compare <- openxlsx::loadWorkbook(file = paste0(comp_file_dir, "cars.xlsx")) + testthat::expect_true(all.equal(wb$worksheets, wb_compare$worksheets)) + +}) + +test_that("cars - color - 2", { + library(tablespan) + library(testthat) + library(dplyr) + library(openxlsx) + + comp_file_dir <- paste0(testthat::test_path(), "/xlsx_files/") + + summarized_table <- mtcars |> + group_by(cyl, vs) |> + summarise(N = n(), + mean_hp = mean(hp), + sd_hp = sd(hp), + mean_wt = mean(wt), + sd_wt = sd(wt)) + + tbl <- tablespan(data = summarized_table, + formula = Cylinder:cyl + Engine:vs ~ + N + + (`Horse Power` = Mean:mean_hp + SD:sd_hp) + + (`Weight` = Mean:mean_wt + SD:sd_wt), + title = "Motor Trend Car Road Tests", + subtitle = "A table created with tablespan", + footnote = "Data from the infamous mtcars data set.") + + wb <- as_excel(tbl = tbl, styles = style_color(primary_color = "#893485")) + + # Compare just the data + xlsx_compare <- openxlsx::read.xlsx(xlsxFile = paste0(comp_file_dir, "cars_893485.xlsx")) + testthat::expect_true(all.equal(openxlsx::read.xlsx(wb), xlsx_compare)) + + # to compare workbooks, we have to write and reload the xlsx file + tmp_dir <- tempdir() + openxlsx::saveWorkbook(wb, + file = paste0(tmp_dir, "/cars_test.xlsx"), + overwrite = TRUE) + + wb <- openxlsx::loadWorkbook(file = paste0(tmp_dir, "/cars_test.xlsx")) + wb_compare <- openxlsx::loadWorkbook(file = paste0(comp_file_dir, "cars.xlsx")) + testthat::expect_true(all.equal(wb$worksheets, wb_compare$worksheets)) + +}) + test_that("cars-offset", { library(tablespan) library(testthat) diff --git a/tests/testthat/xlsx_files/cars.xlsx b/tests/testthat/xlsx_files/cars.xlsx index 9dbb9d3..6de3ee1 100644 Binary files a/tests/testthat/xlsx_files/cars.xlsx and b/tests/testthat/xlsx_files/cars.xlsx differ diff --git a/tests/testthat/xlsx_files/cars_893485.xlsx b/tests/testthat/xlsx_files/cars_893485.xlsx new file mode 100644 index 0000000..c2a6ca4 Binary files /dev/null and b/tests/testthat/xlsx_files/cars_893485.xlsx differ diff --git a/tests/testthat/xlsx_files/cars_987349.xlsx b/tests/testthat/xlsx_files/cars_987349.xlsx new file mode 100644 index 0000000..48d8446 Binary files /dev/null and b/tests/testthat/xlsx_files/cars_987349.xlsx differ diff --git a/tests/testthat/xlsx_files/cars_additional_spanners.xlsx b/tests/testthat/xlsx_files/cars_additional_spanners.xlsx index 3d9d08f..b966752 100644 Binary files a/tests/testthat/xlsx_files/cars_additional_spanners.xlsx and b/tests/testthat/xlsx_files/cars_additional_spanners.xlsx differ diff --git a/tests/testthat/xlsx_files/cars_additional_spanners_left_right.xlsx b/tests/testthat/xlsx_files/cars_additional_spanners_left_right.xlsx index 9425abd..ae920ad 100644 Binary files a/tests/testthat/xlsx_files/cars_additional_spanners_left_right.xlsx and b/tests/testthat/xlsx_files/cars_additional_spanners_left_right.xlsx differ diff --git a/tests/testthat/xlsx_files/cars_cell_styles.xlsx b/tests/testthat/xlsx_files/cars_cell_styles.xlsx index 4e8d23c..09a0494 100644 Binary files a/tests/testthat/xlsx_files/cars_cell_styles.xlsx and b/tests/testthat/xlsx_files/cars_cell_styles.xlsx differ diff --git a/tests/testthat/xlsx_files/cars_complex_merge.xlsx b/tests/testthat/xlsx_files/cars_complex_merge.xlsx index 6ecceb5..d823335 100644 Binary files a/tests/testthat/xlsx_files/cars_complex_merge.xlsx and b/tests/testthat/xlsx_files/cars_complex_merge.xlsx differ diff --git a/tests/testthat/xlsx_files/cars_data_styles.xlsx b/tests/testthat/xlsx_files/cars_data_styles.xlsx index b8c303c..f53e603 100644 Binary files a/tests/testthat/xlsx_files/cars_data_styles.xlsx and b/tests/testthat/xlsx_files/cars_data_styles.xlsx differ diff --git a/tests/testthat/xlsx_files/cars_no_row_names.xlsx b/tests/testthat/xlsx_files/cars_no_row_names.xlsx index b5da335..54d2486 100644 Binary files a/tests/testthat/xlsx_files/cars_no_row_names.xlsx and b/tests/testthat/xlsx_files/cars_no_row_names.xlsx differ diff --git a/tests/testthat/xlsx_files/cars_no_titles.xlsx b/tests/testthat/xlsx_files/cars_no_titles.xlsx index cb42c92..4fccb92 100644 Binary files a/tests/testthat/xlsx_files/cars_no_titles.xlsx and b/tests/testthat/xlsx_files/cars_no_titles.xlsx differ diff --git a/tests/testthat/xlsx_files/cars_no_titles_no_footnote.xlsx b/tests/testthat/xlsx_files/cars_no_titles_no_footnote.xlsx index b0e4bfc..e0297ab 100644 Binary files a/tests/testthat/xlsx_files/cars_no_titles_no_footnote.xlsx and b/tests/testthat/xlsx_files/cars_no_titles_no_footnote.xlsx differ diff --git a/tests/testthat/xlsx_files/cars_offset.xlsx b/tests/testthat/xlsx_files/cars_offset.xlsx index cfc9099..55d73d7 100644 Binary files a/tests/testthat/xlsx_files/cars_offset.xlsx and b/tests/testthat/xlsx_files/cars_offset.xlsx differ