Skip to content

Commit

Permalink
Merged origin/main into fix/sidebar-blur-safari-2
Browse files Browse the repository at this point in the history
  • Loading branch information
gadenbuie committed Jul 15, 2024
2 parents a90d53a + b1e0656 commit 4c4f7c1
Show file tree
Hide file tree
Showing 13 changed files with 351 additions and 54 deletions.
2 changes: 2 additions & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ export(card_footer)
export(card_header)
export(card_image)
export(card_title)
export(css)
export(font_collection)
export(font_face)
export(font_google)
Expand Down Expand Up @@ -156,6 +157,7 @@ import(htmltools)
import(sass)
importFrom(grDevices,col2rgb)
importFrom(htmltools,as.tags)
importFrom(htmltools,css)
importFrom(jquerylib,jquery_core)
importFrom(jsonlite,fromJSON)
importFrom(lifecycle,deprecated)
Expand Down
12 changes: 12 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,16 @@

* Example apps provided with bslib have now moved from `examples` to `examples-shiny` to take advantage of the new `package` argument in `shiny::runExample()` with shiny >= 1.8.1. For example, try `shiny::runExample("build-a-box", package = "bslib")`. (#1049)

* bslib now re-exports `htmltools::css()` to make it easier to specify style declarations. (#1086)

* `card_image()` was improved in a few ways (#1076):
* `alt` is now included in the function inputs and is set to `""` by default. This default value marks images as decorative; please describe the image in the `alt` attribute if it is not decorative.
* `border_radius` now defaults to `"auto"` by default, in which case the image's position in the card will automatically determine whether it should receive the `.card-img-top` (first child), `.card-img-bottom` (last child) or `.card-img` (only child).
* `file` is designed to accept a path to a local (server-side) file, but now recognizes remote files that start with a protocol prefix, e.g. `https://`, or two slashes, e.g. `//`. Local files are base64-encoded and embedded in the HTML output, while remote files are linked directly. To use a relative path for a file that will be served by the Shiny app, use `src` instead of file, e.g. `card_image(src = "cat.jpg")` where `cat.jpg` is stored in `www/`.
* `container` is now `NULL` by default to avoid wrapping the card image in an additional card body container and `fill` is now `FALSE` by default to avoid stretching the image. These changes makes it easier to construct [cards with image caps](https://getbootstrap.com/docs/5.3/components/card/#images).

* The `open` argument of `layout_sidebar()` now includes the option to place a sidebar that's always open on mobile screens _above the main content_ with `open = list(mobile = "always-above")`. (#1088)

## Bug fixes

* `toggle_sidebar()` once again correctly closes a sidebar. (@fredericva, #1043)
Expand All @@ -22,6 +32,8 @@

* When `card_body(fillable = FALSE)`, bslib now preserves flow-layout margin bottom settings. (#1073)

* Fixed a bug in `layout_sidebar()` that caused a spurious and confusing error message. (#1081)

# bslib 0.7.0

This large release includes many improvements and bug fixes for newer UI components like `layout_columns()`, `card()`, and `sidebar()`. In addition, the new `input_task_button()` offers a drop-in replacement for `shiny::actionButton()` (to prevent multiple submissions of the same operation) as well as pairing nicely with the new `shiny::ExtendedTask` for implementing truly non-blocking operations in Shiny.
Expand Down
4 changes: 4 additions & 0 deletions R/bslib-package.R
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ utils::globalVariables("!!")
## usethis namespace: end
NULL

#' @export
#' @importFrom htmltools css
htmltools::css


# For usethis::use_release_issue()
release_bullets <- function() {
Expand Down
129 changes: 111 additions & 18 deletions R/card.R
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ card <- function(

attribs <- args[nzchar(argnames)]
children <- as_card_items(args[!nzchar(argnames)], wrapper = wrapper)
children <- card_image_add_classes(children)

is_shiny_input <- !is.null(id)

Expand Down Expand Up @@ -248,32 +249,94 @@ card_footer <- function(..., class = NULL) {
)
}

#' @describeIn card_body Include static (i.e., pre-generated) images.
#' @param file a file path pointing an image. The image will be base64 encoded
#' and provided to the `src` attribute of the `<img>`. Alternatively, you may
#' set this value to `NULL` and provide the `src` yourself.
#' @param href an optional URL to link to.
#' @param border_radius where to apply `border-radius` on the image.
#' @param mime_type the mime type of the `file`.
#' @param container a function to generate an HTML element to contain the image.
#' @param width Any valid [CSS unit][htmltools::validateCssUnit] (e.g., `width="100%"`).
#' @describeIn card_body Include static images in a card, for example as an
#' image cap at the top or bottom of the card.
#'
#' @param file A file path pointing an image. Local images (i.e. not a URI
#' starting with `https://` or similar) will be base64 encoded and provided to
#' the `src` attribute of the `<img>`. Alternatively, you may directly set
#' the image `src`, in which case `file` is ignored.
#' @param alt Alternate text for the image, used by screen readers and assistive
#' devices. Provide alt text with a description of the image for any images
#' with important content. If alt text is not provided, the image will be
#' considered to be decorative and will not be read or announced by screen
#' readers.
#'
#' For more information, the Web Accessibility Initiative (WAI) has a
#' [helpful tutorial on alt text](https://www.w3.org/WAI/tutorials/images/).
#' @param src The `src` attribute of the `<img>` tag. If provided, `file` is
#' ignored entirely. Use `src` to provide a relative path to a file that will
#' be served by the Shiny application and should not be base64 encoded.
#' @param href An optional URL to link to when a user clicks on the image.
#' @param border_radius Which side of the image should have rounded corners,
#' useful when `card_image()` is used as an image cap at the top or bottom of
#' the card.
#'
#' The value of `border_radius` determines whether the `card-img-top`
#' (`"top"`), `card-img-bottom` (`"bottom"`), or `card-img` (`"all"`)
#' [Bootstrap
#' classes](https://getbootstrap.com/docs/5.3/components/card/#images) are
#' applied to the card. The default `"auto"` value will use the image's
#' position within a `card()` to automatically choose the appropriate class.
#' @param mime_type The mime type of the `file` when it is base64 encoded. This
#' argument is available for advanced use cases where [mime::guess_type()] is
#' unable to automatically determine the file type.
#' @param container A function to generate an HTML element to contain the image.
#' Setting this value to `card_body()` places the image inside the card body
#' area, otherwise the image will extend to the edges of the card.
#' @param width Any valid [CSS unit][htmltools::validateCssUnit] (e.g.,
#' `width="100%"`).
#'
#' @export
card_image <- function(
file, ..., href = NULL, border_radius = c("top", "bottom", "all", "none"),
mime_type = NULL, class = NULL, height = NULL, fill = TRUE, width = NULL, container = card_body) {

src <- NULL
if (length(file) > 0) {
src <- base64enc::dataURI(
file = file, mime = mime_type %||% mime::guess_type(file)
)
file,
...,
alt = "",
src = NULL,
href = NULL,
border_radius = c("auto", "top", "bottom", "all", "none"),
mime_type = NULL,
class = NULL,
height = NULL,
fill = FALSE,
width = NULL,
container = NULL
) {
if (any(!nzchar(rlang::names2(list(...))))) {
rlang::abort(c(
"Unnamed arguments were included in `...`.",
i = "All additional arguments to `card_image()` in `...` should be named attributes for the `<img>` tag."
))
}

border_radius <- rlang::arg_match(border_radius)

if (is.null(src)) {
if (grepl("^([[:alnum:]]+:)?//|data:", file)) {
src <- file
} else {
if (!file.exists(file)) {
rlang::abort(c(
sprintf("`file` does not exist: %s", file),
i = sprintf(
"If `file` is a remote file or will be served by the Shiny app, use a URL or set `src = \"%s\"`.",
file
)
))
}
src <- base64enc::dataURI(
file = file,
mime = mime_type %||% mime::guess_type(file)
)
}
}

image <- tags$img(
src = src,
alt = alt,
class = "img-fluid",
class = switch(
match.arg(border_radius),
border_radius,
all = "card-img",
top = "card-img-top",
bottom = "card-img-bottom",
Expand All @@ -295,11 +358,41 @@ card_image <- function(

if (is.function(container)) {
image <- container(image)
} else {
image <- as.card_item(image)
}

class(image) <- c(
if (border_radius == "auto") "card_image_auto",
"card_image",
class(image)
)

image
}

card_image_add_classes <- function(children) {
for (idx_child in seq_along(children)) {
if (inherits(children[[idx_child]], "card_image_auto")) {
card_img_class <-
if (length(children) == 1) {
"card-img"
} else if (idx_child == 1) {
"card-img-top"
} else if (idx_child == length(children)) {
"card-img-bottom"
}

children[[idx_child]] <- tagAppendAttributes(
children[[idx_child]],
class = card_img_class
)
}
}

children
}

#' @describeIn card_body Mark an object as a card item. This will prevent the
#' [card()] from putting the object inside a `wrapper` (i.e., a
#' `card_body()`).
Expand Down
3 changes: 3 additions & 0 deletions R/page.R
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@
#'
#' @seealso Dashboard-style pages: [page_sidebar()], [page_navbar()],
#' [page_fillable()].
#'
#' @describeIn page A \pkg{bslib} wrapper for [shiny::bootstrapPage()], a basic
#' Boostrap page where the content is added directly to the page body.
#'
#' @export
page <- function(..., title = NULL, theme = bs_theme(), lang = NULL) {
Expand Down
35 changes: 23 additions & 12 deletions R/sidebar.R
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,9 @@
#' the initial sidebar state independently for `desktop` and `mobile` screen
#' sizes. In this case, `desktop` or `mobile` can use any of the above options
#' except `"desktop"`, which is equivalent to
#' `list(desktop = "open", mobile = "closed")`.
#' `list(desktop = "open", mobile = "closed")`. You can also choose to place
#' an always open sidebar above the main content on mobile devices by setting
#' `mobile = "always-above"`.
#'
#' In `sidebar_toggle()`, `open` indicates the desired state of the sidebar,
#' where the default of `open = NULL` will cause the sidebar to be toggled
Expand Down Expand Up @@ -99,12 +101,11 @@ sidebar <- function(
gap = NULL,
padding = NULL
) {

position <- rlang::arg_match(position)
gap <- validateCssUnit(gap)
padding <- validateCssPadding(padding)
width <- validateCssUnit(width)
max_height_mobile <- validateCssUnit(max_height_mobile)
width <- validateCssUnit(width)
max_height_mobile <- validateCssUnit(max_height_mobile)

if (!is.null(open)) {
open <- as_sidebar_open_on(open)
Expand Down Expand Up @@ -167,6 +168,10 @@ as.tags.bslib_sidebar <- function(x, ...) {
open <- sidebar_open_on()
}

if (x$open$mobile == "always-above") {
x$open$mobile <- "always"
}

is_always_open <- all(vapply(x$open, identical, logical(1), "always"))

if (!is_always_open && is.null(x$id)) {
Expand Down Expand Up @@ -257,14 +262,17 @@ as_sidebar_open_on <- function(open) {

#' @param desktop,mobile The initial state of the sidebar on desktop or mobile
#' screen sizes. Can be one of `"open"` (or `TRUE`), `"closed"` (or `FALSE`),
#' or `"always"` (or `NA`).
#' or `"always"` (or `NA`). `mobile` also accepts `"always-above"`.
#' @noRd
sidebar_open_on <- function(
desktop = c("open", "closed", "always"),
mobile = c("closed", "open", "always")
desktop = "open",
mobile = "closed"
) {
desktop <- sidebar_open_as_string(desktop %||% "open")
mobile <- sidebar_open_as_string(mobile %||% "closed")
desktop <- desktop %||% "open"
mobile <- mobile %||% "closed"

desktop <- sidebar_open_as_string(desktop)
mobile <- sidebar_open_as_string(mobile, extra = "always-above")

structure(
list(desktop = desktop, mobile = mobile),
Expand Down Expand Up @@ -317,7 +325,6 @@ layout_sidebar <- function(
gap = NULL,
height = NULL
) {

if (!inherits(sidebar, "sidebar")) {
sidebar <- sidebar(sidebar)
}
Expand Down Expand Up @@ -352,7 +359,11 @@ layout_sidebar <- function(
)

main <- bindFillRole(main, container = fillable)
contents <- list(main, as.tags(sidebar))
contents <- if (sidebar$open$mobile == "always-above") {
list(as.tags(sidebar), main)
} else {
list(main, as.tags(sidebar))
}

right <- identical(sidebar$position, "right")

Expand All @@ -363,7 +374,7 @@ layout_sidebar <- function(
`data-bslib-sidebar-init` = TRUE,
`data-open-desktop` = sidebar$open$desktop,
`data-open-mobile` = sidebar$open$mobile,
`data-collapsible-mobile` = tolower(!identical(sidebar$open$mobile, "always")),
`data-collapsible-mobile` = tolower(!sidebar$open$mobile %in% c("always", "always-above")),
`data-collapsible-desktop` = tolower(!identical(sidebar$open$desktop, "always")),
`data-bslib-sidebar-border` = if (!is.null(border)) tolower(border),
`data-bslib-sidebar-border-radius` = if (!is.null(border_radius)) tolower(border_radius),
Expand Down
12 changes: 1 addition & 11 deletions _pkgdown.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ navbar:
type: light
structure:
left: [get-started, examples, theming, components, layouts]
right: [reference, news, github, lightswitch]
right: [search, reference, news, github, lightswitch]
components:
home: ~

Expand Down Expand Up @@ -75,16 +75,6 @@ navbar:
- text: Filling layout
href: articles/filling/index.html


reference:
text: Reference
href: reference/index.html

github:
icon: bi-github
aria-label: GitHub
href: https://github.com/rstudio/bslib

news:
releases:
- text: "v0.6.1"
Expand Down
Loading

0 comments on commit 4c4f7c1

Please sign in to comment.