Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ export(navs_tab)
export(navs_tab_card)
export(page)
export(page_fill)
export(page_fillable)
export(page_fixed)
export(page_fluid)
export(page_navbar)
Expand Down
25 changes: 20 additions & 5 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,34 @@
# bslib 0.4.2.9000

This significant release adds [sidebar layouts](https://rstudio.github.io/bslib/articles/sidebars.html), [accordions](https://rstudio.github.io/bslib/articles/sidebars.html#sidebar-accordions), and makes many improvements to make dashboard-like filling layouts easier to create.

Although `{bslib}` is still maturing, and will continue to receiving new UI features, we now see it as a viable replacement for `{shinydashboard}`.

## Breaking changes

* `page_fill()` now produces a `<body>` tag with `display:flex` (instead of `display:block`). It also no longer fills the windows height on mobile (i.e., narrow screens) by default. If this breaks existing behavior, consider using `shiny::fillPage(theme = bslib::bs_theme(), ...)` instead of `page_fill()`. (#479)
* `page_navbar()` (and consequently `shiny::navbarPage()`) no longer implicitly wrap `header` and `footer` in an additional `shiny::fluidRow()` container for Bootstrap 5+ (i.e., `theme = bs_theme()`). Similarly, `navs_bar()` no longer does this (for any version of Bootstrap). If this breaks existing behavior, consider wrapping the `header` and `footer` value(s) with `shiny::fluidRow()`). (#479)
* `card_body()` now provides the same behavior as `card_body_fill()` (i.e., it is both a fillable container and fill item) by default. And, now, since `card_body()` can do everything `card_body_fill()` can do, `card_body_fill()` has been deprecated. The main benefit of this change is that `card(full_screen = TRUE, ...)` with output(s) passed to `...` "just works" in an intuitive way. To revert to the previous behavior, set `fillable = FALSE` and `fill = FALSE` in calls to `card_body()` and set `wrapper = function(x) card_body(x, fillable = FALSE, fill = FALSE)` in calls to `card()`. (#498)
* `layout_column_wrap()`'s `fill` argument now controls whether or not the _layout container_ is allowed to grow/shrink to fit a fillable container (e.g., `page_fillable()`). It also gains a new `fillable` argument for controlling whether _UI elements_ are allowed to fill their row height. This is more consistent with the meaning of `fill` in other functions, like `card()`, `card_body()`, `layout_sidebar()`, etc. (#498)
* `page_fill()` (now called `page_fillable()`) had several breaking changes (listed below). If this breaks existing behavior, consider using `shiny::fillPage(theme = bslib::bs_theme(), ...)` instead of `page_fill()`.
* `page_fill()` now produces a `<body>` tag with `display:flex` (instead of `display:block`).
* `page_fill()` no longer fills the windows height on mobile (i.e., narrow screens) by default (set `fill_mobile = TRUE` to restore the old behavior).
* `page_fill()` now adds `padding` and `gap` by default, set `padding = 0` and `gap = 0` to restore the old behavior.
* `page_navbar()` (and consequently `shiny::navbarPage()`) no longer implicitly wrap `header` and `footer` in an additional `shiny::fluidRow()` container for Bootstrap 5+ (i.e., `theme = bs_theme()`) usage. Similarly, `navs_bar()` no longer does this (for any version of Bootstrap). If this breaks existing behavior, consider wrapping the `header` and `footer` value(s) with `shiny::fluidRow()`). (#479)
* Defaults for the following Bootstrap 5 Sass variables were changed to `null`: `$accordion-button-active-bg`, `$accordion-button-active-color`, and `$accordion-icon-active-color`. To restore the old behavior, do `bs_add_variables(theme, "accordion-button-active-bg" = "tint-color($component-active-bg, 90%)", "accordion-button-active-color" = "shade-color($primary, 10%)", "accordion-icon-active-color" = "$accordion-button-active-color", .where = "declarations")`. (#475)

## New features

* Added a `sidebar()` API for creating sidebar layouts in various contexts. See [the article](https://rstudio.github.io/bslib/articles/sidebars.html) to learn more. (#479)
* Adds a new `accordion()` API. See `help(accordion)` for examples and details. Note also `accordion()` is designed to [work well inside a `sidebar()`](https://rstudio.github.io/bslib/articles/sidebars.html#accordions). (#475)
* Adds a new `accordion()` API. See `help(accordion)` for examples and details. Note also `accordion()` is designed to [work well inside a `sidebar()`](https://rstudio.github.io/bslib/articles/sidebars.html#sidebar-accordions). (#475)
* `page_navbar()`, `navs_tab_card()`, and ` navs_pill_card()` gain a `sidebar` argument for putting a `sidebar()` on every page/tab/pill. (#479)
* `page_navbar()` gains a `fillable` argument to make the content of particular page(s) fit the window/card. (#479)
* `page_fill()` is now considered a `fillable` container, meaning that `fill` items like `card()`, `layout_column_wrap()`, and `layout_sidebar()` now grow/shrink to fit the window's height when they appear as a direct child of `page_fill()`. (#479)
* `page_navbar()` and `page_fill()` gain `fill_mobile` arguments to control whether the page should grow/shrink to fit the viewport on mobile. (#479)
* `page_fillable()` (aka, `page_fill()`) is now considered a `fillable` container, meaning that `fill` items like `card()`, `layout_column_wrap()`, and `layout_sidebar()` now grow/shrink to fit the window's height when they appear as a direct child of `page_fillable()`. (#479)
* `page_navbar()` and `page_fillable()` gain `fill_mobile` arguments to control whether the page should grow/shrink to fit the viewport on mobile. (#479)
* `card()`, `value_box()`, and `card_image()` gain `max_height` and `fill` arguments. (#498)

## Deprecations

* `card_body_fill()` has been deprecated in favor of `card_body()`. (#498)
* `page_fill()` has been deprecated in favor of `page_fillable()`. (#498)


# bslib 0.4.2
Expand Down
106 changes: 46 additions & 60 deletions R/card.R
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,22 @@
#' tag][htmltools::tags] (which includes card items such as [card_body()].
#' Named arguments become HTML attributes on returned UI element.
#' @param full_screen If `TRUE`, an icon will appear when hovering over the card
#' body. Clicking the icon expands the card to fit viewport size. Consider
#' pairing this feature with [card_body_fill()] to get output that responds to
#' changes in the size of the card.
#' body. Clicking the icon expands the card to fit viewport size.
#' @param height Any valid [CSS unit][htmltools::validateCssUnit] (e.g.,
#' `height="200px"`).
#' `height="200px"`). Doesn't apply when a card is made `full_screen`
#' (in this case, consider setting a `height` in [card_body()]).
#' @param max_height Any valid [CSS unit][htmltools::validateCssUnit] (e.g.,
#' `max_height="200px"`). Doesn't apply when a card is made `full_screen`
#' (in this case, consider setting a `max_height` in [card_body()]).
#' @param fill Whether or not to allow the card to grow/shrink to fit a
#' fillable container with an opinionated height (e.g., `page_fillable()`).
#' @param class Additional CSS classes for the returned UI element.
#' @param wrapper A function (which returns a UI element) to call on unnamed
#' arguments in `...` which are not already card item(s) (like
#' [card_header()], [card_body()], etc.). Note that non-card items are grouped
#' together into one `wrapper` call (e.g. given `card("a", "b",
#' card_body("c"), "d")`, `wrapper` would be called twice, once with `"a"` and
#' `"b"` and once with `"d"`). Consider setting `wrapper` to [card_body_fill]
#' if the entire card wants responsive sizing or `NULL` to avoid wrapping
#' altogether
#' `"b"` and once with `"d"`).
#'
#' @return A [htmltools::div()] tag.
#'
Expand Down Expand Up @@ -50,7 +52,7 @@
#' )
#' }
#'
card <- function(..., full_screen = FALSE, height = NULL, class = NULL, wrapper = card_body) {
card <- function(..., full_screen = FALSE, height = NULL, max_height = NULL, fill = TRUE, class = NULL, wrapper = card_body) {

args <- rlang::list2(...)
argnames <- rlang::names2(args)
Expand All @@ -60,13 +62,16 @@ card <- function(..., full_screen = FALSE, height = NULL, class = NULL, wrapper

tag <- div(
class = "card bslib-card",
style = css(height = validateCssUnit(height)),
style = css(
height = validateCssUnit(height),
max_height = validateCssUnit(max_height)
),
!!!attribs,
!!!children,
if (full_screen) full_screen_toggle()
)

tag <- bindFillRole(tag, container = TRUE, item = TRUE)
tag <- bindFillRole(tag, container = TRUE, item = fill)
tag <- tagAppendAttributes(tag, class = class)

as_fragment(
Expand Down Expand Up @@ -113,8 +118,14 @@ as_card_items <- function(children, wrapper) {
#' @param ... Unnamed arguments can be any valid child of an [htmltools
#' tag][htmltools::tags]. Named arguments become HTML attributes on returned
#' UI element.
#' @param fill whether to allow the `card_body()` to grow and shrink to fit its
#' `card()`.
#' @param min_height,max_height,max_height_full_screen Any valid [CSS length
#' unit][htmltools::validateCssUnit()].
#' @param fillable Whether or not the card item should be a fillable (i.e.
#' flexbox) container.
#' @param fill Whether to allow this element to grow/shrink to fit its `card()`
#' container.
#' @param gap A [CSS length unit][htmltools::validateCssUnit()] defining the
#' `gap` (i.e., spacing) between elements provided to `...`. This argument is only applicable when `fillable = TRUE`
#' @inheritParams card
#'
#' @return An [htmltools::div()] tag.
Expand All @@ -126,43 +137,38 @@ as_card_items <- function(children, wrapper) {
#' columns inside a card).
#'
#' @describeIn card_body A general container for the "main content" of a [card()].
card_body <- function(..., fill = FALSE, height = NULL, class = NULL) {
card_body_(
fill_item = fill,
height = height,
class = class,
...
)
}

#' @describeIn card_body Similar to `card_body(fill = TRUE)`, but also marks the
#' return element as a "fill container" (via [htmltools::bindFillRole()]) so
#' that its immediate children are allowed to grow and shrink to fit.
#' @param gap A [CSS length unit][htmltools::validateCssUnit()] defining the
#' `gap` (i.e., spacing) between elements provided to `...`.
#' @param max_height,max_height_full_screen,min_height Any valid [CSS length unit][htmltools::validateCssUnit()].
#' @export
card_body_fill <- function(..., gap = NULL, max_height = NULL, max_height_full_screen = max_height, min_height = NULL, class = NULL) {
card_body <- function(..., fillable = TRUE, min_height = NULL, max_height = NULL, max_height_full_screen = max_height, height = NULL, gap = NULL, fill = TRUE, class = NULL) {

register_runtime_package_check("`card_body_fill()`", "shiny", "1.7.3.9001")
register_runtime_package_check("`card_body_fill()`", "htmlwidgets", "1.5.4.9001")
if (fillable) {
register_runtime_package_check("`card_body()`", "shiny", "1.7.4")
register_runtime_package_check("`card_body()`", "htmlwidgets", "1.6.0")
}

card_body_(
fill_item = TRUE,
fill_container = TRUE,
class = class,
style = htmltools::css(
gap = validateCssUnit(gap),
tag <- div(
class = "card-body",
style = css(
min_height = validateCssUnit(min_height),
"--bslib-card-body-max-height" = validateCssUnit(max_height),
"--bslib-card-body-max-height-full-screen" = validateCssUnit(max_height_full_screen),
margin_top = "auto",
margin_bottom = "auto"
margin_bottom = "auto",
# .card-body already adds `flex: 1 1 auto` so make sure to override it
flex = if (fill) "1 1 auto" else "0 0 auto",
gap = validateCssUnit(gap),
height = validateCssUnit(height)
),
...
)

tag <- bindFillRole(tag, item = fill, container = fillable)

# Make sure user has the opportunity to override the classes added by bindFillRole()
tag <- tagAppendAttributes(tag, class = class)

as.card_item(tag)
}


#' @describeIn card_body Similar to `card_header()` but without the border and background color.
#' @param container a function to generate an HTML element.
#' @export
Expand All @@ -172,26 +178,6 @@ card_title <- function(..., container = htmltools::h5) {
)
}

card_body_ <- function(..., fill_item = FALSE, fill_container = FALSE, height = NULL, class = NULL, container = htmltools::div) {

tag <- container(
class = "card-body",
style = css(
height = validateCssUnit(height),
# .card-body already adds `flex: 1 1 auto` so make sure to override it
flex = if (fill_item) "1 1 auto" else "0 0 auto"
),
...
)

tag <- bindFillRole(tag, item = fill_item, container = fill_container)

# Make sure user has the opportunity to override the classes added by bindFillRole()
tag <- tagAppendAttributes(tag, class = class)

as.card_item(tag)
}


#' @describeIn card_body A header (with border and background color) for the `card()`. Typically appears before a `card_body()`.
#' @param container a function that generates an [htmltools tag][htmltools::tags].
Expand Down Expand Up @@ -222,7 +208,7 @@ card_footer <- function(..., class = NULL) {
#' @export
card_image <- function(
file, ..., href = NULL, border_radius = c("top", "bottom", "all", "none"),
mime_type = NULL, class = NULL, height = NULL, width = NULL, container = card_body_fill) {
mime_type = NULL, class = NULL, height = NULL, fill = TRUE, width = NULL, container = card_body) {

src <- NULL
if (length(file) > 0) {
Expand All @@ -248,7 +234,7 @@ card_image <- function(
...
)

image <- bindFillRole(image, item = TRUE)
image <- bindFillRole(image, item = fill)
image <- tagAppendAttributes(image, class = class)

if (!is.null(href)) {
Expand Down
15 changes: 15 additions & 0 deletions R/deprecated.R
Original file line number Diff line number Diff line change
Expand Up @@ -124,3 +124,18 @@ bs_add_declarations <- function(theme, declarations) {
.Deprecated("bs_add_mixins")
bs_bundle(theme, sass_layer(declarations = declarations))
}


#' @rdname deprecated
#' @export
card_body_fill <- function(...) {
.Deprecated("card_body", old = "card_body_fill")
card_body(...)
}

#' @rdname deprecated
#' @export
page_fill <- function(...) {
.Deprecated("page_fillable", old = "page_fill")
page_fillable(...)
}
15 changes: 8 additions & 7 deletions R/layout.R
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@
#' @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 fill Whether or not to allow the layout to grow/shrink to fit a
#' fillable container with an opinionated height (e.g., `page_fillable()`).
#' @param fillable Whether or not each element is wrapped in a fillable container.
#' @param height_mobile Any valid CSS unit to use for the height when on mobile
#' devices (or narrow windows).
#' @inheritParams card
Expand All @@ -39,8 +41,8 @@
#' 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) {
width, ..., fixed_width = FALSE, heights_equal = c("all", "row"), fill = TRUE,
fillable = TRUE, height = NULL, height_mobile = NULL, gap = NULL, class = NULL) {

heights_equal <- match.arg(heights_equal)

Expand Down Expand Up @@ -73,12 +75,12 @@ layout_column_wrap <- function(
# 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
# 2. Allow for heights_equal="-cell", 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))
div(bindFillRole(div(x), container = fillable, item = TRUE))
)
})

Expand All @@ -97,9 +99,8 @@ layout_column_wrap <- function(
children
)

tag <- bindFillRole(tag, item = TRUE)
tag <- bindFillRole(tag, item = fill)
tag <- tagAppendAttributes(tag, class = class)
tag <- as.card_item(tag)

as_fragment(
tag_require(tag, version = 5, caller = "layout_column_wrap()")
Expand Down
2 changes: 1 addition & 1 deletion R/navs.R
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ collect_nav_items <- function(..., wrapper) {

# Always give tab contents the potential to fill since that's akin to the
# normal card() API (i.e. the card() is a fill container) and users have
# option to make the contents fill via card_body(fill = TRUE) and/or card_body_fill()
# option to make the contents fill via card_body(fill = TRUE, fillable = TRUE)
navs_card_body <- function(content, sidebar) {
content <- makeTabsFillable(content, fillable = TRUE)
if (!is.null(sidebar)) {
Expand Down
7 changes: 5 additions & 2 deletions R/page.R
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,11 @@ page_fixed <- function(..., title = NULL, theme = bs_theme(), lang = NULL) {
#' @inheritParams shiny::fillPage
#' @param fill_mobile Whether or not the page should fill the viewport's
#' height on mobile devices (i.e., narrow windows).
#' @param gap A [CSS length unit][htmltools::validateCssUnit()] defining the
#' `gap` (i.e., spacing) between elements provided to `...`.
#' @seealso [shiny::fillPage()]
#' @export
page_fill <- function(..., padding = 0, fill_mobile = FALSE, title = NULL, theme = bs_theme(), lang = NULL) {
page_fillable <- function(..., padding = NULL, gap = NULL, fill_mobile = FALSE, title = NULL, theme = bs_theme(), lang = NULL) {
page(
title = title,
theme = theme,
Expand All @@ -54,6 +56,7 @@ page_fill <- function(..., padding = 0, fill_mobile = FALSE, title = NULL, theme
class = "bslib-page-fill",
style = css(
padding = validateCssPadding(padding),
gap = validateCssUnit(gap),
"--bslib-page-fill-mobile-height" = if (fill_mobile) "100%" else "auto"
),
...
Expand Down Expand Up @@ -109,7 +112,7 @@ page_navbar <- function(..., title = NULL, id = NULL, selected = NULL,
page_func <- if (isFALSE(fillable) && is.null(sidebar)) {
page
} else {
function(...) page_fill(..., fill_mobile = fill_mobile)
function(...) page_fillable(..., fill_mobile = fill_mobile, padding = 0, gap = 0)
}

page_func(
Expand Down
4 changes: 2 additions & 2 deletions R/sidebar.R
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,8 @@ sidebar <- function(
#' @param sidebar A [sidebar()] object.
#' @param fillable Whether or not the `main` content area should be considered a
#' fillable (i.e., flexbox) container.
#' @param fill Whether or not the layout container should grow/shrink to fit
#' a fillable container.
#' @param fill Whether or not to allow the layout container to grow/shrink to fit a
#' fillable container with an opinionated height (e.g., `page_fillable()`).
#' @param border Whether or not to add a border.
#' @param border_radius Whether or not to add a border radius.
#' @inheritParams card
Expand Down
Binary file modified R/sysdata.rda
Binary file not shown.
Loading