- Now works like bulma_column(), doesn't re-wrap level items - Export bulma_level_item() and functions for header level items - map_arg now takes multiple arguments, and drops null .args - Adds tag_[div|p|a]() - Adds c_str()master
| export(bulma_document) | export(bulma_document) | ||||
| export(bulma_helper) | export(bulma_helper) | ||||
| export(bulma_level) | export(bulma_level) | ||||
| export(bulma_level_item) | |||||
| export(bulma_level_item_header) | |||||
| export(bulma_level_items_header) | |||||
| export(bulma_modifier) | export(bulma_modifier) | ||||
| export(bulma_responsive) | export(bulma_responsive) | ||||
| export(bulma_responsive_alignment) | export(bulma_responsive_alignment) |
| ) | ) | ||||
| ret <- tag("div", list(class = str_trim(class), style = style, | ret <- tag("div", list(class = str_trim(class), style = style, | ||||
| map_arg(items, bulma_column, column_options))) | |||||
| map_arg(items, .f = bulma_column, .arg = column_options))) | |||||
| ret <- tagList(ret) | ret <- tagList(ret) | ||||
| class(ret) <- c("bulma_columns", class(ret)) | class(ret) <- c("bulma_columns", class(ret)) | ||||
| ret | ret |
| # Layout ------------------------------------------------------------------ | |||||
| #' Bulma Level | |||||
| #' | |||||
| #' A multi-purpose horizontal level that can contain almost any other element. | |||||
| #' | |||||
| #' @param ... Items to be wrapped in a `"level-item"` `<div>` and included in the | |||||
| #' main level container. If only one argument is provided and that argument is | |||||
| #' a list, the items inside the list will be treated as individual level items. | |||||
| #' @param left A list of items to be placed in the left side of the level in a | |||||
| #' `"level-left"` container. | |||||
| #' @param right A list of items to be placed in the right side of the level in a | |||||
| #' `"level-right"` container. | |||||
| #' @param type One of `"item"` or `"header"`. The default (`"item"`) places level | |||||
| #' items in the standard `"level-item"` container. Use `"header"` to use the | |||||
| #' names of the arguments in `...` as headers over their elements to create a | |||||
| #' large single-row pseudo-table. See examples for more details. | |||||
| #' @param container_tag Which tag should be used for the main `"level"` container? | |||||
| #' One of `"div"` or `"nav"`. | |||||
| #' @param class Additional classes applied to level container | |||||
| #' @param style Additional style parameters applied to level container | |||||
| #' @param level_class Additional classes applied to all level item containers (except | |||||
| #' for manually created level items). | |||||
| #' @param level_style Additional style arguments applied to all level item containers | |||||
| #' (except for manually create level items). | |||||
| #' @examples | |||||
| #' bulma_level("Home", "Menu", "Bulma", "Reservations", "Contact") | |||||
| #' bulma_level("Tweets" = 3456, Following = 123, Followers = "456K", Likes = 789, type = "header") | |||||
| #' | |||||
| #' iris_vals <- lapply(iris, function(x) length(unique(x))) | |||||
| #' bulma_level(iris_vals, type = "header") | |||||
| #' | |||||
| #' @references <https://bulma.io/documentation/layout/level/> | |||||
| #' @family Bulma layouts | |||||
| #' @export | |||||
| bulma_level <- function( | |||||
| ..., | |||||
| left = NULL, | |||||
| right = NULL, | |||||
| is_mobile = TRUE, | |||||
| type = c("item", "header"), | |||||
| container_tag = c("div", "nav"), | |||||
| class = NULL, | |||||
| style = NULL, | |||||
| level_class = NULL, | |||||
| level_style = NULL | |||||
| ) { | |||||
| type <- match.arg(type) | |||||
| tag_f <- tag_function(match.arg(container_tag)) | |||||
| x <- tag_f( | |||||
| class = c_str("level", if (is_mobile) "is-mobile", class), | |||||
| style = style, | |||||
| level_side(left, "left"), | |||||
| if (type == "item") | |||||
| tagList(lapply(dots2list(...), bulma_level_item, class = level_class, style = level_style)), | |||||
| if (type == "header") bulma_level_items_header(..., class = level_class, style = level_style), | |||||
| level_side(right, "right") | |||||
| ) | |||||
| x | |||||
| } | |||||
| #' Bulma Level Item | |||||
| #' | |||||
| #' Constructs an individual level item. | |||||
| #' | |||||
| #' @param ... Elements to be included in the level item | |||||
| #' @param class Additional classes to be applied to the level item container | |||||
| #' @param style Additional CSS style directives to be applied to the level item container | |||||
| #' @family Bulma layouts | |||||
| #' @export | |||||
| bulma_level_item <- function(..., class = NULL, style = NULL) { | |||||
| item <- dots2list(...) | |||||
| if (is_level_item(item)) return(item) | |||||
| as_level_item( | |||||
| tag_div(class = c_str("level-item", class), | |||||
| style = style, | |||||
| item) | |||||
| ) | |||||
| } | |||||
| as_level_item <- function(x) { | |||||
| if (inherits(x, "level_item")) return(x) | |||||
| class(x) <- c("level_item", class(x)) | |||||
| x | |||||
| } | |||||
| is_level_item <- function(x) inherits(x, "level_item") | |||||
| level_side <- function(x, side = "left") { | |||||
| if (is.null(x)) return(NULL) | |||||
| match.arg(side, c("left", "right")) | |||||
| items <- lapply(x, bulma_level_item) | |||||
| tag_div(class = c_prefix(side, "level-"), items) | |||||
| } | |||||
| #' Bulma Level Items with Headers | |||||
| #' | |||||
| #' Takes named arguments and converts them to | |||||
| #' (level items with headers)[bulma_level_item_header]. | |||||
| #' | |||||
| #' @inheritParams bulma_level_item | |||||
| #' @param header_class Additional classes to be applied to the header (upper) | |||||
| #' container in addition to `"heading"` | |||||
| #' @param body_class Additional classes to be applied to the title (lower) | |||||
| #' container in addition to `"title"` | |||||
| #' @family Bulma layouts | |||||
| #' @export | |||||
| bulma_level_items_header <- function(..., class = NULL, style = NULL, header_class = NULL, body_class = NULL) { | |||||
| items <- dots2list(...) | |||||
| x <- map_arg( | |||||
| .f = bulma_level_item_header, | |||||
| names(items), | |||||
| items, | |||||
| .args = list(class = if (is.null(class)) "has-text-centered" else class, | |||||
| style = style, | |||||
| header_class = header_class, | |||||
| body_class = body_class) | |||||
| ) | |||||
| tagList(x) | |||||
| } | |||||
| #' Bulma Level Item with Header | |||||
| #' | |||||
| #' Constructs an individual level item with a header (upper) and a title (lower). | |||||
| #' | |||||
| #' @param item_name The header (or name) of the item | |||||
| #' @param item_body The body of the item | |||||
| #' @inheritParams bulma_level_items_header | |||||
| #' @family Bulma layouts | |||||
| #' @export | |||||
| bulma_level_item_header <- function(item_header, item_body, class = NULL, style = NULL, header_class = NULL, body_class = NULL) { | |||||
| bulma_level_item( | |||||
| class = class, | |||||
| style = style, | |||||
| tag_div( | |||||
| tag_p(class = c_str("heading", header_class), item_header), | |||||
| tag_p(class = c_str("title", body_class), item_body) | |||||
| )) | |||||
| } |
| #' @title Bulma Level | |||||
| #' @examples | |||||
| #' bulma_level("Home", "Menu", "Bulma", "Reservations", "Contact") | |||||
| #' bulma_level("Tweets" = 3456, Following = 123, Followers = "456K", Likes = 789, style = "header") | |||||
| #' @export | |||||
| bulma_level <- function( | |||||
| ..., | |||||
| left = NULL, | |||||
| right = NULL, | |||||
| is_mobile = TRUE, | |||||
| style = c("item", "header"), | |||||
| container_tag = c("div", "nav") | |||||
| ) { | |||||
| level_item_f <- switch( | |||||
| match.arg(style), | |||||
| item = level_item, | |||||
| header = level_item_header | |||||
| ) | |||||
| tag_f <- tag_function(match.arg(container_tag)) | |||||
| x <- tag_f( | |||||
| class = paste("level", if (is_mobile) "is-mobile"), | |||||
| level_side(left), | |||||
| level_item_f(...), | |||||
| level_side(right, "right") | |||||
| ) | |||||
| x | |||||
| } | |||||
| level_item <- function(...) { | |||||
| x <- apply_tag(dots2list(...), tag = "div", class = "level-item") | |||||
| tagList(x) | |||||
| } | |||||
| level_side <- function(x, side = "left") { | |||||
| if (is.null(x)) return(NULL) | |||||
| match.arg(side, c("left", "right")) | |||||
| lapply(x, function(item) { | |||||
| htmltools::tags$div( | |||||
| class = paste0("level-", side), | |||||
| level_item(x) | |||||
| ) | |||||
| }) | |||||
| } | |||||
| #' @title Bulma Level Items With Headers | |||||
| level_item_header <- function(..., item_class = "has-text-centered", heading_class = NULL, title_class = NULL) { | |||||
| items <- dots2list(...) | |||||
| x <- mapply(level_item_header_, names(items), items, | |||||
| MoreArgs = list(item_class = item_class, | |||||
| heading_class = heading_class, | |||||
| title_class = title_class), | |||||
| SIMPLIFY = FALSE) | |||||
| tagList(x) | |||||
| } | |||||
| #' @importFrom htmltools tag | |||||
| level_item_header_ <- function(item_name, item_body, item_class = NULL, heading_class = NULL, title_class = NULL) { | |||||
| tag("div", list( | |||||
| class = paste("level-item", item_class), | |||||
| list(tag("div", list( | |||||
| tag("p", list(class = paste("heading", heading_class), item_name)), | |||||
| tag("p", list(class = paste("title", title_class), item_body)) | |||||
| ))) | |||||
| )) | |||||
| } |
| x | x | ||||
| } | } | ||||
| map_arg <- function(x, .f, .args = NULL) { | |||||
| mapply(.f, x, MoreArgs = .args, SIMPLIFY = FALSE, USE.NAMES = TRUE) | |||||
| map_arg <- function(..., .f, .args = NULL) { | |||||
| mapply(.f, ..., MoreArgs = compact(.args), SIMPLIFY = FALSE, USE.NAMES = TRUE) | |||||
| } | |||||
| compact <- function(x) { | |||||
| x[!vapply(x, is.null, logical(1))] | |||||
| } | } | ||||
| tag_function <- function(.tag = "div") { | tag_function <- function(.tag = "div") { | ||||
| function(...) htmltools::tag(.tag, list(...)) | function(...) htmltools::tag(.tag, list(...)) | ||||
| } | } | ||||
| tag_div <- tag_function("div") | |||||
| tag_p <- tag_function("p") | |||||
| tag_a <- tag_function("a") | |||||
| validate_value <- function(value = NULL, choices, several.ok = TRUE, value_name = "") { | validate_value <- function(value = NULL, choices, several.ok = TRUE, value_name = "") { | ||||
| if (!is.null(value) && length(value)) { | if (!is.null(value) && length(value)) { | ||||
| value_name <- if (nchar(value_name) > 0) glue("`{value_name}` - ") else "" | value_name <- if (nchar(value_name) > 0) glue("`{value_name}` - ") else "" | ||||
| paste0(prefix, x) | paste0(prefix, x) | ||||
| } | } | ||||
| c_str <- function(...) { | |||||
| str_trim(paste(..., sep = " ", collapse = " ")) | |||||
| } | |||||
| str_trim <- function(x) { | str_trim <- function(x) { | ||||
| x <- gsub("^\\s*|\\s*$", "", x) | x <- gsub("^\\s*|\\s*$", "", x) | ||||
| gsub("\\s+", " ", x) | gsub("\\s+", " ", x) |
| % Generated by roxygen2: do not edit by hand | % Generated by roxygen2: do not edit by hand | ||||
| % Please edit documentation in R/layout_level.R | |||||
| % Please edit documentation in R/layout.R | |||||
| \name{bulma_level} | \name{bulma_level} | ||||
| \alias{bulma_level} | \alias{bulma_level} | ||||
| \title{Bulma Level} | \title{Bulma Level} | ||||
| \usage{ | \usage{ | ||||
| bulma_level(..., left = NULL, right = NULL, is_mobile = TRUE, | bulma_level(..., left = NULL, right = NULL, is_mobile = TRUE, | ||||
| style = c("item", "header"), container_tag = c("div", "nav")) | |||||
| type = c("item", "header"), container_tag = c("div", "nav"), | |||||
| class = NULL, style = NULL, level_class = NULL, | |||||
| level_style = NULL) | |||||
| } | |||||
| \arguments{ | |||||
| \item{...}{Items to be wrapped in a \code{"level-item"} \code{<div>} and included in the | |||||
| main level container. If only one argument is provided and that argument is | |||||
| a list, the items inside the list will be treated as individual level items.} | |||||
| \item{left}{A list of items to be placed in the left side of the level in a | |||||
| \code{"level-left"} container.} | |||||
| \item{right}{A list of items to be placed in the right side of the level in a | |||||
| \code{"level-right"} container.} | |||||
| \item{type}{One of \code{"item"} or \code{"header"}. The default (\code{"item"}) places level | |||||
| items in the standard \code{"level-item"} container. Use \code{"header"} to use the | |||||
| names of the arguments in \code{...} as headers over their elements to create a | |||||
| large single-row pseudo-table. See examples for more details.} | |||||
| \item{container_tag}{Which tag should be used for the main \code{"level"} container? | |||||
| One of \code{"div"} or \code{"nav"}.} | |||||
| \item{class}{Additional classes applied to level container} | |||||
| \item{style}{Additional style parameters applied to level container} | |||||
| \item{level_class}{Additional classes applied to all level item containers (except | |||||
| for manually created level items).} | |||||
| \item{level_style}{Additional style arguments applied to all level item containers | |||||
| (except for manually create level items).} | |||||
| } | } | ||||
| \description{ | \description{ | ||||
| Bulma Level | |||||
| A multi-purpose horizontal level that can contain almost any other element. | |||||
| } | } | ||||
| \examples{ | \examples{ | ||||
| bulma_level("Home", "Menu", "Bulma", "Reservations", "Contact") | bulma_level("Home", "Menu", "Bulma", "Reservations", "Contact") | ||||
| bulma_level("Tweets" = 3456, Following = 123, Followers = "456K", Likes = 789, style = "header") | |||||
| bulma_level("Tweets" = 3456, Following = 123, Followers = "456K", Likes = 789, type = "header") | |||||
| iris_vals <- lapply(iris, function(x) length(unique(x))) | |||||
| bulma_level(iris_vals, type = "header") | |||||
| } | |||||
| \references{ | |||||
| \url{https://bulma.io/documentation/layout/level/} | |||||
| } | |||||
| \seealso{ | |||||
| Other Bulma layouts: \code{\link{bulma_level_item_header}}, | |||||
| \code{\link{bulma_level_items_header}}, | |||||
| \code{\link{bulma_level_item}} | |||||
| } | } | ||||
| \concept{Bulma layouts} |
| % Generated by roxygen2: do not edit by hand | |||||
| % Please edit documentation in R/layout.R | |||||
| \name{bulma_level_item} | |||||
| \alias{bulma_level_item} | |||||
| \title{Bulma Level Item} | |||||
| \usage{ | |||||
| bulma_level_item(..., class = NULL, style = NULL) | |||||
| } | |||||
| \arguments{ | |||||
| \item{...}{Elements to be included in the level item} | |||||
| \item{class}{Additional classes to be applied to the level item container} | |||||
| \item{style}{Additional CSS style directives to be applied to the level item container} | |||||
| } | |||||
| \description{ | |||||
| Constructs an individual level item. | |||||
| } | |||||
| \seealso{ | |||||
| Other Bulma layouts: \code{\link{bulma_level_item_header}}, | |||||
| \code{\link{bulma_level_items_header}}, | |||||
| \code{\link{bulma_level}} | |||||
| } | |||||
| \concept{Bulma layouts} |
| % Generated by roxygen2: do not edit by hand | |||||
| % Please edit documentation in R/layout.R | |||||
| \name{bulma_level_item_header} | |||||
| \alias{bulma_level_item_header} | |||||
| \title{Bulma Level Item with Header} | |||||
| \usage{ | |||||
| bulma_level_item_header(item_header, item_body, class = NULL, | |||||
| style = NULL, header_class = NULL, body_class = NULL) | |||||
| } | |||||
| \arguments{ | |||||
| \item{item_body}{The body of the item} | |||||
| \item{class}{Additional classes to be applied to the level item container} | |||||
| \item{style}{Additional CSS style directives to be applied to the level item container} | |||||
| \item{header_class}{Additional classes to be applied to the header (upper) | |||||
| container in addition to \code{"heading"}} | |||||
| \item{body_class}{Additional classes to be applied to the title (lower) | |||||
| container in addition to \code{"title"}} | |||||
| \item{item_name}{The header (or name) of the item} | |||||
| } | |||||
| \description{ | |||||
| Constructs an individual level item with a header (upper) and a title (lower). | |||||
| } | |||||
| \seealso{ | |||||
| Other Bulma layouts: \code{\link{bulma_level_items_header}}, | |||||
| \code{\link{bulma_level_item}}, \code{\link{bulma_level}} | |||||
| } | |||||
| \concept{Bulma layouts} |
| % Generated by roxygen2: do not edit by hand | |||||
| % Please edit documentation in R/layout.R | |||||
| \name{bulma_level_items_header} | |||||
| \alias{bulma_level_items_header} | |||||
| \title{Bulma Level Items with Headers} | |||||
| \usage{ | |||||
| bulma_level_items_header(..., class = NULL, style = NULL, | |||||
| header_class = NULL, body_class = NULL) | |||||
| } | |||||
| \arguments{ | |||||
| \item{...}{Elements to be included in the level item} | |||||
| \item{class}{Additional classes to be applied to the level item container} | |||||
| \item{style}{Additional CSS style directives to be applied to the level item container} | |||||
| \item{header_class}{Additional classes to be applied to the header (upper) | |||||
| container in addition to \code{"heading"}} | |||||
| \item{body_class}{Additional classes to be applied to the title (lower) | |||||
| container in addition to \code{"title"}} | |||||
| } | |||||
| \description{ | |||||
| Takes named arguments and converts them to | |||||
| (level items with headers)\link{bulma_level_item_header}. | |||||
| } | |||||
| \seealso{ | |||||
| Other Bulma layouts: \code{\link{bulma_level_item_header}}, | |||||
| \code{\link{bulma_level_item}}, \code{\link{bulma_level}} | |||||
| } | |||||
| \concept{Bulma layouts} |
| % Generated by roxygen2: do not edit by hand | |||||
| % Please edit documentation in R/layout_level.R | |||||
| \name{level_item_header} | |||||
| \alias{level_item_header} | |||||
| \title{Bulma Level Items With Headers} | |||||
| \usage{ | |||||
| level_item_header(..., item_class = "has-text-centered", | |||||
| heading_class = NULL, title_class = NULL) | |||||
| } | |||||
| \description{ | |||||
| Bulma Level Items With Headers | |||||
| } |
| context("test-layout") | |||||
| # Bulma Levels ------------------------------------------------------------ | |||||
| level_home_example <- ' | |||||
| <div class="level is-mobile"> | |||||
| <div class="level-item">Home</div> | |||||
| <div class="level-item">Menu</div> | |||||
| <div class="level-item">Bulma</div> | |||||
| <div class="level-item">Reservations</div> | |||||
| <div class="level-item">Contact</div> | |||||
| </div>' | |||||
| level_header_example <- ' | |||||
| <div class="level is-mobile"> | |||||
| <div class="level-item has-text-centered"> | |||||
| <div> | |||||
| <p class="heading">Tweets</p> | |||||
| <p class="title">3456</p> | |||||
| </div> | |||||
| </div> | |||||
| <div class="level-item has-text-centered"> | |||||
| <div> | |||||
| <p class="heading">Following</p> | |||||
| <p class="title">123</p> | |||||
| </div> | |||||
| </div> | |||||
| </div>' | |||||
| collapse_html_chr <- function(x) { | |||||
| x <- paste(x, collapse = "\n") | |||||
| gsub("\\s*\n\\s*", "", x) | |||||
| } | |||||
| expect_html_chr <- function(x, y) { | |||||
| expect_equal(collapse_html_chr(x), collapse_html_chr(y)) | |||||
| } | |||||
| test_that("bulma_level_item single", { | |||||
| expect_html_chr(bulma_level_item("one"), '<div class="level-item">one</div>') | |||||
| expect_true(is_level_item(bulma_level_item("a"))) | |||||
| expect_true(is_level_item(bulma_level_item("a", "b"))) | |||||
| expect_equal(as_level_item(bulma_level_item("a")), | |||||
| bulma_level_item("a")) | |||||
| }) | |||||
| test_that("bulma_level_item doesn't wrap other bulma_level_items", { | |||||
| expect_html_chr(bulma_level_item(bulma_level_item("one")), '<div class="level-item">one</div>') | |||||
| }) | |||||
| test_that("bulma_level_item concatenates inputs into single div", { | |||||
| expect_html_chr(bulma_level_item("A", "B"), '<div class="level-item">AB</div>') | |||||
| expect_html_chr(bulma_level_item(tag_p("A"), tag_p("B")), '<div class="level-item"><p>A</p><p>B</p></div>') | |||||
| }) | |||||
| test_that("bulma_level generally works", { | |||||
| expect_html_chr(bulma_level("Home", "Menu", "Bulma", "Reservations", "Contact"), | |||||
| level_home_example) | |||||
| expect_html_chr(bulma_level("Home", bulma_level_item("Menu"), "Bulma", "Reservations", "Contact"), | |||||
| level_home_example) | |||||
| expect_html_chr(bulma_level("Tweets" = 3456, Following = 123, type = "header"), | |||||
| level_header_example) | |||||
| }) | |||||
| test_that("bulma_level works with sides", { | |||||
| bulma_with_sides <- bulma_level(left = list(bulma_level_item("123 posts")), | |||||
| right = list("All", "Published"), | |||||
| "Normal") | |||||
| level_with_sides <- ' | |||||
| <div class="level is-mobile"> | |||||
| <div class="level-left"> | |||||
| <div class="level-item">123 posts</div> | |||||
| </div> | |||||
| <div class="level-item">Normal</div> | |||||
| <div class="level-right"> | |||||
| <div class="level-item">All</div> | |||||
| <div class="level-item">Published</div> | |||||
| </div> | |||||
| </div>' | |||||
| expect_html_chr(bulma_with_sides, level_with_sides) | |||||
| }) |