Skip to content

Commit

Permalink
Merge pull request #41 from JulienBlasco/new-styling-method
Browse files Browse the repository at this point in the history
New styling method
  • Loading branch information
ddotta authored Oct 16, 2024
2 parents 3d9074c + d13ba17 commit 66613f5
Show file tree
Hide file tree
Showing 14 changed files with 478 additions and 32 deletions.
3 changes: 3 additions & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

export(add_table)
export(toxlsx)
export(xls_theme)
export(xls_theme_default)
export(xls_theme_plain)
import(utils)
importFrom(cli,cli_alert_success)
importFrom(magrittr,"%>%")
Expand Down
3 changes: 2 additions & 1 deletion NEWS.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
# tablexlsx (WIP)

* (fix) `toxlsx()` no longer fails when the `object` argument is the result of a computation (#18)
* new method for styling tables: `toxlsx()` now accepts a `theme` argument, which has to be supplied as an object returned by `xls_theme()` functions. Some themes are provided by default: `xls_theme_default()` and `xls_theme_plain()`. The default theme has been slightly changed. (#40)
* provide meaningful error message if merge cols don't exist (#20)
* `path` can now be supplied as a file name with full path instead of a directory name (#29)
* (fix) `toxlsx()` no longer fails when the `object` argument is the result of a computation (#18)

# tablexlsx 1.0.0

Expand Down
27 changes: 15 additions & 12 deletions R/add_table.R
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#' @param StartRow : export start line number in the sheet (by default 1)
#' @param StartCol : export start column number in the sheet (by default 1)
#' @param FormatList : list that indicates the format of each column of the data frame
#' @param Theme : styling theme, a named list of `openxlsx` Styles
#' @param HeightTableTitle : multiplier (if needed) for the height of the title line (by default 2)
#' @param TableFootnote1 : string for TableFootnote1
#' @param TableFootnote2 : string for TableFootnote2
Expand All @@ -28,7 +29,8 @@ add_table <- function(
TableTitle,
StartRow = 1,
StartCol = 1,
FormatList = list(),
FormatList = setNames(rep(list(Theme[["character"]]), length(colnames(Table))), colnames(Table)),
Theme = xls_theme_default(),
HeightTableTitle = 2,
TableFootnote1 = "",
TableFootnote2 = "",
Expand Down Expand Up @@ -68,9 +70,7 @@ add_table <- function(


# Adjusting the size of columns and rows
openxlsx::setColWidths(WbTitle, sheet = mysheet, cols = StartCol + 1, widths = 45)
openxlsx::setColWidths(WbTitle, sheet = mysheet, cols = StartCol + 2, widths = 30)
openxlsx::setColWidths(WbTitle, sheet = mysheet, cols = c(StartCol + 3:38), widths = 20)
openxlsx::setColWidths(WbTitle, sheet = mysheet, cols = StartCol + 1:(ncol(Table)-length(ByGroup)), widths = 20)

# Size of column headers
openxlsx::setRowHeights(WbTitle, sheet = mysheet, rows = StartRow + 2, heights = 20 * HeightTableTitle)
Expand All @@ -92,7 +92,7 @@ add_table <- function(
sheet = mysheet,
cols = StartCol,
rows = StartRow,
style = style$title
style = Theme$title
)

if (isTRUE(asTable)) {
Expand All @@ -110,7 +110,7 @@ add_table <- function(
startRow = StartRow + 2,
startCol = StartCol + 1,
rowNames = FALSE,
headerStyle = style$col_header
headerStyle = Theme$col_header
)
lastrowtable <- StartRow + 2 + nrow(Table)
} else {
Expand All @@ -121,13 +121,16 @@ add_table <- function(
startRow = StartRow + 2,
startCol = StartCol + 1,
rowNames = FALSE,
headerStyle = style$col_header,
headerStyle = Theme$col_header,
group = ByGroup,
groupname = GroupName,
)
lastrowtable <- StartRow + 2 + nrow(Table) + nrow(unique(Table[ByGroup]))
}

# Remove grouping columns from the list of formats
FormatList[colnames(Table) %in% ByGroup] <- NULL

# Format of the table's columns
sapply(seq_len(length(FormatList)), function(i) {
openxlsx::addStyle(
Expand All @@ -149,7 +152,7 @@ add_table <- function(
WbTitle,
sheet = mysheet,
cols = StartCol, rows = lastrowtable + 2,
style = style$footnote1
style = Theme$footnote1
)

openxlsx::writeData(
Expand All @@ -161,7 +164,7 @@ add_table <- function(
WbTitle,
sheet = mysheet,
cols = StartCol, rows = lastrowtable + 3,
style = style$footnote2
style = Theme$footnote2
)

openxlsx::writeData(
Expand All @@ -173,7 +176,7 @@ add_table <- function(
WbTitle,
sheet = mysheet,
cols = StartCol, rows = lastrowtable + 4,
style = style$footnote3
style = Theme$footnote3
)

# If mergecol is filled in
Expand All @@ -188,7 +191,7 @@ add_table <- function(
# loop on each modality of mycol
for (i in (1:distinct_mergecol)) {

mergeCells(wb = WbTitle,
openxlsx::mergeCells(wb = WbTitle,
sheet = mysheet,
# here we add 1 because the table starts to be written from col 2 in workbook
cols = which(names(Table) %in% mycol)+1,
Expand All @@ -206,7 +209,7 @@ add_table <- function(
rows = convert_range_string(
get_indices_from_vector(Table[[mycol]])
) + StartRow + 2,
style = style$mergedcell
style = Theme$mergedcell
)

}
Expand Down
16 changes: 16 additions & 0 deletions R/asserts.R
Original file line number Diff line number Diff line change
Expand Up @@ -110,3 +110,19 @@ assert_grouped <- function(x) {
stop(substitute(x), " must not be grouped", call. = FALSE)
}
}

#' @name assert_xls_theme
#'
#' @param x Object
#'
#' @noRd
assert_xls_theme <- function(x) {
if (!all(vapply(x, FUN = inherits, FUN.VALUE = logical(1L), "Style"))) {
stop(substitute(x), " must be a list of elements of class Style", call. = FALSE)
}
necessary_elements <- c("title", "col_header", "character", "footnote1", "footnote2", "footnote3", "mergedcell")
missing_elements <- setdiff(necessary_elements, names(x))
if (length(missing_elements) > 0) {
stop(substitute(x), " must contain styles for elements ", paste(missing_elements, collapse=" "), call. = FALSE)
}
}
221 changes: 221 additions & 0 deletions R/themes.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,221 @@
#' @name xls_theme
#'
#' @title Constructor function for xls themes
#'
#' @description
#' This function creates an xls theme for styling exported tables.
#' All its arguments must be `openxlsx` Style objects.
#'
#' @param title Style for the title
#' @param col_header Style for the columns header
#' @param character Default style for data cells
#' @param footnote1 Style for footnote1
#' @param footnote2 Style for footnote2
#' @param footnote3 Style for footnote3
#' @param mergedcell Style for merged cells
#' @param ... Other (named) custom styles
#'
#' @return a named list of class xls_theme, whose elements are `openxlsx` Style objects.
#' @export
#'
#' @seealso \code{\link[tablexlsx:xls_theme_plain]{xls_theme_plain()}},
#' \code{\link[tablexlsx:xls_theme_default]{xls_theme_default()}}
#'
#' @examples
#' my_theme <- xls_theme(
#' title = openxlsx::createStyle(),
#' col_header = openxlsx::createStyle(),
#' character = openxlsx::createStyle(),
#' footnote1 = openxlsx::createStyle(),
#' footnote2 = openxlsx::createStyle(),
#' footnote3 = openxlsx::createStyle(),
#' mergedcell = openxlsx::createStyle()
#' )
#'
#' \dontrun{
#' toxlsx(object = iris, path = tempdir(), theme = my_theme)
#' }
xls_theme <- function(title,
col_header,
character,
footnote1,
footnote2,
footnote3,
mergedcell,
...) {
theme <- list(title = title,
col_header = col_header,
character = character,
footnote1 = footnote1,
footnote2 = footnote2,
footnote3 = footnote3,
mergedcell = mergedcell,
...)
class(theme) <- "xls_theme"
assert_xls_theme(theme)
theme
}

#' @name xls_theme_plain
#'
#' @title Constructor function for a plain xls theme
#'
#' @description
#' This function is a wrapper around [xls_theme()] that creates an xls theme for styling exported tables.
#' It defines a simple theme whith no special formatting.
#' All its arguments must be `openxlsx` Style objects.
#'
#' @param title Style for the title
#' @param col_header Style for the columns header
#' @param character Default style for data cells
#' @param footnote1 Style for footnote1
#' @param footnote2 Style for footnote2
#' @param footnote3 Style for footnote3
#' @param mergedcell Style for merged cells
#' @param ... Other (named) custom styles
#'
#' @return a named list of class xls_theme, whose elements are `openxlsx` Style objects.
#' @export
#'
#' @seealso \code{\link[tablexlsx:xls_theme]{xls_theme()}},
#' \code{\link[tablexlsx:xls_theme_default]{xls_theme_default()}}
#'
#' @examples
#' # plain theme
#' xls_theme_plain()
#'
#' # plain theme with title in bold
#' my_theme <- xls_theme_plain(title = openxlsx::createStyle(textDecoration = "bold"))
#'
#' \dontrun{
#' toxlsx(object = iris, path = tempdir(), theme = my_theme)
#' }
xls_theme_plain = function(
title = openxlsx::createStyle(),
col_header = openxlsx::createStyle(),
character = openxlsx::createStyle(),
footnote1 = openxlsx::createStyle(),
footnote2 = openxlsx::createStyle(),
footnote3 = openxlsx::createStyle(),
mergedcell = openxlsx::createStyle(),
...
) {
xls_theme(
title = title,
col_header = col_header,
character = character,
footnote1 = footnote1,
footnote2 = footnote2,
footnote3 = footnote3,
mergedcell = mergedcell,
...
)
}

#' @name xls_theme_default
#'
#' @title Constructor function for the default xls theme
#'
#' @description
#' This function is a wrapper around [xls_theme()] that creates an xls theme for styling exported tables.
#' It defines a theme whith sensible default formatting values.
#' It also defines custom styles for "number", "decimal" and "percent column types.
#' All its arguments must be `openxlsx` Style objects.
#'
#' @param title Style for the title
#' @param col_header Style for the columns header
#' @param character Default style for data cells
#' @param number Style for columns in number format
#' @param decimal Style for columns in decimal format
#' @param percent Style for columns in percent format
#' @param footnote1 Style for footnote1
#' @param footnote2 Style for footnote2
#' @param footnote3 Style for footnote3
#' @param mergedcell Style for merged cells
#' @param ... Other (named) custom styles
#'
#' @return a named list of class xls_theme, whose elements are `openxlsx` Style objects.
#' @export
#'
#' @seealso \code{\link[tablexlsx:xls_theme]{xls_theme()}},
#' \code{\link[tablexlsx:xls_theme_plain]{xls_theme_plain()}}
#'
#' @examples
#' # default theme
#' xls_theme_default()
#'
#' # default theme with title in italic
#' my_theme <- xls_theme_default(title = openxlsx::createStyle(textDecoration = "italic"))
#'
#' \dontrun{
#' toxlsx(object = iris, path = tempdir(), theme = my_theme)
#' }
xls_theme_default = function(
title = openxlsx::createStyle(fontSize = 16, textDecoration = "bold"),
# For footnote1
footnote1 = openxlsx::createStyle(fontSize = 12),
# For footnote2
footnote2 = openxlsx::createStyle(fontSize = 12),
# For footnote3
footnote3 = openxlsx::createStyle(fontSize = 12),
# For column headers
col_header = openxlsx::createStyle(
fontSize = 12,
textDecoration = "bold",
border = c("top", "bottom", "left", "right"),
borderStyle = "thin",
wrapText = TRUE,
halign = "center"
),
# For cells in character format
character = openxlsx::createStyle(
fontSize = 12,
border = c("top", "bottom", "left", "right"),
borderStyle = "thin"
),
# For cells in number format (with thousands separator)
number = openxlsx::createStyle(
fontSize = 12,
numFmt = "### ### ### ##0",
border = c("top", "bottom", "left", "right"),
borderStyle = "thin"
),
# For cells in number format with decimals (and with thousands separator)
decimal = openxlsx::createStyle(
fontSize = 12,
numFmt = "### ### ### ##0.0",
border = c("top", "bottom", "left", "right"),
borderStyle = "thin"
),
# For cells in percentage format (centered)
percent = openxlsx::createStyle(
fontSize = 12,
numFmt = "#0.0",
border = c("top", "bottom", "left", "right"),
borderStyle = "thin",
halign = "center"
),
mergedcell = openxlsx::createStyle(
fontSize = 12,
border = c("top", "bottom", "left", "right"),
borderStyle = "thin",
wrapText = TRUE,
valign = "center",
halign = "center"
),
...
) {
xls_theme_plain(
title = title,
col_header = col_header,
character = character,
number = number,
decimal = decimal,
percent = percent,
footnote1 = footnote1,
footnote2 = footnote2,
footnote3 = footnote3,
mergedcell = mergedcell,
...
)
}
Loading

0 comments on commit 66613f5

Please sign in to comment.