1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108
|
#' A grid-like, column-first, layout
#'
#' Wraps a 1d sequence of UI elements into a 2d grid. The number of columns (and
#' rows) in the grid dependent on the column `width` as well as the size of the
#' display. For more explanation and illustrative examples, see [here](https://rstudio.github.io/bslib/articles/cards.html#multiple-cards)
#'
#' @param ... Unnamed arguments should be UI elements (e.g., [card()])
#' Named arguments become attributes on the containing [htmltools::tag] element.
#' @param width The desired width of each card, which can be any of the
#' following:
#' * A (unit-less) number between 0 and 1.
#' * This should be specified as `1/num`, where `num` represents the number
#' of desired columns.
#' * A [CSS length unit][htmltools::validateCssUnit()]
#' * Either the minimum (when `fixed_width=FALSE`) or fixed width
#' (`fixed_width=TRUE`).
#' * `NULL`
#' * Allows power users to set the `grid-template-columns` CSS property
#' manually, either via a `style` attribute or a CSS stylesheet.
#' @param fixed_width Whether or not to interpret the `width` as a minimum
#' (`fixed_width=FALSE`) or fixed (`fixed_width=TRUE`) width when it is a CSS
#' length unit.
#' @param heights_equal If `"all"` (the default), every card in every row of the
#' grid will have the same height. If `"row"`, then every card in _each_ row
#' of the grid will have the same height, but heights may vary between rows.
#' @param fill whether or not the grid items should grow to fill the row height.
#' @param height_mobile Any valid CSS unit to use for the height when on mobile
#' devices (or narrow windows).
#' @inheritParams card
#' @inheritParams card_body
#'
#' @export
#' @examples
#'
#' x <- card("A simple card")
#' # Always has 2 columns (on non-mobile)
#' layout_column_wrap(1/2, x, x, x)
#' # Has three columns when viewport is wider than 750px
#' layout_column_wrap("250px", x, x, x)
#'
layout_column_wrap <- function(
width, ..., fixed_width = FALSE, heights_equal = c("all", "row"),
fill = TRUE, height = NULL, height_mobile = NULL, gap = NULL, class = NULL) {
heights_equal <- match.arg(heights_equal)
args <- rlang::list2(...)
argnames <- rlang::names2(args)
attribs <- args[nzchar(argnames)]
children <- args[!nzchar(argnames)]
if (length(width) > 1) {
stop("`width` of length greater than 1 is not currently supported.")
}
colspec <- if (!is.null(width)) {
if (width > 0 && width <= 1) {
num_cols <- 1 / width
if (num_cols != as.integer(num_cols)) {
stop("Could not interpret width argument; see ?layout_column_wrap")
}
paste0(rep_len("1fr", num_cols), collapse = " ")
} else {
if (fixed_width) {
paste0("repeat(auto-fit, ", validateCssUnit(width), ")")
} else {
paste0("repeat(auto-fit, minmax(", validateCssUnit(width), ", 1fr))")
}
}
}
# Wrap grid items in flex containers for essentially two reasons:
# 1. Allow fill item children (e.g. plotOutput("id", fill = TRUE))
# to fill the grid row.
# 2. Allow for fill=FALSE, which useful for allowing contents to
# shrink but not grow (i.e., default flex behavior).
children <- lapply(children, function(x) {
bindFillRole(
container = TRUE,
div(bindFillRole(div(x), container = TRUE, item = fill))
)
})
tag <- div(
class = "bslib-column-wrap",
style = css(
grid_template_columns = colspec,
grid_auto_rows = if (heights_equal == "all") "1fr",
# Always provide the `height:auto` default so that the CSS variable
# doesn't get inherited in a scenario like layout_column_wrap(height=200, ..., layout_column_wrap(...))
"--bslib-column-wrap-height" = validateCssUnit(height %||% "auto"),
"--bslib-column-wrap-height-mobile" = validateCssUnit(height_mobile %||% "auto"),
gap = validateCssUnit(gap)
),
!!!attribs,
children
)
tag <- bindFillRole(tag, item = TRUE)
tag <- tagAppendAttributes(tag, class = class)
tag <- as.card_item(tag)
as_fragment(
tag_require(tag, version = 5, caller = "layout_column_wrap()")
)
}
|