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

Remove asFillContainer()/asFillItem() in favor of bindFillRole() #345

Merged
merged 2 commits into from
Oct 25, 2022
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
3 changes: 1 addition & 2 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,8 @@ export("htmlDependencies<-")
export(HTML)
export(a)
export(as.tags)
export(asFillContainer)
export(asFillItem)
export(attachDependencies)
export(bindFillRole)
export(br)
export(browsable)
export(capturePlot)
Expand Down
122 changes: 67 additions & 55 deletions R/fill.R
Original file line number Diff line number Diff line change
@@ -1,24 +1,23 @@
#' Allow tags to intelligently fill their container
#'
#' Create fill containers and items. If a fill item is a direct child of a fill
#' container with a fixed height, then the item is allowed to grow and shrink to
#' its container's size.
#' container, and that container has an opinionated height, then the item is
#' allowed to grow and shrink to its container's size.
#'
#' @details `asFillContainer()` changes the CSS `display` property on the tag to
#' `flex`, which changes the way it does layout of it's direct children. Thus,
#' one should be careful not to mark a tag as a fill container when it needs
#' to rely on other `display` behavior.
#'
#' @param x a [tag()] object.
#' @param x a [tag()] object. Can also be a valid [tagQuery()] input if
#' `.cssSelector` is specified.
#' @param ... currently unused.
#' @param height,width Any valid [CSS unit][htmltools::validateCssUnit] (e.g.,
#' height="200px").
#' @param asItem whether or not to also treat the container as an item. This is
#' useful if the tag wants to both be a direct child of a fill container and a
#' direct parent of a fill item.
#' @param item whether or not to treat `x` as a fill item.
#' @param container whether or not to treat `x` as a fill container. Note this
#' will the CSS `display` property on the tag to `flex`, which changes the way
#' it does layout of it's direct children. Thus, one should be careful not to
#' mark a tag as a fill container when it needs to rely on other `display`
#' behavior.
#' @param overwrite whether or not to override previous calls to
#' `bindFillRole()` (e.g., to remove the item/container role from a tag).
#' @param .cssSelector A character string containing a CSS selector for
#' targeting particular (inner) tag(s) of interest. For more details on what
#' selector(s) are supported, see [tagAppendAttributes()]
#' selector(s) are supported, see [tagAppendAttributes()].
#'
#' @returns The original tag object (`x`) with additional attributes (and a
#' [htmlDependency()]).
Expand All @@ -44,51 +43,76 @@
#' # Inner doesn't fill outer
#' if (interactive()) browsable(tagz)
#'
#' tagz <- asFillContainer(tagz)
#' tagz <- asFillItem(tagz, .cssSelector = "#inner")
#' tagz <- bindFillRole(tagz, container = TRUE)
#' tagz <- bindFillRole(tagz, item = FALSE, .cssSelector = "#inner")
#'
#' # Inner does fill outer
#' if (interactive()) browsable(tagz)
#'
asFillContainer <- function(x, ..., height = NULL, width = NULL, asItem = FALSE, .cssSelector = NULL) {
if (!inherits(x, "shiny.tag")) {
return(throwFillWarning(x))
}
bindFillRole <- function(x, ..., item = FALSE, container = FALSE, overwrite = FALSE, .cssSelector = NULL) {

ellipsis::check_dots_empty()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you using the ... as the equivalent of Python's *?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep


hasSelection <- FALSE
query <- NULL
if (!is.null(.cssSelector)) {

try(silent = TRUE, {
query <- tagQuery(x)$find(.cssSelector)
hasSelection <- length(query$selectedTags()) > 0
})

if (!hasSelection) {
rlang::warn(
paste0(
"`bindFillRole()` didn't find any tags matching the .cssSelector: '", .cssSelector, "'. ",
"Thus, it won't apply any fill roles."
),
class = "htmltools_fill_role_selector"
)
return(x)
}
}

if (!(inherits(x, "shiny.tag") || hasSelection)) {
rlang::warn(
paste0(
"`bindFillRole()` only works on htmltools::tag() objects (e.g., div(), p(), etc.), ",
"not objects of type '", class(x)[1], "'. "
),
class = "htmltools_fill_role_object"
)
return(x)
}

x <- tagAppendAttributes(
x, class = "html-fill-container",
class = if (asItem) "html-fill-item",
style = css(
height = validateCssUnit(height),
width = validateCssUnit(width)
),
.cssSelector = .cssSelector
x, .cssSelector = .cssSelector,
class = if (item) "html-fill-item",
class = if (container) "html-fill-container"
)

attachDependencies(x, fillDependencies(), append = TRUE)
}
if (container) {
x <- attachDependencies(x, fillDependencies(), append = TRUE)
}

#' @export
#' @rdname asFillContainer
asFillItem <- function(x, ..., height = NULL, width = NULL, .cssSelector = NULL) {
if (!inherits(x, "shiny.tag")) {
return(throwFillWarning(x, "item"))
if (!overwrite) {
return(x)
}

ellipsis::check_dots_empty()
query <- query %||% tagQuery(x)

tagAppendAttributes(
x, class = "html-fill-item",
style = css(
height = validateCssUnit(height),
width = validateCssUnit(width)
),
.cssSelector = .cssSelector
)
# removeClass() removes all occurrences of a given class
if (!item) {
query <- query$removeClass("html-fill-item")
}
if (!container) {
query <- query$removeClass("html-fill-container")
}

query$allTags()
}


fillDependencies <- function() {
htmlDependency(
name = "htmltools-fill",
Expand All @@ -98,15 +122,3 @@ fillDependencies <- function() {
stylesheet = "fill.css"
)
}

throwFillWarning <- function(x, type = "container") {
rlang::warn(
paste0(
"Don't know how to treat an object of type '",
class(x)[1], "' as a fill ", type, ". ",
"Only a htmltools::tag() object may be treated as a fill ", type
),
class = "htmltools_fill_input_type"
)
x
}
76 changes: 0 additions & 76 deletions man/asFillContainer.Rd

This file was deleted.

72 changes: 72 additions & 0 deletions man/bindFillRole.Rd

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

5 changes: 2 additions & 3 deletions pkgdown/_pkgdown.yml
Original file line number Diff line number Diff line change
Expand Up @@ -97,10 +97,9 @@ reference:
- '`capturePlot`'
- '`defaultPngDevice`'

- title: Fill containers
- title: Fill items and containers
contents:
- '`asFillContainer`'
- '`asFillItem`'
- '`bindFillRole`'

- title: Utilities
contents:
Expand Down
Loading