Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add tooltip() API #662

Merged
merged 33 commits into from
Jul 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
2c856b7
First pass on tooltip()
cpsievert Jul 6, 2023
afc7fba
`yarn build` (GitHub Actions)
cpsievert Jul 6, 2023
dc34842
chore(srcts): Separate _utils and web component helpers
gadenbuie Jul 10, 2023
08c3d16
chore: Update yarn.lock
gadenbuie Jul 10, 2023
d24448a
Merge branch 'main' into tooltips
cpsievert Jul 11, 2023
444d821
`devtools::document()` (GitHub Actions)
cpsievert Jul 11, 2023
311c33b
`yarn build` (GitHub Actions)
cpsievert Jul 11, 2023
1bc957f
Fix bug introduced by merge conflict error
cpsievert Jul 11, 2023
52f6b9b
`devtools::document()` (GitHub Actions)
cpsievert Jul 11, 2023
6f83755
Code review; fix tooltips inside a card
cpsievert Jul 11, 2023
a6a901d
`devtools::document()` (GitHub Actions)
cpsievert Jul 11, 2023
a8d442d
`yarn build` (GitHub Actions)
cpsievert Jul 11, 2023
e6ea4fe
Resave distributed files (GitHub Action)
cpsievert Jul 11, 2023
9db9d14
Make sure value_box()'s dependency doesn't create an empty card-body …
cpsievert Jul 11, 2023
149e9a6
Add input_switch() (#483)
cpsievert Jul 12, 2023
44b0ed3
fix(bs_theme_preview): Don't include dashboard tab for BS < 5 (#670)
gadenbuie Jul 12, 2023
5111433
feat(bs_theme_preview): Add more DT features and more tables (#671)
gadenbuie Jul 12, 2023
13e69e4
feat(bs_theme_preview): Add more DT features and more tables (#671)
gadenbuie Jul 12, 2023
17cddeb
feat(card): Use an attribute to signal card is in full screen (#669)
gadenbuie Jul 12, 2023
9683c4f
Constrain width of showcase icons in value boxes (#653)
gadenbuie Jul 12, 2023
b254430
Better tooltip_toggle() and tooltip_update() behavior; small improvem…
cpsievert Jul 12, 2023
2d587ec
Merge branch 'main' into tooltips
cpsievert Jul 12, 2023
2786ad0
`devtools::document()` (GitHub Actions)
cpsievert Jul 12, 2023
c447bef
`yarn build` (GitHub Actions)
cpsievert Jul 12, 2023
dd9fd21
Resave distributed files (GitHub Action)
cpsievert Jul 12, 2023
5d0d951
Embrace update/toggle as a prefix; doc improvements
cpsievert Jul 13, 2023
fe14af6
Update/use utility for collection/separating ... args
cpsievert Jul 13, 2023
f9fe0e2
Don't show if trigger isn't visible; hide when trigger loses visibili…
cpsievert Jul 13, 2023
22664b8
`devtools::document()` (GitHub Actions)
cpsievert Jul 13, 2023
941f45f
`yarn build` (GitHub Actions)
cpsievert Jul 13, 2023
7cd8ba7
Resave data (GitHub Action)
cpsievert Jul 13, 2023
634e30b
simplify
cpsievert Jul 14, 2023
dac46d8
`yarn build` (GitHub Actions)
cpsievert Jul 14, 2023
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 DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ Collate:
'shiny-devmode.R'
'sidebar.R'
'staticimports.R'
'tooltip.R'
'utils-deps.R'
'utils-shiny.R'
'utils-tags.R'
Expand Down
3 changes: 3 additions & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,10 @@ export(sidebar)
export(sidebar_toggle)
export(theme_bootswatch)
export(theme_version)
export(toggle_tooltip)
export(tooltip)
export(update_switch)
export(update_tooltip)
export(value_box)
export(version_default)
export(versions)
Expand Down
14 changes: 7 additions & 7 deletions R/card.R
Original file line number Diff line number Diff line change
Expand Up @@ -271,13 +271,13 @@ is.card_item <- function(x) {


full_screen_toggle <- function() {
tags$span(
class = "bslib-full-screen-enter",
class = "badge rounded-pill bg-dark",
"data-bs-toggle" = "tooltip",
"data-bs-placement" = "bottom",
title = "Expand",
full_screen_toggle_icon()
tooltip(
tags$span(
class = "bslib-full-screen-enter",
cpsievert marked this conversation as resolved.
Show resolved Hide resolved
class = "badge rounded-pill bg-dark",
full_screen_toggle_icon()
),
"Expand"
)
}

Expand Down
12 changes: 6 additions & 6 deletions R/layout.R
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,9 @@ layout_column_wrap <- function(

heights_equal <- match.arg(heights_equal)

args <- list_split_named(rlang::list2(...))
attribs <- args[["named"]]
children <- dropNulls(args[["unnamed"]])
args <- separate_arguments(...)
attribs <- args$attribs
children <- args$children

if (length(width) > 1) {
stop("`width` of length greater than 1 is not currently supported.")
Expand Down Expand Up @@ -189,9 +189,9 @@ layout_columns <- function(
class = NULL,
height = NULL
) {
args <- list_split_named(rlang::list2(...))
attribs <- args[["named"]]
children <- dropNulls(args[["unnamed"]])
args <- separate_arguments(...)
attribs <- args$attribs
children <- args$children
n_kids <- length(children)

# Resolve missing value(s) for col_widths, etc.
Expand Down
Binary file modified R/sysdata.rda
Binary file not shown.
125 changes: 125 additions & 0 deletions R/tooltip.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
#' Add a tooltip to a UI element
#'
#' Display additional information when focusing (or hovering over) a UI element.
#'
#' @param trigger A UI element (i.e., [htmltools tag][htmltools::tags]) to serve
#' as the tooltips trigger. It's good practice for this element to be a
#' keyboard-focusable and interactive element (e.g., `actionButton()`,
#' `actionLink()`, etc) so that the tooltip is accessible to keyboard and
#' assistive technology users.
#' @param ... UI elements for the tooltip. Character strings are [automatically
#' escaped][htmlEscape()] unless marked as [HTML()].
#' @param id A character string. Required to re-actively respond to the
#' visibility of the tooltip (via the `input[[id]]` value) and/or update the
#' visibility/contents of the tooltip.
#' @param placement The placement of the tooltip relative to its trigger.
#' @param options A list of additional [Bootstrap
#' options](https://getbootstrap.com/docs/5.3/components/tooltips/#options).
#'
#' @details If `trigger` yields multiple HTML elements (e.g., a `tagList()` or
#' complex `{htmlwidgets}` object), the last HTML element is used as the
#' trigger. If the `trigger` should contain all of those elements, wrap the
#' object in a [div()] or [span()].
#'
#' @describeIn tooltip Add a tooltip to a UI element
#' @references <https://getbootstrap.com/docs/5.3/components/tooltips/>
#' @export
#' @examplesIf interactive()
#'
#' tooltip(
#' shiny::actionButton("btn", "A button"),
#' "A message"
#' )
#'
#' card(
#' card_header(
#' tooltip(
#' span("Card title ", bsicons::bs_icon("question-circle-fill")),
#' "Additional info",
#' placement = "right"
#' )
#' ),
#' "Card body content..."
#' )
tooltip <- function(
trigger,
...,
id = NULL,
placement = c("auto", "top", "right", "bottom", "left"),
options = list()
) {

args <- separate_arguments(...)
children <- args$children
attribs <- args$attribs

if (length(children) == 0) {
abort("At least one value must be provided to `...`.")
}

res <- web_component(
"bslib-tooltip",
id = id,
placement = rlang::arg_match(placement),
options = jsonlite::toJSON(options, auto_unbox = TRUE),
!!!attribs,
# Use display:none instead of <template> since shiny.js
# doesn't bind to the contents of the latter
div(!!!children, style = "display:none;"),
trigger
)

res <- tag_require(res, version = 5, caller = "tooltip()")
as_fragment(res)
}

#' @describeIn tooltip Programmatically show/hide a tooltip.
#'
#' @param id a character string that matches an existing tooltip id.
#' @param show Whether to show (`TRUE`) or hide (`FALSE`) the tooltip. The
#' default (`NULL`) will show if currently hidden and hide if currently shown.
#' Note that a tooltip will not be shown if the trigger is not visible (e.g.,
#' it's hidden behind a tab).
#' @param session A Shiny session object (the default should almost always be
#' used).
#'
#' @export
toggle_tooltip <- function(id, show = NULL, session = get_current_session()) {
show <- normalize_show_value(show)

msg <- list(method = "toggle", value = show)
force(id)
callback <- function() {
session$sendInputMessage(id, msg)
}
session$onFlush(callback, once = TRUE)
}


#' @describeIn tooltip Update the contents of a tooltip.
#' @export
update_tooltip <- function(id, ..., session = get_current_session()) {

title <- tagList(...)

msg <- dropNulls(list(
method = "update",
title = if (length(title) > 0) processDeps(title, session)
))

force(id)
callback <- function() {
session$sendInputMessage(id, msg)
}
session$onFlush(callback, once = TRUE)
}

normalize_show_value <- function(show) {
if (is.null(show)) return("toggle")

if (length(show) != 1 || !is.logical(show)) {
abort("`show` must be `TRUE`, `FALSE`, or `NULL`.")
}

if (show) "show" else "hide"
}
13 changes: 11 additions & 2 deletions R/utils-deps.R
Original file line number Diff line number Diff line change
@@ -1,12 +1,21 @@
component_dependency_js <- function(name) {
web_component <- function(tagName, ...) {
js_dep <- component_dependency_js("webComponents", type = "module")
args <- c(list(js_dep), rlang::list2(...))
tag(tagName, args)
}

component_dependency_js <- function(name, ...) {
minified <- get_shiny_devmode_option("shiny.minified", default = TRUE)

htmlDependency(
name = paste0("bslib-", name, "-js"),
version = get_package_version("bslib"),
package = "bslib",
src = file.path("components", "dist", name),
script = paste0(name, if (minified) ".min", ".js"),
script = list(
src = paste0(name, if (minified) ".min", ".js"),
...
),
all_files = TRUE
)
}
Expand Down
9 changes: 5 additions & 4 deletions R/utils.R
Original file line number Diff line number Diff line change
Expand Up @@ -88,19 +88,20 @@ any_unnamed <- function(x) {
is.null(nms) || !all(nzchar(nms))
}

list_split_named <- function(x) {
separate_arguments <- function(...) {
x <- rlang::list2(...)
x_names <- rlang::names2(x)
is_named <- nzchar(x_names)

if (all(is_named)) {
return(list(named = x, unnamed = list()))
return(list(attribs = x, children = list()))
}

if (!any(is_named)) {
return(list(named = list(), unnamed = x))
return(list(attribs = list(), children = x))
}

list(named = x[is_named], unnamed = unname(x[!is_named]))
list(attribs = x[is_named], children = unname(dropNulls(x[!is_named])))
}

#' Rename a named list
Expand Down
2 changes: 1 addition & 1 deletion inst/components/dist/card/card.css

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 1 addition & 16 deletions inst/components/dist/card/card.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions inst/components/dist/card/card.js.map

Large diffs are not rendered by default.

Loading