From fb851a47c6e2f309a8d0b338559de736052e4118 Mon Sep 17 00:00:00 2001 From: Carson Date: Tue, 20 Apr 2021 18:30:26 -0500 Subject: [PATCH 01/31] wip mainly improve tagQuery() docs --- R/selector.R | 7 - R/tag_query.R | 324 +++++++++++--------------------------- man/tagQuery.Rd | 349 ++++++++++++----------------------------- vignettes/tagQuery.Rmd | 89 +++++++++++ 4 files changed, 285 insertions(+), 484 deletions(-) create mode 100644 vignettes/tagQuery.Rmd diff --git a/R/selector.R b/R/selector.R index 1dc2318f..985ca86e 100644 --- a/R/selector.R +++ b/R/selector.R @@ -43,13 +43,6 @@ asSelector <- function(selector) { stop("Do not know how to handle special pseudo classes like `:first-child` or `:not()` in selector values") } - # if it contains multiple elements, recurse - if (txt_detect(selector, "* ", fixed = TRUE)) { - # we already match on all elements. No need to know about this selector - warning("Removing `* ` from selector. ") - selector <- txt_remove_all(selector, "* ", fixed = TRUE) - } - # Check here to avoid inf recursion if (txt_detect(selector, ">", fixed = TRUE)) { # If there is a `>`, pad it with spaces diff --git a/R/tag_query.R b/R/tag_query.R index 4bce0e01..49b30e86 100644 --- a/R/tag_query.R +++ b/R/tag_query.R @@ -330,10 +330,10 @@ isTagQuery <- function(x) { } assertNotTagEnvLike <- function(x, fnName) { if (isTagEnv(x)) { - stop("Tag environment objects (which inherit `htmltools.tag.env`) are not allowed to be processed in `", fnName, "()`") + stop("Tag environment objects (i.e., `tagQuery()`'s tag structure) are not allowed to be used as if they are regular `tag()` objects. Did you forget to call `tagQuery(x)$asTags(selected = TRUE)`?", call. = FALSE) } if (isTagQuery(x)) { - stop("`tagQuery()` structures (which inherit `htmltools.tag.query`) are not allowed to be processed in `", fnName, "()`") + stop("`tagQuery()` objects are not allowed to be used as if they are regular `tag()` objects. Did you forget to call `$asTags()`?", call. = FALSE) } invisible() } @@ -394,109 +394,15 @@ as.character.htmltools.tag.query <- function(x, ...) { #' #' `r lifecycle::badge("experimental")` #' -#' This function is VERY experimental. The result api will most likely change. -#' **Use at your own risk.** +#' `tagQuery()` provides a [`jQuery`](https://jquery.com/) inspired interface for +#' querying and/or modifying [tag()] (and [tagList()]) objects. #' -#' `tagQuery()` is modeled after [`jQuery`](https://jquery.com/)'s [DOM -#' maninipulation methods](https://api.jquery.com/category/manipulation/) and -#' [Tree Traversal](https://api.jquery.com/category/traversing/tree-traversal/) -#' categories with some alterations. One of the main differences is that there -#' is no centralized `window.document` object to use or reference. Instead, only -#' the original tags provided to `tagQuery(tags=)` are fully search-able. This -#' difference requires a call to `$find(cssSelector)` to make any meaningful -#' alterations to the `tagQuery()` object. -#' -#' `tagQuery()` is built to perform complex alterations within a set of tags. -#' For example, it is difficult to find a set of tags and alter the parent tag -#' when working with standard [`tag`] objects. With `tagQuery()`, it is possible -#' to find all `` tags that match the css selector `div .inner span`, then -#' ask for the grandparent tag objects, then add a class to these grandparent -#' tag elements. This could be accomplished using code similar to -#' -#' ```r tagQuery(ex_tags)$find("div .inner -#' span")$parent()$parent()$addClass("custom-class")$asTags(selected = FALSE) -#' ``` -#' -#' This style of alteration is not easily achieved when using typical "pass by -#' value" R objects or standard tag objects. -#' -#' # Tag Query components -#' -#' ## Tag environments -#' -#' "Tag environments" are the building blocks to `tagQuery()`. When creating a -#' `tagQuery()`, each tag object is converted to a tag environment. This -#' conversion allows for element alterations to happen in place (pass by -#' reference). Meaning that if a css class is added to each selected tag -#' environment using `$addClass()` and the result of the method call is ignored, -#' the selected tag environments within the tag query object will still contain -#' the class addition. The added class will exist when the tag query tag -#' environment are converted back to standard tags objects with `$asTags()`. -#' -#' Tag environments also contain an extra field, `.$parent`. The `.$parent` -#' value contains their parent tag environment. The top level tags supplied to -#' `tagQuery()` will also have a shared parent element. (The shared parent -#' element will have a `NULL` `.$parent` value.) This allows for performing -#' sibling alterations at the top level of the tag query object. -#' -#' The set of tag environments in a pointing to each other within a tag query -#' object can be thought of as a linked list while allowing for a "1 to many" -#' parent to child relationship and up to 1 parent per child. -#' -#' ## Tag Query -#' -#' A `tagQuery()` behaves simliar to an R6 object in that internal values are -#' altered in place. (but a `tagQuery()` object is not implemented with `R6`). -#' The `tagQuery()`'s methods will return itself as much as possible, unless the -#' method is directly asking for information, e.g. `$selected()` or `$asTags()`. -#' -#' Internally, two important pieces of information are maintained: the root -#' elements and the selected elements. The root tag environment will always -#' point (after upgrading to a tag environment) to the original tag object -#' provided to `tagQuery(tag=)`. However, the selected elements are a list of -#' tag environments that update for every `$find(cssSelector)` call. The -#' selected elements are initialized to a list containing the `root` tag -#' environment. All `tagQuery()` methods will act on the selected elements -#' unless declared otherwise. -#' -#' Tag query objects can be created from other tag query objects. Note, unless -#' there is an intermediate call to `$asTags()`, the original and new tag query -#' objects will share the same tag environments. The new tag query object will -#' have its selected elements reset. For example: -#' -#' ```r -#' x <- tagQuery(div()) -#' y <- tagQuery(x) -#' z <- tagQuery(x$asTags(selected = FALSE)) -#' -#' # Add an example class -#' y$addClass("example") -#' -#' # Show `x` and `y` both have the new class -#' x$asTags() -#' #>
-#' y$asTags() -#' #>
-#' -#' # `z` is isolated from the changes in `x` and `y` due to the `$asTags()` -#' z$asTags() -#' #>
-#' ``` -#' -#' -#' @section Limitations: -#' -#' `tagQuery()`s can **not** be used directly within typical `tag` locations. -#' An error should be thrown. Instead, please call `$asTags()` to retrieve the -#' tag structures of the selected tag elements or root element respectively. -#' -#' @param tags Any standard tag object or `tagList()`. If a `list()` or +#' @param tags A [tag()], [tagList()], or [list()] of tags. If a `list()` or #' `tagList()` is provided, a `tagList()` will be returned when calling #' `$asTags()`. -#' @return A `tagQuery()` object. The `tag` supplied will be considered the -#' `root` object. At the time of initialization, the `root` is also considered -#' the single selected item. If any selections are made, the selected elements -#' will be updated. +#' @return A list of methods for working with the "root" `tags` as well as +#' "selected" subset of `tags` (by default, `tags` is selected, but methods +#' such as `$find()` may be used to update the selection). #' @export tagQuery <- function(tags) { @@ -596,37 +502,18 @@ tagQuery_ <- function( structure( class = "htmltools.tag.query", list( - #' @details - #' # CSS Selector - #' - #' The `cssSelector` parameter to many methods is a typical CSS - #' selector. Currently `tagQuery()` understands how to handle any - #' combination of the following CSS selector information: + #' ## Query methods #' - #' * `element`:Match against a tag name. Example: `div` - #' * `id`: Match against a tag `id` attribute. Example: `#myID` - #' * `class`: Match against a tag's class attribute. Example: - #' `.my-class`. This may include multiple classes in any order. + #' Find particular subsets of HTML using CSS selectors (or R functions). + #' Methods that accept a `cssSelector` are currently only allowed to + #' reference tag names (e.g, `div`), ids (e.g., `#myID`), and classes + #' (e.g., `.my-class`). All query methods support combinations of tag + #' named, ids, and classes (e.g., `div#myID.my-class`), but only + #' `$find()` currently supports multiple CSS selectors (e.g. `div span` + #' or `div > a.my-class`). #' - #' The `$find(cssSelector)` method allows for a CSS selector with - #' multiple selections. Example: `div span` or `.outer > span.inner`. - #' All other functions only allow for single-element CSS selections, - #' such as `div#myID.my-class`. - #' - #' # Methods - #' - #' All methods return the altered tag query object unless otherwise - #' stated. - #' - #' ## Select tags - #' - #' - #' * `$find(cssSelector)`: Find all tag elements matching the - #' multi-element `cssSelector` starting from each selected element's - #' children. If nothing has been selected, it will start from the root - #' elements. The tag query object's selected elements will be updated - #' with the matching set of tag environments. A new `tagQuery()` object - #' will be created with the selected items set to the found elements. + #' * `$find(cssSelector)`: Get the descendants of each selected tag, + #' filtered by a `cssSelector` (required). find = function(cssSelector) { rebuild_() @@ -634,70 +521,50 @@ tagQuery_ <- function( tagQueryFindAll(selected_, cssSelector) ) }, - #' * `$children(cssSelector = NULL)`: Update the selected elements to - #' contain all direct child elements of the selected elements. If a CSS - #' selector is provided, only the direct children matching the - #' single-element CSS selector will be selected. A new `tagQuery()` - #' object will be created with the selected items set to the children - #' elements. + #' * `$children(cssSelector = NULL)`: Get the direct children of each + #' selected tag, optionally filtered by a `cssSelector`. children = function(cssSelector = NULL) { rebuild_() newTagQuery( tagQueryFindChildren(selected_, cssSelector) ) }, - #' * `$parent(cssSelector = NULL)`: Update the selected elements to - #' contain the unique set of direct parent of the selected elements. If - #' a CSS selector is provided, only the direct parents matching the - #' single-element CSS selector will be selected. A new `tagQuery()` - #' object will be created with the selected items set to the parent - #' elements. + #' * `$parent(cssSelector = NULL)`: Get the parent of each selected tag, + #' optionally filtered by a `cssSelector`. parent = function(cssSelector = NULL) { rebuild_() newTagQuery( tagQueryFindParent(selected_, cssSelector) ) }, - #' * `$parents(cssSelector = NULL)`: Update the selected elements to - #' contain the unique set of all ancestors of the selected elements. If - #' a CSS selector is provided, only the ancestors matching the - #' single-element CSS selector will be selected. A new `tagQuery()` - #' object will be created with the selected items set to the ancestor - #' elements. + #' * `$parents(cssSelector = NULL)`: Get the ancestors of each selected tag, + #' optionally filtered by a `cssSelector`. parents = function(cssSelector = NULL) { rebuild_() newTagQuery( tagQueryFindParents(selected_, cssSelector) ) }, - #' * `$closest(cssSelector = NULL)`: For each selected element, get the - #' closest ancestor element (including itself) that matches the - #' single-element CSS selector. If `cssSelector = NULL`, it is - #' equivalent to calling `$selected()`. A new `tagQuery()` object will be - #' created with the selected items set to the closest matching elements. - closest = function(cssSelector = NULL) { + #' * `$closest(cssSelector)`: For each selected tag, get the + #' closest ancestor tag (including itself) satisfying a `cssSelector`. + closest = function(cssSelector) { rebuild_() newTagQuery( tagQueryFindClosest(selected_, cssSelector) ) }, - #' * `siblings(cssSelector = NULL)`: Get the siblings of each element in - #' the set of matched elements. If a CSS selector is provided, only the - #' siblings matching the single-element CSS selector will be selected. A - #' new `tagQuery()` object will be created with the selected items set - #' to the sibling elements. + #' * `siblings(cssSelector = NULL)`: Get the siblings of each selected + #' tag, optionally filtered by a `cssSelector`. siblings = function(cssSelector = NULL) { rebuild_() newTagQuery( tagQueryFindSiblings(selected_, cssSelector) ) }, - #' * `$filter(fn)`: Update the selected elements to a subset of the - #' selected elements given `fn(el, i)` returns `TRUE`. If `fn` is a CSS - #' selector, then the selected elements will be filtered if they match - #' the single-element CSS selector. A new `tagQuery()` object will be - #' created with the selected items set to the filtered selected - #' elements. + #' * `$filter(fn)`: Filter the selected tags to those for which + #' `fn(tag, i)` returns `TRUE`. In addition to an R function with two + #' arguments (the `tag` and the index `i`), `fn` may also be a valid CSS + #' selector. filter = function(fn) { rebuild_() newTagQuery( @@ -713,144 +580,142 @@ tagQuery_ <- function( tagQueryFindReset(root) ) }, - ## end Find - #' ## Update selected tag info + #' ## Modify attributes #' - #' * `$addClass(class)`: Apps class(es) to each of the the selected - #' elements. + #' * `$addClass(class)`: `[character()]`\cr Adds class(es) to each + #' selected tag. addClass = function(class) { rebuild_() tagQueryClassAdd(selected_, class) - self + invisible(self) }, - #' * `$removeClass(class)`: Removes a set of class values from all of - #' the selected elements. + #' * `$removeClass(class)`: `[character()]`\cr Removes class(es) to each + #' selected tag. removeClass = function(class) { rebuild_() tagQueryClassRemove(selected_, class) - self - }, - #' * `$hasClass(class)`: Determine whether the selected elements have a - #' given class. Returns a vector of logical values. - hasClass = function(class) { - rebuild_() - tagQueryClassHas(selected_, class) + invisible(self) }, - #' * `$toggleClass(class)`: If the a class value is missing, add it. If - #' a class value already exists, remove it. + #' * `$toggleClass(class)`: `[character()]`\cr If a given `class` element + #' is missing, add it; otherwise, remove it. toggleClass = function(class) { rebuild_() tagQueryClassToggle(selected_, class) - self + invisible(self) }, - - #' * `$addAttrs(...)`: Add named attributes to all selected children. - #' Similar to [`tagAppendAttributes()`]. + #' * `$addAttrs(...)`: Add a set of attributes to each selected tag. addAttrs = function(...) { rebuild_() tagQueryAttrsAdd(selected_, ...) # no need to rebuild_(); already flattened in add attr function - self + invisible(self) }, - #' * `$removeAttrs(attrs)`: Removes the provided attributes in each of - #' the selected elements. + #' * `$removeAttrs(attrs)`: `[character()]`\cr Remove a set of + #' attributes from each selected tag. removeAttrs = function(attrs) { rebuild_() tagQueryAttrsRemove(selected_, attrs) - self + invisible(self) }, - #' * `$emptyAttrs()`: Removes all attributes in each of the selected - #' elements. + #' * `$emptyAttrs()`: Remove all attributes from each selected tag. emptyAttrs = function() { rebuild_() tagQueryAttrsEmpty(selected_) - self + invisible(self) }, - #' * `$hasAttr(attr)`: Returns a vector whose values are whether the - #' selected element contains the non-`NULL` attribute. + + + #' ## Logical assertions + #' + #' These methods return a logical vector with one element per selected + #' tag. + #' + #' * `$hasClass(class)`: `[character()]`\cr Do selected tags have + #' particular class(es)? + hasClass = function(class) { + rebuild_() + tagQueryClassHas(selected_, class) + }, + #' * `$hasAttr(attr)`: `[character()]`\cr Do selected tags have + #' particular attributes? hasAttr = function(attr) { rebuild_() tagQueryAttrHas(selected_, attr) }, - #' ## Adjust child elements + + #' ## Modify children #' - #' * `$append(...)`: Add all `...` objects as children **after** any - #' existing children to the selected elements. Similar to - #' [`tagAppendChildren()`] + #' * `$append(...)`: For each selected tag, insert `...` **after** any + #' existing children. append = function(...) { rebuild_() tagQueryChildrenAppend(selected_, ...) rebuild_() - self + invisible(self) }, - #' * `$prepend(...)`: Add all `...` objects as children **before** any - #' existing children to the selected elements. A variation of - #' [`tagAppendChildren()`] + #' * `$prepend(...)`: For each selected tag, insert `...` **before** any + #' existing children. prepend = function(...) { rebuild_() tagQueryChildrenPrepend(selected_, ...) rebuild_() - self + invisible(self) }, - #' * `$empty()`: Remove all children in the selected elements. Use this - #' method before calling `$append(...)` to replace all selected - #' elements' children. + #' * `$empty()`: Remove any children of each selected tag. Use this + #' method before calling `$append(...)` to replace the children of + #' each selected tag, with other content. empty = function() { rebuild_() tagQueryChildrenEmpty(selected_) # no need to rebuild_ - self + invisible(self) }, - ## end Adjust Children - #' ## Adjust Siblings + + #' ## Modify siblings #' #' * `$after(...)`: Add all `...` objects as siblings after each of the - #' selected elements. + #' selected tags. after = function(...) { rebuild_() tagQuerySiblingAfter(selected_, ...) rebuild_() - self + invisible(self) }, #' * `$before(...)`: Add all `...` objects as siblings before each of - #' the selected elements. + #' the selected tags. before = function(...) { rebuild_() tagQuerySiblingBefore(selected_, ...) rebuild_() - self + invisible(self) }, - #' * `$replaceWith(...)`: Replace all selected elements with `...`. This - #' also sets the selected elements to an empty set. A new `tagQuery()` - #' object will be created with an empty set of selected elements. + #' * `$replaceWith(...)`: Replace all selected tags with `...` in the + #' root tag and clear the selection. replaceWith = function(...) { rebuild_() tagQuerySiblingReplaceWith(selected_, ...) newTagQuery(list(), rebuild = TRUE) }, - #' * `$remove(...)`: Remove all selected elements from the `tagQuery()` - #' object. The selected elements is set to an empty set. A new - #' `tagQuery()` object will be created with an empty set of selected - #' elements. + #' * `$remove(...)`: Remove all selected tags from the root tag and + #' clear the current selection. remove = function() { rebuild_() tagQuerySiblingRemove(selected_) # Remove items from selected info newTagQuery(list(), rebuild = TRUE) }, - ## end Adjust Siblings - ## Generic Methods + #' ## Generic methods #' - #' * `$each(fn)`: Perform function `fn` on each of the selected - #' elements. `fn` should accept two arguments: a selected element and + #' * `$each(fn)`: Modify each selected tag with a function `fn`. + #' `fn` should accept two arguments: a selected element and #' the selected element's position within the selected elements. This - #' argument order is different than jQuery's `$().each()` as there is no + #' argument order mimics jQuery's `$().each()` as there is no #' concept of a `this` object inside the function execution. To stay #' consistent with other methods, the each of the selected tag #' environments will be given first, followed by the index position. Any @@ -860,9 +725,8 @@ tagQuery_ <- function( rebuild_() tagQueryEach(selected_, fn) rebuild_() - self + invisible(self) }, - ## end Generic Methods #' ## Tag Query methods #' @@ -873,7 +737,7 @@ tagQuery_ <- function( #' standard tags. If there is more than one element being #' returned, a `tagList()` will be used to hold all of the #' objects. - asTags = function(selected = TRUE) { + asTags = function(selected = FALSE) { rebuild_() if (isTRUE(selected)) { tagQuerySelectedAsTags(selected_) @@ -906,7 +770,7 @@ tagQuery_ <- function( #' introduced into the tag structure. rebuild = function() { rebuild_() - self + invisible(self) }, #' * `$print()`: Internal print method. Called by #' `print.htmltools.tag.query()` diff --git a/man/tagQuery.Rd b/man/tagQuery.Rd index 8053fdcf..003a6c28 100644 --- a/man/tagQuery.Rd +++ b/man/tagQuery.Rd @@ -5,269 +5,124 @@ \title{Perform jQuery-like alterations on tags} \usage{ tagQuery(tags) -} -\arguments{ -\item{tags}{Any standard tag object or \code{tagList()}. If a \code{list()} or -\code{tagList()} is provided, a \code{tagList()} will be returned when calling -\verb{$asTags()}.} -} -\value{ -A \code{tagQuery()} object. The \code{tag} supplied will be considered the -\code{root} object. At the time of initialization, the \code{root} is also considered -the single selected item. If any selections are made, the selected elements -will be updated. -} -\description{ -\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#experimental}{\figure{lifecycle-experimental.svg}{options: alt='[Experimental]'}}}{\strong{[Experimental]}} -} -\details{ -This function is VERY experimental. The result api will most likely change. -\strong{Use at your own risk.} - -\code{tagQuery()} is modeled after \href{https://jquery.com/}{\code{jQuery}}'s \href{https://api.jquery.com/category/manipulation/}{DOM maninipulation methods} and -\href{https://api.jquery.com/category/traversing/tree-traversal/}{Tree Traversal} -categories with some alterations. One of the main differences is that there -is no centralized \code{window.document} object to use or reference. Instead, only -the original tags provided to \code{tagQuery(tags=)} are fully search-able. This -difference requires a call to \verb{$find(cssSelector)} to make any meaningful -alterations to the \code{tagQuery()} object. - -\code{tagQuery()} is built to perform complex alterations within a set of tags. -For example, it is difficult to find a set of tags and alter the parent tag -when working with standard \code{\link{tag}} objects. With \code{tagQuery()}, it is possible -to find all \verb{} tags that match the css selector \verb{div .inner span}, then -ask for the grandparent tag objects, then add a class to these grandparent -tag elements. This could be accomplished using code similar to\if{html}{\out{
}}\preformatted{span")$parent()$parent()$addClass("custom-class")$asTags(selected = FALSE) -}\if{html}{\out{
}} - -This style of alteration is not easily achieved when using typical "pass by -value" R objects or standard tag objects. -} -\section{Tag Query components}{ -\subsection{Tag environments}{ - -"Tag environments" are the building blocks to \code{tagQuery()}. When creating a -\code{tagQuery()}, each tag object is converted to a tag environment. This -conversion allows for element alterations to happen in place (pass by -reference). Meaning that if a css class is added to each selected tag -environment using \verb{$addClass()} and the result of the method call is ignored, -the selected tag environments within the tag query object will still contain -the class addition. The added class will exist when the tag query tag -environment are converted back to standard tags objects with \verb{$asTags()}. - -Tag environments also contain an extra field, \code{.$parent}. The \code{.$parent} -value contains their parent tag environment. The top level tags supplied to -\code{tagQuery()} will also have a shared parent element. (The shared parent -element will have a \code{NULL} \code{.$parent} value.) This allows for performing -sibling alterations at the top level of the tag query object. - -The set of tag environments in a pointing to each other within a tag query -object can be thought of as a linked list while allowing for a "1 to many" -parent to child relationship and up to 1 parent per child. -} - -\subsection{Tag Query}{ - -A \code{tagQuery()} behaves simliar to an R6 object in that internal values are -altered in place. (but a \code{tagQuery()} object is not implemented with \code{R6}). -The \code{tagQuery()}'s methods will return itself as much as possible, unless the -method is directly asking for information, e.g. \verb{$selected()} or \verb{$asTags()}. - -Internally, two important pieces of information are maintained: the root -elements and the selected elements. The root tag environment will always -point (after upgrading to a tag environment) to the original tag object -provided to \code{tagQuery(tag=)}. However, the selected elements are a list of -tag environments that update for every \verb{$find(cssSelector)} call. The -selected elements are initialized to a list containing the \code{root} tag -environment. All \code{tagQuery()} methods will act on the selected elements -unless declared otherwise. - -Tag query objects can be created from other tag query objects. Note, unless -there is an intermediate call to \verb{$asTags()}, the original and new tag query -objects will share the same tag environments. The new tag query object will -have its selected elements reset. For example:\if{html}{\out{
}}\preformatted{x <- tagQuery(div()) -y <- tagQuery(x) -z <- tagQuery(x$asTags(selected = FALSE)) - -# Add an example class -y$addClass("example") - -# Show `x` and `y` both have the new class -x$asTags() -#>
-y$asTags() -#>
- -# `z` is isolated from the changes in `x` and `y` due to the `$asTags()` -z$asTags() -#>
-}\if{html}{\out{
}} -} -} - -\section{Limitations}{ - -\code{tagQuery()}s can \strong{not} be used directly within typical \code{tag} locations. -An error should be thrown. Instead, please call \verb{$asTags()} to retrieve the -tag structures of the selected tag elements or root element respectively. -} - -\section{CSS Selector}{ -The \code{cssSelector} parameter to many methods is a typical CSS -selector. Currently \code{tagQuery()} understands how to handle any -combination of the following CSS selector information: -\itemize{ -\item \code{element}:Match against a tag name. Example: \code{div} -\item \code{id}: Match against a tag \code{id} attribute. Example: \verb{#myID} -\item \code{class}: Match against a tag's class attribute. Example: -\code{.my-class}. This may include multiple classes in any order. -} - -The \verb{$find(cssSelector)} method allows for a CSS selector with -multiple selections. Example: \verb{div span} or \code{.outer > span.inner}. -All other functions only allow for single-element CSS selections, -such as \code{div#myID.my-class}. -} - -\section{Methods}{ -All methods return the altered tag query object unless otherwise -stated. -\subsection{Select tags}{ -\itemize{ -\item \verb{$find(cssSelector)}: Find all tag elements matching the -multi-element \code{cssSelector} starting from each selected element's -children. If nothing has been selected, it will start from the root -elements. The tag query object's selected elements will be updated -with the matching set of tag environments. A new \code{tagQuery()} object -will be created with the selected items set to the found elements. -\item \verb{$children(cssSelector = NULL)}: Update the selected elements to -contain all direct child elements of the selected elements. If a CSS -selector is provided, only the direct children matching the -single-element CSS selector will be selected. A new \code{tagQuery()} -object will be created with the selected items set to the children -elements. -\item \verb{$parent(cssSelector = NULL)}: Update the selected elements to -contain the unique set of direct parent of the selected elements. If -a CSS selector is provided, only the direct parents matching the -single-element CSS selector will be selected. A new \code{tagQuery()} -object will be created with the selected items set to the parent -elements. -\item \verb{$parents(cssSelector = NULL)}: Update the selected elements to -contain the unique set of all ancestors of the selected elements. If -a CSS selector is provided, only the ancestors matching the -single-element CSS selector will be selected. A new \code{tagQuery()} -object will be created with the selected items set to the ancestor -elements. -\item \verb{$closest(cssSelector = NULL)}: For each selected element, get the -closest ancestor element (including itself) that matches the -single-element CSS selector. If \code{cssSelector = NULL}, it is -equivalent to calling \verb{$selected()}. A new \code{tagQuery()} object will be -created with the selected items set to the closest matching elements. -\item \code{siblings(cssSelector = NULL)}: Get the siblings of each element in -the set of matched elements. If a CSS selector is provided, only the -siblings matching the single-element CSS selector will be selected. A -new \code{tagQuery()} object will be created with the selected items set -to the sibling elements. -\item \verb{$filter(fn)}: Update the selected elements to a subset of the -selected elements given \code{fn(el, i)} returns \code{TRUE}. If \code{fn} is a CSS -selector, then the selected elements will be filtered if they match -the single-element CSS selector. A new \code{tagQuery()} object will be -created with the selected items set to the filtered selected -elements. -\item \verb{$reset()}: A new \code{tagQuery()} object will be created with the +NULL +## Query methods + +Find particular subsets of HTML using CSS selectors (or R functions). +Methods that accept a `cssSelector` are currently only allowed to +reference tag names (e.g, `div`), ids (e.g., `#myID`), and classes +(e.g., `.my-class`). All query methods support combinations of tag +named, ids, and classes (e.g., `div#myID.my-class`), but only +`$find()` currently supports multiple CSS selectors (e.g. `div span` +or `div > a.my-class`). + +* `$find(cssSelector)`: Get the descendants of each selected tag, +filtered by a `cssSelector` (required). +* `$children(cssSelector = NULL)`: Get the direct children of each +selected tag, optionally filtered by a `cssSelector`. +* `$parent(cssSelector = NULL)`: Get the parent of each selected tag, +optionally filtered by a `cssSelector`. +* `$parents(cssSelector = NULL)`: Get the ancestors of each selected tag, +optionally filtered by a `cssSelector`. +* `$closest(cssSelector)`: For each selected tag, get the +closest ancestor tag (including itself) satisfying a `cssSelector`. +* `siblings(cssSelector = NULL)`: Get the siblings of each selected +tag, optionally filtered by a `cssSelector`. +* `$filter(fn)`: Filter the selected tags to those for which +`fn(tag, i)` returns `TRUE`. In addition to an R function with two +arguments (the `tag` and the index `i`), `fn` may also be a valid CSS +selector. +* `$reset()`: A new `tagQuery()` object will be created with the selected items set to the top level tag objects. -} -} - -\subsection{Update selected tag info}{ -\itemize{ -\item \verb{$addClass(class)}: Apps class(es) to each of the the selected -elements. -\item \verb{$removeClass(class)}: Removes a set of class values from all of -the selected elements. -\item \verb{$hasClass(class)}: Determine whether the selected elements have a -given class. Returns a vector of logical values. -\item \verb{$toggleClass(class)}: If the a class value is missing, add it. If -a class value already exists, remove it. -\item \verb{$addAttrs(...)}: Add named attributes to all selected children. -Similar to \code{\link[=tagAppendAttributes]{tagAppendAttributes()}}. -\item \verb{$removeAttrs(attrs)}: Removes the provided attributes in each of -the selected elements. -\item \verb{$emptyAttrs()}: Removes all attributes in each of the selected -elements. -\item \verb{$hasAttr(attr)}: Returns a vector whose values are whether the -selected element contains the non-\code{NULL} attribute. -} -} - -\subsection{Adjust child elements}{ -\itemize{ -\item \verb{$append(...)}: Add all \code{...} objects as children \strong{after} any -existing children to the selected elements. Similar to -\code{\link[=tagAppendChildren]{tagAppendChildren()}} -\item \verb{$prepend(...)}: Add all \code{...} objects as children \strong{before} any -existing children to the selected elements. A variation of -\code{\link[=tagAppendChildren]{tagAppendChildren()}} -\item \verb{$empty()}: Remove all children in the selected elements. Use this -method before calling \verb{$append(...)} to replace all selected -elements' children. -} -} - -\subsection{Adjust Siblings}{ -\itemize{ -\item \verb{$after(...)}: Add all \code{...} objects as siblings after each of the -selected elements. -\item \verb{$before(...)}: Add all \code{...} objects as siblings before each of -the selected elements. -\item \verb{$replaceWith(...)}: Replace all selected elements with \code{...}. This -also sets the selected elements to an empty set. A new \code{tagQuery()} -object will be created with an empty set of selected elements. -\item \verb{$remove(...)}: Remove all selected elements from the \code{tagQuery()} -object. The selected elements is set to an empty set. A new -\code{tagQuery()} object will be created with an empty set of selected -elements. -} -} - -\subsection{Generic methods}{ -\itemize{ -\item \verb{$each(fn)}: Perform function \code{fn} on each of the selected -elements. \code{fn} should accept two arguments: a selected element and +## Modify attributes + +* `$addClass(class)`: `[character()]`\cr Adds class(es) to each +selected tag. +* `$removeClass(class)`: `[character()]`\cr Removes class(es) to each +selected tag. +* `$toggleClass(class)`: `[character()]`\cr If a given `class` element +is missing, add it; otherwise, remove it. +* `$addAttrs(...)`: Add a set of attributes to each selected tag. +* `$removeAttrs(attrs)`: `[character()]`\cr Remove a set of +attributes from each selected tag. +* `$emptyAttrs()`: Remove all attributes from each selected tag. +## Logical assertions + +These methods return a logical vector with one element per selected +tag. + +* `$hasClass(class)`: `[character()]`\cr Do selected tags have +particular class(es)? +* `$hasAttr(attr)`: `[character()]`\cr Do selected tags have +particular attributes? +## Modify children + +* `$append(...)`: For each selected tag, insert `...` **after** any +existing children. +* `$prepend(...)`: For each selected tag, insert `...` **before** any +existing children. +* `$empty()`: Remove any children of each selected tag. Use this +method before calling `$append(...)` to replace the children of +each selected tag, with other content. +## Modify siblings + +* `$after(...)`: Add all `...` objects as siblings after each of the +selected tags. +* `$before(...)`: Add all `...` objects as siblings before each of +the selected tags. +* `$replaceWith(...)`: Replace all selected tags with `...` in the +root tag and clear the selection. +* `$remove(...)`: Remove all selected tags from the root tag and +clear the current selection. +## Generic methods + +* `$each(fn)`: Modify each selected tag with a function `fn`. +`fn` should accept two arguments: a selected element and the selected element's position within the selected elements. This -argument order is different than jQuery's \verb{$().each()} as there is no -concept of a \code{this} object inside the function execution. To stay +argument order mimics jQuery's `$().each()` as there is no +concept of a `this` object inside the function execution. To stay consistent with other methods, the each of the selected tag environments will be given first, followed by the index position. Any alterations to the provided tag environments will persist in calling tag query object. -} -} +## Tag Query methods -\subsection{Tag Query methods}{ -\itemize{ -\item \verb{$asTags(selected = TRUE)}: If \code{selected = TRUE}, then all +* `$asTags(selected = TRUE)`: If `selected = TRUE`, then all previously found elements (and their descendants) will be -converted to tags. If \code{selected = FALSE}, the top level tag +converted to tags. If `selected = FALSE`, the top level tag elements (and their descendants) will be converted to standard tags. If there is more than one element being -returned, a \code{tagList()} will be used to hold all of the +returned, a `tagList()` will be used to hold all of the objects. -\item \verb{$root()}: Return all top level (root) tags environments. If there -are more than one, it will be returned within a \code{tagList()}. If there +* `$root()`: Return all top level (root) tags environments. If there +are more than one, it will be returned within a `tagList()`. If there is only one tag, it will be returned. -\item \verb{$selected()}: Returns a list of selected tag environments. -\item \verb{$get(position)}: Returns the selected tag element at the position -\code{position}. -\item \verb{$rebuild()}: Makes sure that all tags have been upgraded to tag -environments. Objects wrapped in \code{HTML()} will not be inspected or +* `$selected()`: Returns a list of selected tag environments. +* `$get(position)`: Returns the selected tag element at the position +`position`. +* `$rebuild()`: Makes sure that all tags have been upgraded to tag +environments. Objects wrapped in `HTML()` will not be inspected or altered. This method is internally called before each method executes and after any alterations where standard tag objects could be introduced into the tag structure. -\item \verb{$print()}: Internal print method. Called by -\code{print.htmltools.tag.query()} +* `$print()`: Internal print method. Called by +`print.htmltools.tag.query()` } +\arguments{ +\item{tags}{A \code{\link[=tag]{tag()}}, \code{\link[=tagList]{tagList()}}, or \code{\link[=list]{list()}} of tags. If a \code{list()} or +\code{tagList()} is provided, a \code{tagList()} will be returned when calling +\verb{$asTags()}.} } +\value{ +A list of methods for working with the "root" \code{tags} as well as +"selected" subset of \code{tags} (by default, \code{tags} is selected, but methods +such as \verb{$find()} may be used to update the selection). +} +\description{ +\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#experimental}{\figure{lifecycle-experimental.svg}{options: alt='[Experimental]'}}}{\strong{[Experimental]}} +} +\details{ +\code{tagQuery()} provides a \href{https://jquery.com/}{\code{jQuery}} inspired interface for +querying and/or modifying \code{\link[=tag]{tag()}} (and \code{\link[=tagList]{tagList()}}) objects. } - diff --git a/vignettes/tagQuery.Rmd b/vignettes/tagQuery.Rmd new file mode 100644 index 00000000..519f93e4 --- /dev/null +++ b/vignettes/tagQuery.Rmd @@ -0,0 +1,89 @@ +--- +title: "Using tagQuery() to query and modify HTML tags" +output: html_document +--- + +```{r, include=FALSE} +knitr::opts_chunk$set( + collapse = TRUE, + comment = "#>", + out.width = "100%", + fig.align = 'center' +) +``` + +`tagQuery()` provides a [jQuery](https://jquery.com) inspired interface to query and/or modify HTML fragments in R. To start, pass either a `tag()` (e.g., `div()`) or `tagList()` object to `tagQuery()`: + +```{r} +library(htmltools) +tagQ <- tagQuery( + div(span(), a()) +) +tagQ +``` + +Notice how `tagQuery()` tracks two essential pieces: the "root" (aka input) tag as well as the set of currently selected tags. This data structure allows us to [efficiently](#performance) [find](#query), [modify](#modify), and [extract](#extract) particular fragments of HTML. + +## Modify {#modify} + +### Mutation + +In contrast to other functions in `{htmltools}` designed to modify `tag()`s (e.g., `tagAppendAttributes()`, `tagAppendChild()`, etc), `tagQuery()`'s modifier methods (e.g., `$addClass()`) _modify their input_: + +```{r} +tagQ$addClass("foo") +tagQ +``` + +As we'll see in [Query](#query), the [mutable](https://developer.mozilla.org/en-US/docs/Glossary/Mutable) nature of `tagQuery()`'s modifier methods enables [efficient](#performance) modification of child tags without losing a reference to the root tag. + +### Attributes + +`tagQuery()` has numerous methods for modifying attributes + +```{r} +tagQ$addAttrs("data-bar" = "foo") +tagQ +``` + +### Children + +TODO: useful example(s) + +### Siblings + +TODO: useful example(s) + +## Query {#query} + +In order to modify children tags, we first must select them. To do so, provide `$find()` a valid [CSS selector](https://developer.mozilla.org/en-US/docs/Learn/CSS/Building_blocks/Selectors). Here we'll select all the `` tags and add a class of `"bar"` to them: + +```{r} +tagQ$find("a")$addClass("bar") +tagQ +``` + +Note that the `$addClass()` [modifier method](#modify) has modified + +In contrast to `tagQuery()` [modifier methods](#modify), querying methods (e.g., `$find()`, `$parent()`, `$children()`, etc), _don't_ modify `xq`. That means, if you want a selection to persist, you must + +```{r} +aq <- xq$find("a") +aq +``` + + +## Extract {#extract} + +Since `tagQuery()` objects represent both a root tag and a set of selected tags, you'll need to decide whether you want the root or selection via `$asTags()` when trying to use it inside other functions like `tag()`, `tagList()`, or `renderTags()` + +```{r} +tags$body( + tagQ$asTags() +) +``` + + +## Performance {#performance} + +TODO: barret, fill this in! From 55bebce5c85a6fb7fd04e55d602bb9522d633b68 Mon Sep 17 00:00:00 2001 From: Carson Date: Wed, 21 Apr 2021 13:14:33 -0500 Subject: [PATCH 02/31] wip flesh out article --- vignettes/tagQuery.Rmd | 91 ++++++++++++++++++++++++++++++++---------- 1 file changed, 71 insertions(+), 20 deletions(-) diff --git a/vignettes/tagQuery.Rmd b/vignettes/tagQuery.Rmd index 519f93e4..03dc897b 100644 --- a/vignettes/tagQuery.Rmd +++ b/vignettes/tagQuery.Rmd @@ -16,13 +16,81 @@ knitr::opts_chunk$set( ```{r} library(htmltools) +tagQuery(div(a())) +``` + +Notice how `tagQuery()` tracks two essential pieces: the _root_ (i.e., input) tag as well as a set of _selected_ tags (by default the root is selected). This data structure allows us to [efficiently](#performance) [find](#query), and [modify](#modify) particular fragments of the root HTML tag. + +Since `tagQuery()` isn't itself a `tag()` object, it can't be directly to `tag()` or tag rendering functions, but at any given time you can extract the `$root()` or `$selected()` tags. + +## Query {#query} + +`tagQuery()` has numerous methods to select (i.e., query) HTML tag(s). Every query method accepts a [CSS selector](https://developer.mozilla.org/en-US/docs/Learn/CSS/Building_blocks/Selectors) for targeting particular tags of interest. At the moment, `tagQuery()` only supports a combination of [type](https://www.w3.org/TR/CSS22/selector.html#type-selectors) (e.g, `div`), [class](https://www.w3.org/TR/CSS22/selector.html#class-html) (e.g., `.my-class`), [id](https://www.w3.org/TR/CSS22/selector.html#id-selectors) (e.g., `#myID`), and [universal](https://www.w3.org/TR/CSS22/selector.html#universal-selector) (`*`) selectors within a given [simple selector](https://www.w3.org/TR/CSS22/selector.html#selector-syntax). + +### Children + +To begin querying tags, start with either `$find()` or `$children()`. The former traverses _all_ descendants whereas the latter only considers _direct_ descendants. + +```{r} +tagQ <- tagQuery(div(a(), div(a()))) +tagQ$find("a")$selected() +tagQ$children("a")$selected() +``` + +And since `$find()` considers all descendants, it allows for [descendant selectors](https://www.w3.org/TR/CSS22/selector.html#descendant-selectors) (space) and direct [child selectors](https://www.w3.org/TR/CSS22/selector.html#child-selectors) (>). + +```{r} +tagQ <- tagQuery(div(div(span(a())))) +tagQ$find("div a")$selected() +tagQ$find("div > a")$selected() +``` + +Since `tagQuery()` methods may be chained together, you could also implement `tagQ$find("div > a")` as: + +```{r} +tagQ$find("div")$children("a")$selected() +``` + +### Siblings + +Although `tagQuery()` doesn't support [sibling selectors](https://www.w3schools.com/css/css_combinators.asp) (`+` and `~`), it does provide a `$sibling()` method, which provides essentially the same functionality: + +```{r} +tagQ <- tagQuery(div(a(), span(), p())) +# morally equivalent to `tagQ$find("a ~ span")` +tagQ$find("a")$siblings("span")$selected() +``` + +### Parents + +In some cases, after querying descendants, it can be useful to traverse back up the tag tree to find particular ancestors of a selection. Similar to the difference in `$find()` and `$children()`, `$parents()` traverses _all_ ancestors whereas `$parent()` considers just _direct_ ancestors. + +```{r} tagQ <- tagQuery( - div(span(), a()) + div( + div(a(class = "foo")), + span(a()) + ) ) -tagQ +tagQ$find("a.foo")$parent()$selected() +tagQ$find("a.foo")$parents()$selected() +``` + + + +### Filter + +The `$filter()` method can be used to subset selected tags using an R function or CSS selector. This provides a particularly useful workaround for the fact that `tagQuery()` doesn't currently support [attribute selectors](https://www.w3.org/TR/CSS22/selector.html#attribute-selectors). + +```{r} +tagQ <- tagQuery(div(div(), div("data-foo" = "bar"))) +# moral equivalent to `tagQ$find("[data-foo]")` +tagQ$ + find("*")$ + filter(function(x, i) tagHasAttribute(x, "data-foo"))$ + selected() ``` -Notice how `tagQuery()` tracks two essential pieces: the "root" (aka input) tag as well as the set of currently selected tags. This data structure allows us to [efficiently](#performance) [find](#query), [modify](#modify), and [extract](#extract) particular fragments of HTML. ## Modify {#modify} @@ -54,23 +122,6 @@ TODO: useful example(s) TODO: useful example(s) -## Query {#query} - -In order to modify children tags, we first must select them. To do so, provide `$find()` a valid [CSS selector](https://developer.mozilla.org/en-US/docs/Learn/CSS/Building_blocks/Selectors). Here we'll select all the `` tags and add a class of `"bar"` to them: - -```{r} -tagQ$find("a")$addClass("bar") -tagQ -``` - -Note that the `$addClass()` [modifier method](#modify) has modified - -In contrast to `tagQuery()` [modifier methods](#modify), querying methods (e.g., `$find()`, `$parent()`, `$children()`, etc), _don't_ modify `xq`. That means, if you want a selection to persist, you must - -```{r} -aq <- xq$find("a") -aq -``` ## Extract {#extract} From df0a11033420041d894e791bb86680dc9e181f41 Mon Sep 17 00:00:00 2001 From: Carson Date: Wed, 21 Apr 2021 15:17:25 -0500 Subject: [PATCH 03/31] Fix the broken method details --- R/tag_query.R | 3 + man/tagQuery.Rd | 179 +++++++++++++++++++++++++++--------------------- 2 files changed, 103 insertions(+), 79 deletions(-) diff --git a/R/tag_query.R b/R/tag_query.R index 02f71075..3dacb334 100644 --- a/R/tag_query.R +++ b/R/tag_query.R @@ -502,6 +502,8 @@ tagQuery_ <- function( structure( class = "htmltools.tag.query", list( + #' @details + #' #' ## Query methods #' #' Find particular subsets of HTML using CSS selectors (or R functions). @@ -580,6 +582,7 @@ tagQuery_ <- function( tagQueryFindReset(root) ) }, + ## end Query methods #' ## Modify attributes diff --git a/man/tagQuery.Rd b/man/tagQuery.Rd index 003a6c28..3e0bb426 100644 --- a/man/tagQuery.Rd +++ b/man/tagQuery.Rd @@ -5,124 +5,145 @@ \title{Perform jQuery-like alterations on tags} \usage{ tagQuery(tags) +} +\arguments{ +\item{tags}{A \code{\link[=tag]{tag()}}, \code{\link[=tagList]{tagList()}}, or \code{\link[=list]{list()}} of tags. If a \code{list()} or +\code{tagList()} is provided, a \code{tagList()} will be returned when calling +\verb{$asTags()}.} +} +\value{ +A list of methods for working with the "root" \code{tags} as well as +"selected" subset of \code{tags} (by default, \code{tags} is selected, but methods +such as \verb{$find()} may be used to update the selection). +} +\description{ +\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#experimental}{\figure{lifecycle-experimental.svg}{options: alt='[Experimental]'}}}{\strong{[Experimental]}} +} +\details{ +\code{tagQuery()} provides a \href{https://jquery.com/}{\code{jQuery}} inspired interface for +querying and/or modifying \code{\link[=tag]{tag()}} (and \code{\link[=tagList]{tagList()}}) objects. -NULL -## Query methods +\subsection{Query methods}{ Find particular subsets of HTML using CSS selectors (or R functions). -Methods that accept a `cssSelector` are currently only allowed to -reference tag names (e.g, `div`), ids (e.g., `#myID`), and classes -(e.g., `.my-class`). All query methods support combinations of tag -named, ids, and classes (e.g., `div#myID.my-class`), but only -`$find()` currently supports multiple CSS selectors (e.g. `div span` -or `div > a.my-class`). - -* `$find(cssSelector)`: Get the descendants of each selected tag, -filtered by a `cssSelector` (required). -* `$children(cssSelector = NULL)`: Get the direct children of each -selected tag, optionally filtered by a `cssSelector`. -* `$parent(cssSelector = NULL)`: Get the parent of each selected tag, -optionally filtered by a `cssSelector`. -* `$parents(cssSelector = NULL)`: Get the ancestors of each selected tag, -optionally filtered by a `cssSelector`. -* `$closest(cssSelector)`: For each selected tag, get the -closest ancestor tag (including itself) satisfying a `cssSelector`. -* `siblings(cssSelector = NULL)`: Get the siblings of each selected -tag, optionally filtered by a `cssSelector`. -* `$filter(fn)`: Filter the selected tags to those for which -`fn(tag, i)` returns `TRUE`. In addition to an R function with two -arguments (the `tag` and the index `i`), `fn` may also be a valid CSS +Methods that accept a \code{cssSelector} are currently only allowed to +reference tag names (e.g, \code{div}), ids (e.g., \verb{#myID}), and classes +(e.g., \code{.my-class}). All query methods support combinations of tag +named, ids, and classes (e.g., \code{div#myID.my-class}), but only +\verb{$find()} currently supports multiple CSS selectors (e.g. \verb{div span} +or \code{div > a.my-class}). +\itemize{ +\item \verb{$find(cssSelector)}: Get the descendants of each selected tag, +filtered by a \code{cssSelector} (required). +\item \verb{$children(cssSelector = NULL)}: Get the direct children of each +selected tag, optionally filtered by a \code{cssSelector}. +\item \verb{$parent(cssSelector = NULL)}: Get the parent of each selected tag, +optionally filtered by a \code{cssSelector}. +\item \verb{$parents(cssSelector = NULL)}: Get the ancestors of each selected tag, +optionally filtered by a \code{cssSelector}. +\item \verb{$closest(cssSelector)}: For each selected tag, get the +closest ancestor tag (including itself) satisfying a \code{cssSelector}. +\item \code{siblings(cssSelector = NULL)}: Get the siblings of each selected +tag, optionally filtered by a \code{cssSelector}. +\item \verb{$filter(fn)}: Filter the selected tags to those for which +\code{fn(tag, i)} returns \code{TRUE}. In addition to an R function with two +arguments (the \code{tag} and the index \code{i}), \code{fn} may also be a valid CSS selector. -* `$reset()`: A new `tagQuery()` object will be created with the +\item \verb{$reset()}: A new \code{tagQuery()} object will be created with the selected items set to the top level tag objects. -## Modify attributes +} +} -* `$addClass(class)`: `[character()]`\cr Adds class(es) to each +\subsection{Modify attributes}{ +\itemize{ +\item \verb{$addClass(class)}: \verb{[character()]}\cr Adds class(es) to each selected tag. -* `$removeClass(class)`: `[character()]`\cr Removes class(es) to each +\item \verb{$removeClass(class)}: \verb{[character()]}\cr Removes class(es) to each selected tag. -* `$toggleClass(class)`: `[character()]`\cr If a given `class` element +\item \verb{$hasClass(class)}: Determine whether the selected elements have a +given class. Returns a vector of logical values. +\item \verb{$toggleClass(class)}: \verb{[character()]}\cr If a given \code{class} element is missing, add it; otherwise, remove it. -* `$addAttrs(...)`: Add a set of attributes to each selected tag. -* `$removeAttrs(attrs)`: `[character()]`\cr Remove a set of +\item \verb{$addAttrs(...)}: Add a set of attributes to each selected tag. +\item \verb{$removeAttrs(attrs)}: \verb{[character()]}\cr Remove a set of attributes from each selected tag. -* `$emptyAttrs()`: Remove all attributes from each selected tag. -## Logical assertions +\item \verb{$emptyAttrs()}: Remove all attributes from each selected tag. +} +} + +\subsection{Logical assertions}{ These methods return a logical vector with one element per selected tag. - -* `$hasClass(class)`: `[character()]`\cr Do selected tags have +\itemize{ +\item \verb{$hasClass(class)}: \verb{[character()]}\cr Do selected tags have particular class(es)? -* `$hasAttr(attr)`: `[character()]`\cr Do selected tags have +\item \verb{$hasAttr(attr)}: \verb{[character()]}\cr Do selected tags have particular attributes? -## Modify children +} +} -* `$append(...)`: For each selected tag, insert `...` **after** any +\subsection{Modify children}{ +\itemize{ +\item \verb{$append(...)}: For each selected tag, insert \code{...} \strong{after} any existing children. -* `$prepend(...)`: For each selected tag, insert `...` **before** any +\item \verb{$prepend(...)}: For each selected tag, insert \code{...} \strong{before} any existing children. -* `$empty()`: Remove any children of each selected tag. Use this -method before calling `$append(...)` to replace the children of +\item \verb{$empty()}: Remove any children of each selected tag. Use this +method before calling \verb{$append(...)} to replace the children of each selected tag, with other content. -## Modify siblings +} +} -* `$after(...)`: Add all `...` objects as siblings after each of the +\subsection{Modify siblings}{ +\itemize{ +\item \verb{$after(...)}: Add all \code{...} objects as siblings after each of the selected tags. -* `$before(...)`: Add all `...` objects as siblings before each of +\item \verb{$before(...)}: Add all \code{...} objects as siblings before each of the selected tags. -* `$replaceWith(...)`: Replace all selected tags with `...` in the +\item \verb{$replaceWith(...)}: Replace all selected tags with \code{...} in the root tag and clear the selection. -* `$remove(...)`: Remove all selected tags from the root tag and +\item \verb{$remove(...)}: Remove all selected tags from the root tag and clear the current selection. -## Generic methods +} +} -* `$each(fn)`: Modify each selected tag with a function `fn`. -`fn` should accept two arguments: a selected element and +\subsection{Generic methods}{ +\itemize{ +\item \verb{$each(fn)}: Modify each selected tag with a function \code{fn}. +\code{fn} should accept two arguments: a selected element and the selected element's position within the selected elements. This -argument order mimics jQuery's `$().each()` as there is no -concept of a `this` object inside the function execution. To stay +argument order mimics jQuery's \verb{$().each()} as there is no +concept of a \code{this} object inside the function execution. To stay consistent with other methods, the each of the selected tag environments will be given first, followed by the index position. Any alterations to the provided tag environments will persist in calling tag query object. -## Tag Query methods +} +} -* `$asTags(selected = TRUE)`: If `selected = TRUE`, then all +\subsection{Tag Query methods}{ +\itemize{ +\item \verb{$asTags(selected = TRUE)}: If \code{selected = TRUE}, then all previously found elements (and their descendants) will be -converted to tags. If `selected = FALSE`, the top level tag +converted to tags. If \code{selected = FALSE}, the top level tag elements (and their descendants) will be converted to standard tags. If there is more than one element being -returned, a `tagList()` will be used to hold all of the +returned, a \code{tagList()} will be used to hold all of the objects. -* `$root()`: Return all top level (root) tags environments. If there -are more than one, it will be returned within a `tagList()`. If there +\item \verb{$root()}: Return all top level (root) tags environments. If there +are more than one, it will be returned within a \code{tagList()}. If there is only one tag, it will be returned. -* `$selected()`: Returns a list of selected tag environments. -* `$get(position)`: Returns the selected tag element at the position -`position`. -* `$rebuild()`: Makes sure that all tags have been upgraded to tag -environments. Objects wrapped in `HTML()` will not be inspected or +\item \verb{$selected()}: Returns a list of selected tag environments. +\item \verb{$get(position)}: Returns the selected tag element at the position +\code{position}. +\item \verb{$rebuild()}: Makes sure that all tags have been upgraded to tag +environments. Objects wrapped in \code{HTML()} will not be inspected or altered. This method is internally called before each method executes and after any alterations where standard tag objects could be introduced into the tag structure. -* `$print()`: Internal print method. Called by -`print.htmltools.tag.query()` +\item \verb{$print()}: Internal print method. Called by +\code{print.htmltools.tag.query()} } -\arguments{ -\item{tags}{A \code{\link[=tag]{tag()}}, \code{\link[=tagList]{tagList()}}, or \code{\link[=list]{list()}} of tags. If a \code{list()} or -\code{tagList()} is provided, a \code{tagList()} will be returned when calling -\verb{$asTags()}.} } -\value{ -A list of methods for working with the "root" \code{tags} as well as -"selected" subset of \code{tags} (by default, \code{tags} is selected, but methods -such as \verb{$find()} may be used to update the selection). -} -\description{ -\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#experimental}{\figure{lifecycle-experimental.svg}{options: alt='[Experimental]'}}}{\strong{[Experimental]}} -} -\details{ -\code{tagQuery()} provides a \href{https://jquery.com/}{\code{jQuery}} inspired interface for -querying and/or modifying \code{\link[=tag]{tag()}} (and \code{\link[=tagList]{tagList()}}) objects. } From 3f03d6d123bf7ef08119f41b300aca601adb6165 Mon Sep 17 00:00:00 2001 From: Carson Date: Wed, 21 Apr 2021 15:17:56 -0500 Subject: [PATCH 04/31] More vignette improvements --- vignettes/tagQuery.Rmd | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/vignettes/tagQuery.Rmd b/vignettes/tagQuery.Rmd index 03dc897b..d28a97af 100644 --- a/vignettes/tagQuery.Rmd +++ b/vignettes/tagQuery.Rmd @@ -19,9 +19,9 @@ library(htmltools) tagQuery(div(a())) ``` -Notice how `tagQuery()` tracks two essential pieces: the _root_ (i.e., input) tag as well as a set of _selected_ tags (by default the root is selected). This data structure allows us to [efficiently](#performance) [find](#query), and [modify](#modify) particular fragments of the root HTML tag. +Notice how `tagQuery()` tracks two essential pieces: the _root_ (i.e., input) tag as well as a set of _selected_ tags (by default the root is selected). This data structure allows us to [efficiently](#performance) [query](#query) and [modify](#modify) particular fragments of the root HTML tag. -Since `tagQuery()` isn't itself a `tag()` object, it can't be directly to `tag()` or tag rendering functions, but at any given time you can extract the `$root()` or `$selected()` tags. +Since `tagQuery()` isn't itself a `tag()` object, it can't be passed directly to `tag()` or tag rendering functions, but at any given time you can extract the `$root()` or `$selected()` tags. ## Query {#query} @@ -32,9 +32,9 @@ Since `tagQuery()` isn't itself a `tag()` object, it can't be directly to `tag() To begin querying tags, start with either `$find()` or `$children()`. The former traverses _all_ descendants whereas the latter only considers _direct_ descendants. ```{r} -tagQ <- tagQuery(div(a(), div(a()))) -tagQ$find("a")$selected() -tagQ$children("a")$selected() +tagQ <- tagQuery(div(span("foo"), div(span("bar")))) +tagQ$find("span")$selected() +tagQ$children("span")$selected() ``` And since `$find()` considers all descendants, it allows for [descendant selectors](https://www.w3.org/TR/CSS22/selector.html#descendant-selectors) (space) and direct [child selectors](https://www.w3.org/TR/CSS22/selector.html#child-selectors) (>). @@ -43,17 +43,18 @@ And since `$find()` considers all descendants, it allows for [descendant selecto tagQ <- tagQuery(div(div(span(a())))) tagQ$find("div a")$selected() tagQ$find("div > a")$selected() +tagQ$find("div > span > a")$selected() ``` -Since `tagQuery()` methods may be chained together, you could also implement `tagQ$find("div > a")` as: +Since `tagQuery()` methods may be chained together, you could also implement `tagQ$find("div > span > a")` as: ```{r} -tagQ$find("div")$children("a")$selected() +tagQ$find("div")$children("span")$children("a")$selected() ``` ### Siblings -Although `tagQuery()` doesn't support [sibling selectors](https://www.w3schools.com/css/css_combinators.asp) (`+` and `~`), it does provide a `$sibling()` method, which provides essentially the same functionality: +Although `tagQuery()` doesn't (currently) support [sibling selectors](https://www.w3schools.com/css/css_combinators.asp) (`+` and `~`), it does provide a `$sibling()` method, which provides essentially the same functionality: ```{r} tagQ <- tagQuery(div(a(), span(), p())) @@ -63,7 +64,7 @@ tagQ$find("a")$siblings("span")$selected() ### Parents -In some cases, after querying descendants, it can be useful to traverse back up the tag tree to find particular ancestors of a selection. Similar to the difference in `$find()` and `$children()`, `$parents()` traverses _all_ ancestors whereas `$parent()` considers just _direct_ ancestors. +In some cases, after finding children, it can be useful to traverse back up the tag tree to find particular ancestors of a selection. Similar to the difference in `$find()` and `$children()`, `$parents()` traverses _all_ ancestors whereas `$parent()` considers just _direct_ ancestors. ```{r} tagQ <- tagQuery( @@ -80,7 +81,7 @@ tagQ$find("a.foo")$parents()$selected() ### Filter -The `$filter()` method can be used to subset selected tags using an R function or CSS selector. This provides a particularly useful workaround for the fact that `tagQuery()` doesn't currently support [attribute selectors](https://www.w3.org/TR/CSS22/selector.html#attribute-selectors). +The `$filter()` method can be used to subset selected tags using an R function or CSS selector. When combined with the universal selector (`*`), `$filter()` is particularly useful as a workaround for the fact that `tagQuery()` doesn't fully support the entire CSS selector specification. For example, here's a workaround for `tagQuery()`'s currenty lack of support for [attribute selectors](https://www.w3.org/TR/CSS22/selector.html#attribute-selectors): ```{r} tagQ <- tagQuery(div(div(), div("data-foo" = "bar"))) From 3d1b187d302122e0969da222c163dc790bd4b6e8 Mon Sep 17 00:00:00 2001 From: Carson Date: Thu, 22 Apr 2021 14:27:59 -0500 Subject: [PATCH 05/31] wip basic pkgdown site and readme --- NEWS.md | 4 +- R/tag_query.R | 221 ++++++++++++++++----------------- README.Rmd | 17 +++ README.md | 52 +++++++- man/figures/plotly-taglist.png | Bin 0 -> 8378 bytes man/tagQuery.Rd | 169 +++++++++++++------------ pkgdown/_pkgdown.yml | 60 +++++---- vignettes/tagQuery.Rmd | 68 ++++++---- 8 files changed, 342 insertions(+), 249 deletions(-) create mode 100644 README.Rmd create mode 100644 man/figures/plotly-taglist.png diff --git a/NEWS.md b/NEWS.md index d4e64fe1..3c96eef9 100644 --- a/NEWS.md +++ b/NEWS.md @@ -2,7 +2,9 @@ ## New Features & Improvements -* Added `tagQuery(tags)`. A tag query object implements many popular features of jQuery. Similar to jQuery, tag query objects can find internal html using CSS selections. Given a selection (which defaults to the original `tags`), many alterations may be performed before converting the tag query object back to tag objects. (#208) +* `{htmltools}` now has it's own `{pkgdown}` site hosted at . + +* The new `tagQuery()` function provides a [jQuery](https://jquery.com/) inspired interface to query and/or modify HTML `tag()` (e.g., `div()`) or `tagList()` objects. To learn more, see the [{pkgdown} article](). (#208) * Added `tagAddRenderHook()` for delaying modification of a tag object until it is rendered. A list of render-time hooks may also be added via the new `.renderHook` argument added to all `tag()` functions. (#215) diff --git a/R/tag_query.R b/R/tag_query.R index f66241d1..d2ba6a9f 100644 --- a/R/tag_query.R +++ b/R/tag_query.R @@ -330,10 +330,10 @@ isTagQuery <- function(x) { } assertNotTagEnvLike <- function(x, fnName) { if (isTagEnv(x)) { - stop("Tag environment objects (i.e., `tagQuery()`'s tag structure) are not allowed to be used as if they are regular `tag()` objects. Did you forget to call `tagQuery(x)$asTags(selected = TRUE)`?", call. = FALSE) + stop("Tag environment objects (i.e., `tagQuery()`'s tag structure) are not allowed to be used as if they are regular `tag()` objects. Did you forget to call `$root()` or `$selected()`?", call. = FALSE) } if (isTagQuery(x)) { - stop("`tagQuery()` objects are not allowed to be used as if they are regular `tag()` objects. Did you forget to call `$asTags()`?", call. = FALSE) + stop("`tagQuery()` objects are not allowed to be used as if they are regular `tag()` objects. Did you forget to call `$root()` or `$selected()`?", call. = FALSE) } invisible() } @@ -343,8 +343,7 @@ shinyTagEnvStr <- "" #' @export as.tags.htmltools.tag.env <- function(x, ...) { - stop("Method not allowed", call. = TRUE) - # as.tags(tagEnvToTags(x), ...) + as.tags(tagEnvToTags(x), ...) } #' @export print.htmltools.tag.env <- function(x, ...) { @@ -367,7 +366,7 @@ str.htmltools.tag.env <- function(object, ...) { #' @export as.tags.htmltools.tag.query <- function(x, ...) { - stop("Method not allowed", call. = TRUE) + tagQueryAsTagErr() } #' @export print.htmltools.tag.query <- function(x, ...) { @@ -375,34 +374,33 @@ print.htmltools.tag.query <- function(x, ...) { } #' @export format.htmltools.tag.query <- function(x, ...) { - stop( - "`tagQuery()` objects can not be written directly as HTML tags.\n", - "Call either `$root()` or `$selected()` to extract the tags of interest." - ) + tagQueryAsTagErr() } #' @export as.character.htmltools.tag.query <- function(x, ...) { + tagQueryAsTagErr() +} + + +tagQueryAsTagErr <- function() { stop( - "`tagQuery()` objects can not be written directly as HTML tags.\n", - "Call either `$root()` or `$selected()` to extract the tags of interest." + "`tagQuery()` objects can not be written directly as HTML tags.", + "Call either `$root()` or `$selected()` to extract the tags of interest.", + call. = FALSE ) } - -#' Perform jQuery-like alterations on tags +#' Query and modify HTML tags #' -#' `r lifecycle::badge("experimental")` +#' `r lifecycle::badge("experimental")`\cr\cr `tagQuery()` provides a +#' [`jQuery`](https://jquery.com/) inspired interface for querying and modifying +#' [tag()] (and [tagList()]) objects. #' -#' `tagQuery()` provides a [`jQuery`](https://jquery.com/) inspired interface for -#' querying and/or modifying [tag()] (and [tagList()]) objects. -#' -#' @param tags A [tag()], [tagList()], or [list()] of tags. If a `list()` or -#' `tagList()` is provided, a `tagList()` will be returned when calling -#' `$asTags()`. -#' @return A list of methods for working with the "root" `tags` as well as -#' "selected" subset of `tags` (by default, `tags` is selected, but methods -#' such as `$find()` may be used to update the selection). +#' @param tags A [tag()], [tagList()], or [list()] of tags. +#' @return A class with methods that are described below. This class can't be +#' used directly inside other [tag()] or a [renderTags()] context, but +#' underlying HTML tags may be extracted via `$root()` or `$selected()`. #' @export tagQuery <- function(tags) { @@ -504,18 +502,26 @@ tagQuery_ <- function( list( #' @details #' + #' # Vignette + #' + #' To get started with using `tagQuery()`, [see + #' here](https://rstudio.github.io/htmltools/articles/tagQuery.html) or + #' `browseVignettes(package = "htmltools")`. + #' + #' # Methods + #' + #' Unless otherwise stated, `tagQuery()` methods accept a character + #' vector as input. + #' #' ## Query methods #' - #' Find particular subsets of HTML using CSS selectors (or R functions). - #' Methods that accept a `cssSelector` are currently only allowed to - #' reference tag names (e.g, `div`), ids (e.g., `#myID`), and classes - #' (e.g., `.my-class`). All query methods support combinations of tag - #' named, ids, and classes (e.g., `div#myID.my-class`), but only - #' `$find()` currently supports multiple CSS selectors (e.g. `div span` - #' or `div > a.my-class`). + #' Query methods identify particular subsets of the root tag using CSS + #' selectors (or R functions). + #' + #' ### Children #' - #' * `$find(cssSelector)`: Get the descendants of each selected tag, - #' filtered by a `cssSelector` (required). + #' * `$find(cssSelector)`: Get the descendants of + #' each selected tag, filtered by a `cssSelector`. find = function(cssSelector) { rebuild_() @@ -523,50 +529,60 @@ tagQuery_ <- function( tagQueryFindAll(selected_, cssSelector) ) }, - #' * `$children(cssSelector = NULL)`: Get the direct children of each - #' selected tag, optionally filtered by a `cssSelector`. + #' * `$children(cssSelector = NULL)`: Get the direct + #' children of each selected tag, optionally filtered by a + #' `cssSelector`. children = function(cssSelector = NULL) { rebuild_() newTagQuery( tagQueryFindChildren(selected_, cssSelector) ) }, - #' * `$parent(cssSelector = NULL)`: Get the parent of each selected tag, - #' optionally filtered by a `cssSelector`. + #' ### Siblings + #' + #' * `siblings(cssSelector = NULL)`: Get the + #' siblings of each selected tag, optionally filtered by a + #' `cssSelector`. + siblings = function(cssSelector = NULL) { + rebuild_() + newTagQuery( + tagQueryFindSiblings(selected_, cssSelector) + ) + }, + #' ### Parents + #' + #' * `$parent(cssSelector = NULL)`: Get the parent + #' of each selected tag, optionally filtered by a `cssSelector`. parent = function(cssSelector = NULL) { rebuild_() newTagQuery( tagQueryFindParent(selected_, cssSelector) ) }, - #' * `$parents(cssSelector = NULL)`: Get the ancestors of each selected tag, - #' optionally filtered by a `cssSelector`. + #' * `$parents(cssSelector = NULL)`: Get the + #' ancestors of each selected tag, optionally filtered by a + #' `cssSelector`. parents = function(cssSelector = NULL) { rebuild_() newTagQuery( tagQueryFindParents(selected_, cssSelector) ) }, - #' * `$closest(cssSelector)`: For each selected tag, get the - #' closest ancestor tag (including itself) satisfying a `cssSelector`. + #' * `$closest(cssSelector)`: For each selected tag, + #' get the closest ancestor tag (including itself) satisfying a + #' `cssSelector`. closest = function(cssSelector) { rebuild_() newTagQuery( tagQueryFindClosest(selected_, cssSelector) ) }, - #' * `siblings(cssSelector = NULL)`: Get the siblings of each selected - #' tag, optionally filtered by a `cssSelector`. - siblings = function(cssSelector = NULL) { - rebuild_() - newTagQuery( - tagQueryFindSiblings(selected_, cssSelector) - ) - }, - #' * `$filter(fn)`: Filter the selected tags to those for which - #' `fn(tag, i)` returns `TRUE`. In addition to an R function with two - #' arguments (the `tag` and the index `i`), `fn` may also be a valid CSS - #' selector. + #' ### Filter + #' + #' * `$filter(fn)`: Filter the selected tags to those for which `fn(x, + #' i)` returns `TRUE`. In addition to an R function with two arguments + #' (the selected tag `x` and the index `i`), `fn` may also be a valid + #' CSS selector. filter = function(fn) { rebuild_() newTagQuery( @@ -574,45 +590,52 @@ tagQuery_ <- function( rebuild = TRUE ) }, - #' * `$reset()`: A new `tagQuery()` object will be created with the - #' selected items set to the top level tag objects. - reset = function() { + #' ### Reset + #' + #' * `$resetSelection()`: Reset selected tags to the `$root()` tag. + resetSelection = function() { rebuild_() newTagQuery( tagQueryFindReset(root) ) }, - ## end Query methods - - #' ## Modify attributes + #' ## Modify methods #' - #' * `$addClass(class)`: `[character()]`\cr Adds class(es) to each - #' selected tag. + #' Unlike query methods, modify methods modify the `tagQuery()` object. + #' + #' ### Attributes + #' + #' * `$addClass(class)`: Adds class(es) to each selected tag. addClass = function(class) { rebuild_() tagQueryClassAdd(selected_, class) invisible(self) }, - #' * `$removeClass(class)`: `[character()]`\cr Removes class(es) to each - #' selected tag. + #' * `$removeClass(class)`: Removes class(es) to each selected tag. removeClass = function(class) { rebuild_() tagQueryClassRemove(selected_, class) invisible(self) }, - #' * `$hasClass(class)`: Determine whether the selected elements have a - #' given class. Returns a vector of logical values. + #' * `$toggleClass(class)`: If a given `class` element is missing, add + #' it; otherwise, remove it. + toggleClass = function(class) { + rebuild_() + tagQueryClassToggle(selected_, class) + invisible(self) + }, + #' * `$hasClass(class)`: Does each selected tag have particular + #' class(es)? hasClass = function(class) { rebuild_() tagQueryClassHas(selected_, class) }, - #' * `$toggleClass(class)`: `[character()]`\cr If a given `class` element - #' is missing, add it; otherwise, remove it. - toggleClass = function(class) { + #' * `$hasAttr(attr)`: Does each selected tag have particular + #' attributes? + hasAttr = function(attr) { rebuild_() - tagQueryClassToggle(selected_, class) - invisible(self) + tagQueryAttrHas(selected_, attr) }, #' * `$addAttrs(...)`: Add a set of attributes to each selected tag. addAttrs = function(...) { @@ -621,8 +644,8 @@ tagQuery_ <- function( # no need to rebuild_(); already flattened in add attr function invisible(self) }, - #' * `$removeAttrs(attrs)`: `[character()]`\cr Remove a set of - #' attributes from each selected tag. + #' * `$removeAttrs(attrs)`: Remove a set of attributes from each + #' selected tag. removeAttrs = function(attrs) { rebuild_() tagQueryAttrsRemove(selected_, attrs) @@ -634,28 +657,7 @@ tagQuery_ <- function( tagQueryAttrsEmpty(selected_) invisible(self) }, - - - #' ## Logical assertions - #' - #' These methods return a logical vector with one element per selected - #' tag. - #' - #' * `$hasClass(class)`: `[character()]`\cr Do selected tags have - #' particular class(es)? - hasClass = function(class) { - rebuild_() - tagQueryClassHas(selected_, class) - }, - #' * `$hasAttr(attr)`: `[character()]`\cr Do selected tags have - #' particular attributes? - hasAttr = function(attr) { - rebuild_() - tagQueryAttrHas(selected_, attr) - }, - - - #' ## Modify children + #' ### Children #' #' * `$append(...)`: For each selected tag, insert `...` **after** any #' existing children. @@ -682,9 +684,7 @@ tagQuery_ <- function( # no need to rebuild_ invisible(self) }, - - - #' ## Modify siblings + #' ### Siblings #' #' * `$after(...)`: Add all `...` objects as siblings after each of the #' selected tags. @@ -718,18 +718,13 @@ tagQuery_ <- function( newTagQuery(list(), rebuild = TRUE) }, - - #' ## Generic methods + #' ### Custom #' - #' * `$each(fn)`: Modify each selected tag with a function `fn`. - #' `fn` should accept two arguments: a selected element and - #' the selected element's position within the selected elements. This - #' argument order mimics jQuery's `$().each()` as there is no - #' concept of a `this` object inside the function execution. To stay - #' consistent with other methods, the each of the selected tag - #' environments will be given first, followed by the index position. Any - #' alterations to the provided tag environments will persist in calling - #' tag query object. + #' * `$each(fn)`: Modify each selected tag with a function `fn`. `fn` + #' should accept two arguments: the first is the selected tag and second + #' is the selected tags position index. Since the selected tag is a + #' reference, any modifications to it will also modify the `tagQuery()` + #' object. each = function(fn) { rebuild_() tagQueryEach(selected_, fn) @@ -752,23 +747,21 @@ tagQuery_ <- function( rebuild_() tagQuerySelectedAsTags(selected_) }, - #' * `$rebuild()`: Makes sure that all tags have been upgraded to tag - #' environments. Objects wrapped in `HTML()` will not be inspected or - #' altered. This method is internally called before each method executes - #' and after any alterations where standard tag objects could be - #' introduced into the tag structure. rebuild = function() { rebuild_() invisible(self) }, - #' * `$print()`: Internal print method. Called by - #' `print.htmltools.tag.query()` print = function() { rebuild_() # Allows `$print()` to know if there is a root el tagQueryPrint(root, selected_) invisible(self) } + #' @examples + #' + #' tagQ <- tagQuery(div(a())) + #' tagQ$find("a")$addClass("foo") + #' tagQ ) ) self diff --git a/README.Rmd b/README.Rmd new file mode 100644 index 00000000..a2100f29 --- /dev/null +++ b/README.Rmd @@ -0,0 +1,17 @@ +# htmltools + + + +[![R build +status](https://github.com/rstudio/htmltools/workflows/R-CMD-check/badge.svg)](https://github.com/rstudio/htmltools) +[![CRAN +status](https://www.r-pkg.org/badges/version/htmltools)](https://CRAN.R-project.org/package=htmltools) +[![CRAN +Downloads](https://cranlogs.r-pkg.org/badges/grand-total/htmltools)](https://www.rpackages.io/package/htmltools) +[![monthly](https://cranlogs.r-pkg.org/badges/htmltools)](https://www.rpackages.io/package/htmltools) +[![Lifecycle: +experimental](https://img.shields.io/badge/lifecycle-stable-brightgreen.svg)](https://www.tidyverse.org/lifecycle/#stable) + + +Tools for HTML generation and output. + diff --git a/README.md b/README.md index 50b815bc..4091c833 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,4 @@ -# htmltools - - [![R build status](https://github.com/rstudio/htmltools/workflows/R-CMD-check/badge.svg)](https://github.com/rstudio/htmltools) [![CRAN @@ -13,4 +10,51 @@ Downloads](https://cranlogs.r-pkg.org/badges/grand-total/htmltools)](https://www experimental](https://img.shields.io/badge/lifecycle-stable-brightgreen.svg)](https://www.tidyverse.org/lifecycle/#stable) -Tools for HTML generation and output. +# htmltools + +Tools for creating, manipulating, and writing HTML from R. + +## Installation + +Install the stable release of `htmltools` on CRAN: + +```r +install.packages("htmltools") +``` + +Install the development version with: + +```r +remotes::install_github("rstudio/htmltools") +``` + +## Quick overview + +`{htmltools}` makes it easy to customize the user interface (UI) of any [Shiny](https://shiny.rstudio.com/) or [R Markdown](https://rmarkdown.rstudio.com/) project by using R code to generate custom HTML (including JavaScript and CSS). + +[This Shiny article](https://shiny.rstudio.com/articles/html-tags.html) provides a great introduction to `{htmltools}` (even if you're not interested in Shiny). As you'll learn in that article, the general foundation that `{htmltools}` provides allows other R packages (e.g., [`{htmlwidgets}`](http://www.htmlwidgets.org/), [`{crosstalk}`](https://rstudio.github.io/crosstalk/), etc.) to provide "HTML components" in R that users can manipulate and combine in ways the component authors didn't foresee. + +For example, as described in depth [here](https://plotly-r.com/arranging-views.html#arranging-htmlwidgets), `{htmltools}` makes it fairly easily arrange numerous `{htmlwidgets}` (e.g., `{plotly}` graphs) into a single static HTML webpage: + +```r +library(htmltools) +browsable(tagList( + plot_ly(diamonds, x = ~carat, height = 200), + plot_ly(diamonds, x = ~price, height = 200) +)) +``` + +
+ +
+ +Also, thanks to `tagQuery()`, it fairly easy to query and manipulate the underlying HTML structure of components. See the [`tagQuery()` article](https://rstudio.github.io/htmltools/articles/tagQuery.html) to learn more. + + +## Learn more + +If you're looking to learn how to build more custom user interfaces by writing custom HTML/JavaScript/CSS, we recommend the following resource: + +* Shiny's [UI](https://shiny.rstudio.com/articles/#user-interface) and [extensions](https://shiny.rstudio.com/articles/#extensions) articles +* [Outstanding UI with Shiny](https://unleash-shiny.rinterface.com/) by David Granjon +* [JavaScript for R](https://book.javascript-for-r.com/) by John Coene diff --git a/man/figures/plotly-taglist.png b/man/figures/plotly-taglist.png new file mode 100644 index 0000000000000000000000000000000000000000..60fbeeb56fef4a5437844fad3709add499350e0e GIT binary patch literal 8378 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 LAIRm(JbM1$VLO;x literal 0 HcmV?d00001 diff --git a/man/tagQuery.Rd b/man/tagQuery.Rd index 0dc29f94..f4a90ca6 100644 --- a/man/tagQuery.Rd +++ b/man/tagQuery.Rd @@ -2,88 +2,103 @@ % Please edit documentation in R/tag_query.R \name{tagQuery} \alias{tagQuery} -\title{Perform jQuery-like alterations on tags} +\title{Query and modify HTML tags} \usage{ tagQuery(tags) } \arguments{ -\item{tags}{A \code{\link[=tag]{tag()}}, \code{\link[=tagList]{tagList()}}, or \code{\link[=list]{list()}} of tags. If a \code{list()} or -\code{tagList()} is provided, a \code{tagList()} will be returned when calling -\verb{$asTags()}.} +\item{tags}{A \code{\link[=tag]{tag()}}, \code{\link[=tagList]{tagList()}}, or \code{\link[=list]{list()}} of tags.} } \value{ -A list of methods for working with the "root" \code{tags} as well as -"selected" subset of \code{tags} (by default, \code{tags} is selected, but methods -such as \verb{$find()} may be used to update the selection). +A class with methods that are described below. This class can't be +used directly inside other \code{\link[=tag]{tag()}} or a \code{\link[=renderTags]{renderTags()}} context, but +underlying HTML tags may be extracted via \verb{$root()} or \verb{$selected()}. } \description{ -\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#experimental}{\figure{lifecycle-experimental.svg}{options: alt='[Experimental]'}}}{\strong{[Experimental]}} +\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#experimental}{\figure{lifecycle-experimental.svg}{options: alt='[Experimental]'}}}{\strong{[Experimental]}}\cr\cr \code{tagQuery()} provides a +\href{https://jquery.com/}{\code{jQuery}} inspired interface for querying and modifying +\code{\link[=tag]{tag()}} (and \code{\link[=tagList]{tagList()}}) objects. +} +\section{Vignette}{ +To get started with using \code{tagQuery()}, \href{https://rstudio.github.io/htmltools/articles/tagQuery.html}{see here} or +\code{browseVignettes(package = "htmltools")}. } -\details{ -\code{tagQuery()} provides a \href{https://jquery.com/}{\code{jQuery}} inspired interface for -querying and/or modifying \code{\link[=tag]{tag()}} (and \code{\link[=tagList]{tagList()}}) objects. +\section{Methods}{ +Unless otherwise stated, \code{tagQuery()} methods accept a character +vector as input. \subsection{Query methods}{ -Find particular subsets of HTML using CSS selectors (or R functions). -Methods that accept a \code{cssSelector} are currently only allowed to -reference tag names (e.g, \code{div}), ids (e.g., \verb{#myID}), and classes -(e.g., \code{.my-class}). All query methods support combinations of tag -named, ids, and classes (e.g., \code{div#myID.my-class}), but only -\verb{$find()} currently supports multiple CSS selectors (e.g. \verb{div span} -or \code{div > a.my-class}). +Query methods identify particular subsets of the root tag using CSS +selectors (or R functions). +\subsection{Children}{ \itemize{ -\item \verb{$find(cssSelector)}: Get the descendants of each selected tag, -filtered by a \code{cssSelector} (required). -\item \verb{$children(cssSelector = NULL)}: Get the direct children of each -selected tag, optionally filtered by a \code{cssSelector}. -\item \verb{$parent(cssSelector = NULL)}: Get the parent of each selected tag, -optionally filtered by a \code{cssSelector}. -\item \verb{$parents(cssSelector = NULL)}: Get the ancestors of each selected tag, -optionally filtered by a \code{cssSelector}. -\item \verb{$closest(cssSelector)}: For each selected tag, get the -closest ancestor tag (including itself) satisfying a \code{cssSelector}. -\item \code{siblings(cssSelector = NULL)}: Get the siblings of each selected -tag, optionally filtered by a \code{cssSelector}. -\item \verb{$filter(fn)}: Filter the selected tags to those for which -\code{fn(tag, i)} returns \code{TRUE}. In addition to an R function with two -arguments (the \code{tag} and the index \code{i}), \code{fn} may also be a valid CSS -selector. -\item \verb{$reset()}: A new \code{tagQuery()} object will be created with the -selected items set to the top level tag objects. -} -} - -\subsection{Modify attributes}{ +\item \verb{$find(cssSelector)}: Get the descendants of +each selected tag, filtered by a \code{cssSelector}. +\item \verb{$children(cssSelector = NULL)}: Get the direct +children of each selected tag, optionally filtered by a +\code{cssSelector}. +} +} + +\subsection{Siblings}{ \itemize{ -\item \verb{$addClass(class)}: \verb{[character()]}\cr Adds class(es) to each -selected tag. -\item \verb{$removeClass(class)}: \verb{[character()]}\cr Removes class(es) to each -selected tag. -\item \verb{$hasClass(class)}: Determine whether the selected elements have a -given class. Returns a vector of logical values. -\item \verb{$toggleClass(class)}: \verb{[character()]}\cr If a given \code{class} element -is missing, add it; otherwise, remove it. -\item \verb{$addAttrs(...)}: Add a set of attributes to each selected tag. -\item \verb{$removeAttrs(attrs)}: \verb{[character()]}\cr Remove a set of -attributes from each selected tag. -\item \verb{$emptyAttrs()}: Remove all attributes from each selected tag. +\item \code{siblings(cssSelector = NULL)}: Get the +siblings of each selected tag, optionally filtered by a +\code{cssSelector}. +} +} + +\subsection{Parents}{ +\itemize{ +\item \verb{$parent(cssSelector = NULL)}: Get the parent +of each selected tag, optionally filtered by a \code{cssSelector}. +\item \verb{$parents(cssSelector = NULL)}: Get the +ancestors of each selected tag, optionally filtered by a +\code{cssSelector}. +\item \verb{$closest(cssSelector)}: For each selected tag, +get the closest ancestor tag (including itself) satisfying a +\code{cssSelector}. +} +} + +\subsection{Filter}{ +\itemize{ +\item \verb{$filter(fn)}: Filter the selected tags to those for which \code{fn(x, i)} returns \code{TRUE}. In addition to an R function with two arguments +(the selected tag \code{x} and the index \code{i}), \code{fn} may also be a valid +CSS selector. +} +} + +\subsection{Reset}{ +\itemize{ +\item \verb{$resetSelection()}: Reset selected tags to the \verb{$root()} tag. +} } + } -\subsection{Logical assertions}{ +\subsection{Modify methods}{ -These methods return a logical vector with one element per selected -tag. +Unlike query methods, modify methods modify the \code{tagQuery()} object. +\subsection{Attributes}{ \itemize{ -\item \verb{$hasClass(class)}: \verb{[character()]}\cr Do selected tags have -particular class(es)? -\item \verb{$hasAttr(attr)}: \verb{[character()]}\cr Do selected tags have -particular attributes? +\item \verb{$addClass(class)}: Adds class(es) to each selected tag. +\item \verb{$removeClass(class)}: Removes class(es) to each selected tag. +\item \verb{$toggleClass(class)}: If a given \code{class} element is missing, add +it; otherwise, remove it. +\item \verb{$hasClass(class)}: Does each selected tag have particular +class(es)? +\item \verb{$hasAttr(attr)}: Does each selected tag have particular +attributes? +\item \verb{$addAttrs(...)}: Add a set of attributes to each selected tag. +\item \verb{$removeAttrs(attrs)}: Remove a set of attributes from each +selected tag. +\item \verb{$emptyAttrs()}: Remove all attributes from each selected tag. } } -\subsection{Modify children}{ +\subsection{Children}{ \itemize{ \item \verb{$append(...)}: For each selected tag, insert \code{...} \strong{after} any existing children. @@ -95,7 +110,7 @@ each selected tag, with other content. } } -\subsection{Modify siblings}{ +\subsection{Siblings}{ \itemize{ \item \verb{$after(...)}: Add all \code{...} objects as siblings after each of the selected tags. @@ -108,20 +123,18 @@ clear the current selection. } } -\subsection{Generic methods}{ +\subsection{Custom}{ \itemize{ -\item \verb{$each(fn)}: Modify each selected tag with a function \code{fn}. -\code{fn} should accept two arguments: a selected element and -the selected element's position within the selected elements. This -argument order mimics jQuery's \verb{$().each()} as there is no -concept of a \code{this} object inside the function execution. To stay -consistent with other methods, the each of the selected tag -environments will be given first, followed by the index position. Any -alterations to the provided tag environments will persist in calling -tag query object. +\item \verb{$each(fn)}: Modify each selected tag with a function \code{fn}. \code{fn} +should accept two arguments: the first is the selected tag and second +is the selected tags position index. Since the selected tag is a +reference, any modifications to it will also modify the \code{tagQuery()} +object. } } +} + \subsection{Extract HTML tags}{ \itemize{ \item \verb{$root()}: Return the (possibly modified) root \code{tags} as a @@ -129,13 +142,13 @@ tag query object. \item \verb{$selected()}: Converts the selected tag environments to standard \code{\link{tag}} objects. The selected tags will be returned in a \code{\link[=tagList]{tagList()}}. -\item \verb{$rebuild()}: Makes sure that all tags have been upgraded to tag -environments. Objects wrapped in \code{HTML()} will not be inspected or -altered. This method is internally called before each method executes -and after any alterations where standard tag objects could be -introduced into the tag structure. -\item \verb{$print()}: Internal print method. Called by -\code{print.htmltools.tag.query()} } } } + +\examples{ + +tagQ <- tagQuery(div(a())) +tagQ$find("a")$addClass("foo") +tagQ +} diff --git a/pkgdown/_pkgdown.yml b/pkgdown/_pkgdown.yml index b5601c7c..0fd76035 100644 --- a/pkgdown/_pkgdown.yml +++ b/pkgdown/_pkgdown.yml @@ -1,37 +1,45 @@ url: https://rstudio.github.io/htmltools/ -# development: -# mode: auto +development: + mode: release +toc: + depth: 2 -# template: -# # assets: pkgdown/assets -# params: -# docsearch: -# api_key: 'e8f0e2420e2e9aac7973efb2cc877b06' -# index_name: 'rplumber' +template: + package: quillt + params: + footer: htmltools is an R package developed by
RStudio + # docsearch: + # api_key: 53fd87c79a13e431b4298611470d023a + # index_name: bslib -# articles: -# - title: Creating APIs in R with Plumber -# navbar: ~ -# # desc: description here -# contents: -# - 'introduction' -# - 'quickstart' -# - 'routing-and-input' -# - 'rendering-output' -# - 'execution-model' -# - 'security' -# - 'hosting' -# - 'programmatic-usage' -# - 'annotations' -# - 'tips-and-tricks' -# - 'migration' +navbar: + type: default + structure: + left: [intro, articles] + right: [reference, news, github] + components: + home: ~ + articles: + text: Articles + menu: + - text: Query and modify tags + href: articles/tagQuery.html + reference: + text: Reference + href: reference/index.html + news: + text: News + href: news/index.html + github: + icon: fab fa-github fa-lg + href: https://github.com/rstudio/htmltools reference: -- title: Tags +- title: Create HTML tags contents: - '`builder`' - '`HTML`' @@ -42,7 +50,7 @@ reference: - '`as.tags`' - '`withTags`' -- title: Rendering tags +- title: Render tags contents: - '`print.html`' - '`renderDependencies`' diff --git a/vignettes/tagQuery.Rmd b/vignettes/tagQuery.Rmd index d28a97af..db382253 100644 --- a/vignettes/tagQuery.Rmd +++ b/vignettes/tagQuery.Rmd @@ -12,14 +12,14 @@ knitr::opts_chunk$set( ) ``` -`tagQuery()` provides a [jQuery](https://jquery.com) inspired interface to query and/or modify HTML fragments in R. To start, pass either a `tag()` (e.g., `div()`) or `tagList()` object to `tagQuery()`: +`tagQuery()` provides a [jQuery](https://jquery.com) inspired interface to query and modify HTML fragments in R. To start, pass either a `tag()` (e.g., `div()`) or `tagList()` object to `tagQuery()`: ```{r} library(htmltools) tagQuery(div(a())) ``` -Notice how `tagQuery()` tracks two essential pieces: the _root_ (i.e., input) tag as well as a set of _selected_ tags (by default the root is selected). This data structure allows us to [efficiently](#performance) [query](#query) and [modify](#modify) particular fragments of the root HTML tag. +Notice how `tagQuery()` tracks two essential pieces: the _root_ (i.e., input) tag as well as _selected_ tags (by default the root is selected). This data structure allows us to [efficiently](#performance) [query](#query) and [modify](#modify) particular fragments of the root HTML tag. Since `tagQuery()` isn't itself a `tag()` object, it can't be passed directly to `tag()` or tag rendering functions, but at any given time you can extract the `$root()` or `$selected()` tags. @@ -58,7 +58,7 @@ Although `tagQuery()` doesn't (currently) support [sibling selectors](https://ww ```{r} tagQ <- tagQuery(div(a(), span(), p())) -# morally equivalent to `tagQ$find("a ~ span")` +# The moral equivalent to `tagQ$find("a ~ span")` tagQ$find("a")$siblings("span")$selected() ``` @@ -81,61 +81,77 @@ tagQ$find("a.foo")$parents()$selected() ### Filter -The `$filter()` method can be used to subset selected tags using an R function or CSS selector. When combined with the universal selector (`*`), `$filter()` is particularly useful as a workaround for the fact that `tagQuery()` doesn't fully support the entire CSS selector specification. For example, here's a workaround for `tagQuery()`'s currenty lack of support for [attribute selectors](https://www.w3.org/TR/CSS22/selector.html#attribute-selectors): +The `$filter()` method can be used to subset selected tags using an R function or CSS selector. When combined with the universal selector (`*`), `$filter()` is particularly useful as a workaround for the fact that `tagQuery()` doesn't fully support the entire CSS selector specification. For example, here's a workaround for `tagQuery()`'s current lack of support for [attribute selectors](https://www.w3.org/TR/CSS22/selector.html#attribute-selectors): ```{r} tagQ <- tagQuery(div(div(), div("data-foo" = "bar"))) -# moral equivalent to `tagQ$find("[data-foo]")` +# The moral equivalent to `tagQ$find("[data-foo]")` tagQ$ find("*")$ filter(function(x, i) tagHasAttribute(x, "data-foo"))$ selected() ``` +### Reset -## Modify {#modify} - -### Mutation - -In contrast to other functions in `{htmltools}` designed to modify `tag()`s (e.g., `tagAppendAttributes()`, `tagAppendChild()`, etc), `tagQuery()`'s modifier methods (e.g., `$addClass()`) _modify their input_: +To reset the set of selected tags to the root tag, use `$reset()`: ```{r} -tagQ$addClass("foo") -tagQ +tagQ <- tagQuery(div(a()))$find("a") +tagQ$selected() +tagQ$reset()$selected() ``` -As we'll see in [Query](#query), the [mutable](https://developer.mozilla.org/en-US/docs/Glossary/Mutable) nature of `tagQuery()`'s modifier methods enables [efficient](#performance) modification of child tags without losing a reference to the root tag. -### Attributes +## Modify {#modify} -`tagQuery()` has numerous methods for modifying attributes +`tagQuery()` provides numerous functions for modifying HTML [attributes](#modify-attrs), [children](#modify-child), or [sibling](#modify-sibling) tags of the current query selection. Unlike query methods, modifier methods _modify their input_ (both the root and the selection). For example, note how the `$addClass()` call here modifies `tagQ` (but `$find()` doesn't): ```{r} -tagQ$addAttrs("data-bar" = "foo") +tagQ <- tagQuery(div(a())) +tagQ$find("a")$addClass("foo") tagQ ``` -### Children +The [mutable](https://en.wikipedia.org/wiki/Immutable_object) behavior of modifier methods not only allows us to modify child tags without losing a reference to the root tag, but it also makes modifications more [performant](#performance) than they'd otherwise be. -TODO: useful example(s) +### Attributes {#modify-attrs} -### Siblings +Use `$addAttrs()` to add and `$removeAttrs()` to remove any HTML attribute from each selected tag. If you're just working with `class` attributes, consider using the more convenient `$addClass()`, `$removeClass()`, or `$toggleClass()` -TODO: useful example(s) +```{r} +tagQ <- tagQuery(div(span(a()), span())) +tagQ$find("span")$addAttrs("data-bar" = "foo") +tagQ$root() +``` +Also, to check whether each selected tag has a certain attribute, use `$hasAttr()` (or `$hasClass()`) +```{r} +tagQ$find("span")$hasAttr("data-bar") +``` -## Extract {#extract} +### Children {#modify-child} -Since `tagQuery()` objects represent both a root tag and a set of selected tags, you'll need to decide whether you want the root or selection via `$asTags()` when trying to use it inside other functions like `tag()`, `tagList()`, or `renderTags()` +```{r} +tagQ <- tagQuery(div(a())) +tagQ$prepend(span()) +tagQ$append(p()) +tagQ$root() +``` + +### Siblings {#modify-sibling} ```{r} -tags$body( - tagQ$asTags() -) +tagQ <- tagQuery(div(a())) +tagQ$before(span()) +tagQ$after(p()) +tagQ$root() ``` + From 68da5e0089c60a274d17822ea0d4cc05bf0a17d9 Mon Sep 17 00:00:00 2001 From: Carson Date: Thu, 22 Apr 2021 15:54:57 -0500 Subject: [PATCH 06/31] put replace methods into their own section --- R/tag_query.R | 49 ++++++++++++++++++++++-------------------- vignettes/tagQuery.Rmd | 46 +++++++++++++++++++++++++++++++++------ 2 files changed, 65 insertions(+), 30 deletions(-) diff --git a/R/tag_query.R b/R/tag_query.R index 6e594b51..81f5be04 100644 --- a/R/tag_query.R +++ b/R/tag_query.R @@ -577,7 +577,7 @@ tagQuery_ <- function( tagQueryFindClosest(selected_, cssSelector) ) }, - #' ### Filter + #' ### Custom filter #' #' * `$filter(fn)`: Filter the selected tags to those for which `fn(x, #' i)` returns `TRUE`. In addition to an R function with two arguments @@ -592,7 +592,8 @@ tagQuery_ <- function( }, #' ### Reset #' - #' * `$resetSelection()`: Reset selected tags to the `$root()` tag. + #' * `$resetSelection()`: Reset selected tags to the `$root()` tag. Useful + #' in combination with `$replaceWith()` since it empties the selection. resetSelection = function() { rebuild_() newTagQuery( @@ -675,15 +676,6 @@ tagQuery_ <- function( rebuild_() invisible(self) }, - #' * `$empty()`: Remove any children of each selected tag. Use this - #' method before calling `$append(...)` to replace the children of - #' each selected tag, with other content. - empty = function() { - rebuild_() - tagQueryChildrenEmpty(selected_) - # no need to rebuild_ - invisible(self) - }, #' ### Siblings #' #' * `$after(...)`: Add all `...` objects as siblings after each of the @@ -702,7 +694,23 @@ tagQuery_ <- function( rebuild_() invisible(self) }, - #' * `$replaceWith(...)`: Replace all selected tags with `...` in the + #' ### Custom + #' + #' * `$each(fn)`: Modify each selected tag with a function `fn`. `fn` + #' should accept two arguments: the first is the selected tag and second + #' is the selected tags position index. Since the selected tag is a + #' reference, any modifications to it will also modify the `tagQuery()` + #' object. + each = function(fn) { + rebuild_() + tagQueryEach(selected_, fn) + rebuild_() + invisible(self) + }, + + #' ## Replace methods + #' + #' #' * `$replaceWith(...)`: Replace all selected tags with `...` in the #' root tag and clear the selection. replaceWith = function(...) { rebuild_() @@ -717,18 +725,13 @@ tagQuery_ <- function( # Remove items from selected info newTagQuery(list(), rebuild = TRUE) }, - - #' ### Custom - #' - #' * `$each(fn)`: Modify each selected tag with a function `fn`. `fn` - #' should accept two arguments: the first is the selected tag and second - #' is the selected tags position index. Since the selected tag is a - #' reference, any modifications to it will also modify the `tagQuery()` - #' object. - each = function(fn) { - rebuild_() - tagQueryEach(selected_, fn) + #' * `$empty()`: Remove any children of each selected tag. Use this + #' method before calling `$append(...)` to replace the children of + #' each selected tag, with other content. + empty = function() { rebuild_() + tagQueryChildrenEmpty(selected_) + # no need to rebuild_ invisible(self) }, diff --git a/vignettes/tagQuery.Rmd b/vignettes/tagQuery.Rmd index db382253..356cac95 100644 --- a/vignettes/tagQuery.Rmd +++ b/vignettes/tagQuery.Rmd @@ -19,7 +19,7 @@ library(htmltools) tagQuery(div(a())) ``` -Notice how `tagQuery()` tracks two essential pieces: the _root_ (i.e., input) tag as well as _selected_ tags (by default the root is selected). This data structure allows us to [efficiently](#performance) [query](#query) and [modify](#modify) particular fragments of the root HTML tag. +Notice how `tagQuery()` tracks two essential pieces: the _root_ (i.e., input) tag as well as _selected_ tags (by default the root is selected). This data structure allows us to [efficiently](#performance) [query](#query), [modify](#modify), and [replace](#replace) particular fragments of the root HTML tag. Since `tagQuery()` isn't itself a `tag()` object, it can't be passed directly to `tag()` or tag rendering functions, but at any given time you can extract the `$root()` or `$selected()` tags. @@ -133,22 +133,54 @@ tagQ$find("span")$hasAttr("data-bar") ### Children {#modify-child} +Use `$prepend()` to insert content before the children of each selected tag and `$append()` to insert content after: + ```{r} -tagQ <- tagQuery(div(a())) -tagQ$prepend(span()) -tagQ$append(p()) +tagQ <- tagQuery(div(p(a()))) +tagQ$find("p")$prepend(span())$append(tags$table()) tagQ$root() ``` +If you'd like to replace all the children, then you can first call `$empty()` before `$append()`. If you like to just remove particular child elements, then you should call `$children()` + the `$remove()` sibling method. + ### Siblings {#modify-sibling} +Use `$before()` to insert content before each selected tag and `$after()` to insert content after: + ```{r} -tagQ <- tagQuery(div(a())) -tagQ$before(span()) -tagQ$after(p()) +tagQ <- tagQuery(div(p(a()))) +tagQ$find("a")$before(span())$after(tags$table()) tagQ$root() ``` +## Replace {#replace} + +As with `tagQuery()`'s modifier methods, its replace methods also modify their input. They also empty selected tags, so you may want to `$resetSelection()` if you want to make more queries or modifications after-the-fact. + +Use `$replaceWith()` to replace selected tags with some other content. + +```{r} +tagQ <- tagQuery(div(a())) +tagQ$find("a")$replaceWith(p()) +tagQ +``` + +Use `$remove()` to replace selected tags with nothing: + +```{r} +tagQ <- tagQuery(div(a())) +tagQ$find("a")$remove() +tagQ +``` + +And use `$empty()` to replace the _children_ of the selected tags with nothing: + +```{r} +tagQ <- tagQuery(div(span(a()))) +tagQ$find("span")$empty() +tagQ +``` + " #' @export as.tags.htmltools.tag.env <- function(x, ...) { - as.tags(tagEnvToTags(x), ...) + stop("Method not allowed", call. = TRUE) + # as.tags(tagEnvToTags(x), ...) } #' @export print.htmltools.tag.env <- function(x, ...) { diff --git a/vignettes/tagQuery.Rmd b/vignettes/tagQuery.Rmd index 356cac95..6ea9d72e 100644 --- a/vignettes/tagQuery.Rmd +++ b/vignettes/tagQuery.Rmd @@ -94,12 +94,12 @@ tagQ$ ### Reset -To reset the set of selected tags to the root tag, use `$reset()`: +To reset the set of selected tags to the root tag, use `$resetSelected()`: ```{r} tagQ <- tagQuery(div(a()))$find("a") tagQ$selected() -tagQ$reset()$selected() +tagQ$resetSelected()$selected() ``` From b4515d6abb4764653c8936dd95a53d963416366d Mon Sep 17 00:00:00 2001 From: Carson Date: Tue, 4 May 2021 09:12:04 -0500 Subject: [PATCH 10/31] fix incorrect merge conflict --- R/tag_query.R | 9 +++------ man/tagQuery.Rd | 6 ++++-- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/R/tag_query.R b/R/tag_query.R index 61346560..c550e38f 100644 --- a/R/tag_query.R +++ b/R/tag_query.R @@ -503,9 +503,8 @@ tagQuery_ <- function( #' #' # Vignette #' - #' To get started with using `tagQuery()`, [see - #' here](https://rstudio.github.io/htmltools/articles/tagQuery.html) or - #' `browseVignettes(package = "htmltools")`. + #' To get started with using `tagQuery()`, visit + #' . #' #' # Methods #' @@ -540,7 +539,6 @@ tagQuery_ <- function( #' siblings of each selected tag, optionally filtered by a #' `cssSelector`. siblings = function(cssSelector = NULL) { - rebuild_() newTagQuery( tagQueryFindSiblings(selected_, cssSelector) ) @@ -704,8 +702,7 @@ tagQuery_ <- function( #' each selected tag, with other content. empty = function() { tagQueryChildrenEmpty(selected_) - # MUST rebuild full tree as anything could have been done to the tag envs - rebuild_() + # no need to rebuild_ self }, diff --git a/man/tagQuery.Rd b/man/tagQuery.Rd index 64c2e347..71232cf7 100644 --- a/man/tagQuery.Rd +++ b/man/tagQuery.Rd @@ -20,8 +20,8 @@ underlying HTML tags may be extracted via \verb{$root()} or \verb{$selected()}. \code{\link[=tag]{tag()}} (and \code{\link[=tagList]{tagList()}}) objects. } \section{Vignette}{ -To get started with using \code{tagQuery()}, \href{https://rstudio.github.io/htmltools/articles/tagQuery.html}{see here} or -\code{browseVignettes(package = "htmltools")}. +To get started with using \code{tagQuery()}, visit +\url{https://rstudio.github.io/htmltools/articles/tagQuery.html}. } \section{Methods}{ @@ -158,4 +158,6 @@ to standard \code{\link{tag}} objects. The selected tags will be returned in a tagQ <- tagQuery(div(a())) tagQ$find("a")$addClass("foo") tagQ + +# To learn more, visit https://rstudio.github.io/htmltools/articles/tagQuery.html } From bf72fa431dd0b57082fdf921c9b597dbe5f5cb7b Mon Sep 17 00:00:00 2001 From: Carson Date: Tue, 4 May 2021 09:28:20 -0500 Subject: [PATCH 11/31] Show R console output when printing tags/tagLists --- vignettes/tagQuery.Rmd | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/vignettes/tagQuery.Rmd b/vignettes/tagQuery.Rmd index 6ea9d72e..f1e855e6 100644 --- a/vignettes/tagQuery.Rmd +++ b/vignettes/tagQuery.Rmd @@ -4,12 +4,22 @@ output: html_document --- ```{r, include=FALSE} -knitr::opts_chunk$set( +library(knitr) +opts_chunk$set( collapse = TRUE, comment = "#>", out.width = "100%", fig.align = 'center' ) +# Show R console output instead of embedding the HTML +registerS3method( + "knit_print", "shiny.tag", + function(x, ...) print.shiny.tag(x, browse = FALSE) +) +registerS3method( + "knit_print", "shiny.tag.list", + function(x, ...) print.shiny.tag.list(x, browse = FALSE) +) ``` `tagQuery()` provides a [jQuery](https://jquery.com) inspired interface to query and modify HTML fragments in R. To start, pass either a `tag()` (e.g., `div()`) or `tagList()` object to `tagQuery()`: From 910b1ab11dafa82d4242ac49b65f54aab1137cb4 Mon Sep 17 00:00:00 2001 From: Carson Date: Tue, 4 May 2021 09:47:49 -0500 Subject: [PATCH 12/31] Improve code example output --- vignettes/tagQuery.Rmd | 122 +++++++++++++++++++++++++++++------------ 1 file changed, 88 insertions(+), 34 deletions(-) diff --git a/vignettes/tagQuery.Rmd b/vignettes/tagQuery.Rmd index f1e855e6..2e7e33cb 100644 --- a/vignettes/tagQuery.Rmd +++ b/vignettes/tagQuery.Rmd @@ -5,6 +5,7 @@ output: html_document ```{r, include=FALSE} library(knitr) +library(htmltools) opts_chunk$set( collapse = TRUE, comment = "#>", @@ -14,11 +15,11 @@ opts_chunk$set( # Show R console output instead of embedding the HTML registerS3method( "knit_print", "shiny.tag", - function(x, ...) print.shiny.tag(x, browse = FALSE) + getS3method("print", "shiny.tag") ) registerS3method( "knit_print", "shiny.tag.list", - function(x, ...) print.shiny.tag.list(x, browse = FALSE) + getS3method("print", "shiny.tag.list") ) ``` @@ -42,7 +43,11 @@ Since `tagQuery()` isn't itself a `tag()` object, it can't be passed directly to To begin querying tags, start with either `$find()` or `$children()`. The former traverses _all_ descendants whereas the latter only considers _direct_ descendants. ```{r} -tagQ <- tagQuery(div(span("foo"), div(span("bar")))) +(html <- div(span("foo"), div(span("bar")))) +``` + +```{r} +tagQ <- tagQuery(html) tagQ$find("span")$selected() tagQ$children("span")$selected() ``` @@ -50,7 +55,11 @@ tagQ$children("span")$selected() And since `$find()` considers all descendants, it allows for [descendant selectors](https://www.w3.org/TR/CSS22/selector.html#descendant-selectors) (space) and direct [child selectors](https://www.w3.org/TR/CSS22/selector.html#child-selectors) (>). ```{r} -tagQ <- tagQuery(div(div(span(a())))) +(html <- div(div(span(a())))) +``` + +```{r} +tagQ <- tagQuery(html) tagQ$find("div a")$selected() tagQ$find("div > a")$selected() tagQ$find("div > span > a")$selected() @@ -67,7 +76,11 @@ tagQ$find("div")$children("span")$children("a")$selected() Although `tagQuery()` doesn't (currently) support [sibling selectors](https://www.w3schools.com/css/css_combinators.asp) (`+` and `~`), it does provide a `$sibling()` method, which provides essentially the same functionality: ```{r} -tagQ <- tagQuery(div(a(), span(), p())) +(html <- div(a(), span(), p())) +``` + +```{r} +tagQ <- tagQuery(html) # The moral equivalent to `tagQ$find("a ~ span")` tagQ$find("a")$siblings("span")$selected() ``` @@ -77,12 +90,11 @@ tagQ$find("a")$siblings("span")$selected() In some cases, after finding children, it can be useful to traverse back up the tag tree to find particular ancestors of a selection. Similar to the difference in `$find()` and `$children()`, `$parents()` traverses _all_ ancestors whereas `$parent()` considers just _direct_ ancestors. ```{r} -tagQ <- tagQuery( - div( - div(a(class = "foo")), - span(a()) - ) -) +(html <- div(div(a(class = "foo")), span(a()))) +``` + +```{r} +tagQ <- tagQuery(html) tagQ$find("a.foo")$parent()$selected() tagQ$find("a.foo")$parents()$selected() ``` @@ -94,7 +106,11 @@ tagQ$find("a.foo")$parents()$selected() The `$filter()` method can be used to subset selected tags using an R function or CSS selector. When combined with the universal selector (`*`), `$filter()` is particularly useful as a workaround for the fact that `tagQuery()` doesn't fully support the entire CSS selector specification. For example, here's a workaround for `tagQuery()`'s current lack of support for [attribute selectors](https://www.w3.org/TR/CSS22/selector.html#attribute-selectors): ```{r} -tagQ <- tagQuery(div(div(), div("data-foo" = "bar"))) +(html <- div(div(), div("data-foo" = "bar"))) +``` + +```{r} +tagQ <- tagQuery(html) # The moral equivalent to `tagQ$find("[data-foo]")` tagQ$ find("*")$ @@ -107,7 +123,11 @@ tagQ$ To reset the set of selected tags to the root tag, use `$resetSelected()`: ```{r} -tagQ <- tagQuery(div(a()))$find("a") +(html <- div(a())) +``` + +```{r} +tagQ <- tagQuery(html)$find("a") tagQ$selected() tagQ$resetSelected()$selected() ``` @@ -118,9 +138,15 @@ tagQ$resetSelected()$selected() `tagQuery()` provides numerous functions for modifying HTML [attributes](#modify-attrs), [children](#modify-child), or [sibling](#modify-sibling) tags of the current query selection. Unlike query methods, modifier methods _modify their input_ (both the root and the selection). For example, note how the `$addClass()` call here modifies `tagQ` (but `$find()` doesn't): ```{r} -tagQ <- tagQuery(div(a())) -tagQ$find("a")$addClass("foo") -tagQ +(html <- div(a())) +``` + +```{r} +tagQ <- tagQuery(html) +tagQ$ + find("a")$ + addClass("foo")$ + root() ``` The [mutable](https://en.wikipedia.org/wiki/Immutable_object) behavior of modifier methods not only allows us to modify child tags without losing a reference to the root tag, but it also makes modifications more [performant](#performance) than they'd otherwise be. @@ -130,9 +156,15 @@ The [mutable](https://en.wikipedia.org/wiki/Immutable_object) behavior of modifi Use `$addAttrs()` to add and `$removeAttrs()` to remove any HTML attribute from each selected tag. If you're just working with `class` attributes, consider using the more convenient `$addClass()`, `$removeClass()`, or `$toggleClass()` ```{r} -tagQ <- tagQuery(div(span(a()), span())) -tagQ$find("span")$addAttrs("data-bar" = "foo") -tagQ$root() +(html <- div(span(a()), span())) +``` + +```{r} +tagQ <- tagQuery(html) +tagQ$ + find("span")$ + addAttrs("data-bar" = "foo")$ + root() ``` Also, to check whether each selected tag has a certain attribute, use `$hasAttr()` (or `$hasClass()`) @@ -146,9 +178,16 @@ tagQ$find("span")$hasAttr("data-bar") Use `$prepend()` to insert content before the children of each selected tag and `$append()` to insert content after: ```{r} -tagQ <- tagQuery(div(p(a()))) -tagQ$find("p")$prepend(span())$append(tags$table()) -tagQ$root() +(html <- div(p(a()))) +``` + +```{r} +tagQ <- tagQuery(html) +tagQ$ + find("p")$ + prepend(span())$ + append(tags$table())$ + root() ``` If you'd like to replace all the children, then you can first call `$empty()` before `$append()`. If you like to just remove particular child elements, then you should call `$children()` + the `$remove()` sibling method. @@ -158,9 +197,16 @@ If you'd like to replace all the children, then you can first call `$empty()` be Use `$before()` to insert content before each selected tag and `$after()` to insert content after: ```{r} -tagQ <- tagQuery(div(p(a()))) -tagQ$find("a")$before(span())$after(tags$table()) -tagQ$root() +(html <- div(p(a()))) +``` + +```{r} +tagQ <- tagQuery(html) +tagQ$ + find("a")$ + before(span())$ + after(tags$table())$ + root() ``` ## Replace {#replace} @@ -170,25 +216,33 @@ As with `tagQuery()`'s modifier methods, its replace methods also modify their i Use `$replaceWith()` to replace selected tags with some other content. ```{r} -tagQ <- tagQuery(div(a())) -tagQ$find("a")$replaceWith(p()) -tagQ +(html <- div(a())) +``` + +```{r} +tagQ <- tagQuery(html) +tagQ$ + find("a")$ + replaceWith(p())$ + root() ``` Use `$remove()` to replace selected tags with nothing: ```{r} -tagQ <- tagQuery(div(a())) -tagQ$find("a")$remove() -tagQ +tagQ <- tagQuery(html) +tagQ$find("a")$remove()$root() ``` And use `$empty()` to replace the _children_ of the selected tags with nothing: ```{r} -tagQ <- tagQuery(div(span(a()))) -tagQ$find("span")$empty() -tagQ +(html <- div(span(a()))) +``` + +```{r} +tagQ <- tagQuery(html) +tagQ$find("span")$empty()$root() ``` From a419f9ebf5c864029cc79956788142cfe6e5e069 Mon Sep 17 00:00:00 2001 From: Carson Date: Tue, 4 May 2021 10:23:15 -0500 Subject: [PATCH 13/31] fix weird conflict issue --- R/tag_query.R | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/R/tag_query.R b/R/tag_query.R index ddcfd242..350d0540 100644 --- a/R/tag_query.R +++ b/R/tag_query.R @@ -365,11 +365,11 @@ print.shiny.tag.query <- function(x, ...) { x$print() } #' @export -format.htmltools.tag.query <- function(x, ...) { +format.shiny.tag.query <- function(x, ...) { tagQueryAsTagErr() } #' @export -as.character.htmltools.tag.query <- function(x, ...) { +as.character.shiny.tag.query <- function(x, ...) { tagQueryAsTagErr() } From 592a13f16ae826e28206b87df0c17594e9cd87d6 Mon Sep 17 00:00:00 2001 From: Carson Date: Tue, 4 May 2021 10:24:53 -0500 Subject: [PATCH 14/31] Just use a README.md --- README.Rmd | 17 ----------------- 1 file changed, 17 deletions(-) delete mode 100644 README.Rmd diff --git a/README.Rmd b/README.Rmd deleted file mode 100644 index a2100f29..00000000 --- a/README.Rmd +++ /dev/null @@ -1,17 +0,0 @@ -# htmltools - - - -[![R build -status](https://github.com/rstudio/htmltools/workflows/R-CMD-check/badge.svg)](https://github.com/rstudio/htmltools) -[![CRAN -status](https://www.r-pkg.org/badges/version/htmltools)](https://CRAN.R-project.org/package=htmltools) -[![CRAN -Downloads](https://cranlogs.r-pkg.org/badges/grand-total/htmltools)](https://www.rpackages.io/package/htmltools) -[![monthly](https://cranlogs.r-pkg.org/badges/htmltools)](https://www.rpackages.io/package/htmltools) -[![Lifecycle: -experimental](https://img.shields.io/badge/lifecycle-stable-brightgreen.svg)](https://www.tidyverse.org/lifecycle/#stable) - - -Tools for HTML generation and output. - From 0e3f820b1009c1e1f7f7f3b2c9038d79208e36a9 Mon Sep 17 00:00:00 2001 From: Barret Schloerke Date: Wed, 5 May 2021 17:31:33 -0400 Subject: [PATCH 15/31] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 4091c833..cbaea8dc 100644 --- a/README.md +++ b/README.md @@ -30,11 +30,11 @@ remotes::install_github("rstudio/htmltools") ## Quick overview -`{htmltools}` makes it easy to customize the user interface (UI) of any [Shiny](https://shiny.rstudio.com/) or [R Markdown](https://rmarkdown.rstudio.com/) project by using R code to generate custom HTML (including JavaScript and CSS). +`{htmltools}` makes it easy to customize the user interface (UI) of any [Shiny](https://shiny.rstudio.com/) or [R Markdown](https://rmarkdown.rstudio.com/) project by using R code to generate custom HTML (including JavaScript and CSS). [This Shiny article](https://shiny.rstudio.com/articles/html-tags.html) provides a great introduction to `{htmltools}` (even if you're not interested in Shiny). As you'll learn in that article, the general foundation that `{htmltools}` provides allows other R packages (e.g., [`{htmlwidgets}`](http://www.htmlwidgets.org/), [`{crosstalk}`](https://rstudio.github.io/crosstalk/), etc.) to provide "HTML components" in R that users can manipulate and combine in ways the component authors didn't foresee. -For example, as described in depth [here](https://plotly-r.com/arranging-views.html#arranging-htmlwidgets), `{htmltools}` makes it fairly easily arrange numerous `{htmlwidgets}` (e.g., `{plotly}` graphs) into a single static HTML webpage: +For example, as described in depth [here](https://plotly-r.com/arranging-views.html#arranging-htmlwidgets), `{htmltools}` makes it fairly easy to arrange numerous `{htmlwidgets}` (e.g., `{plotly}` graphs) into a single static HTML webpage: ```r library(htmltools) From 5c23fd2c78715c16fba10f764f4ab43428201a88 Mon Sep 17 00:00:00 2001 From: Barret Schloerke Date: Wed, 5 May 2021 17:32:01 -0400 Subject: [PATCH 16/31] Add "Getting Started" article which redirects to Shiny custom html article --- vignettes/htmltools.Rmd | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 vignettes/htmltools.Rmd diff --git a/vignettes/htmltools.Rmd b/vignettes/htmltools.Rmd new file mode 100644 index 00000000..b68da8e3 --- /dev/null +++ b/vignettes/htmltools.Rmd @@ -0,0 +1,8 @@ +--- +title: "Customize your UI with HTML" +output: html_document +--- + + + +Redirecting to [Customize your UI with HTML](https://shiny.rstudio.com/articles/html-tags.html) From 0b67ac27d31ef9da6466c661296dccc4b237433c Mon Sep 17 00:00:00 2001 From: Barret Schloerke Date: Thu, 6 May 2021 16:31:33 -0400 Subject: [PATCH 17/31] Initialize hex sticker (#246) --- .Rbuildignore | 1 + README.md | 2 +- logo/.gitignore | 1 + logo/htmltools.png | Bin 0 -> 125055 bytes logo/htmltools.svg | 157 +++++++++++++++++++ logo/htmltools_src.svg | 32 ++++ man/figures/logo.png | Bin 0 -> 13446 bytes pkgdown/favicon/apple-touch-icon-120x120.png | Bin 0 -> 5645 bytes pkgdown/favicon/apple-touch-icon-152x152.png | Bin 0 -> 7616 bytes pkgdown/favicon/apple-touch-icon-180x180.png | Bin 0 -> 9188 bytes pkgdown/favicon/apple-touch-icon-60x60.png | Bin 0 -> 2387 bytes pkgdown/favicon/apple-touch-icon-76x76.png | Bin 0 -> 3058 bytes pkgdown/favicon/apple-touch-icon.png | Bin 0 -> 9188 bytes pkgdown/favicon/favicon-16x16.png | Bin 0 -> 868 bytes pkgdown/favicon/favicon-32x32.png | Bin 0 -> 1475 bytes pkgdown/favicon/favicon.ico | Bin 0 -> 15086 bytes 16 files changed, 192 insertions(+), 1 deletion(-) create mode 100644 logo/.gitignore create mode 100644 logo/htmltools.png create mode 100644 logo/htmltools.svg create mode 100644 logo/htmltools_src.svg create mode 100644 man/figures/logo.png create mode 100644 pkgdown/favicon/apple-touch-icon-120x120.png create mode 100644 pkgdown/favicon/apple-touch-icon-152x152.png create mode 100644 pkgdown/favicon/apple-touch-icon-180x180.png create mode 100644 pkgdown/favicon/apple-touch-icon-60x60.png create mode 100644 pkgdown/favicon/apple-touch-icon-76x76.png create mode 100644 pkgdown/favicon/apple-touch-icon.png create mode 100644 pkgdown/favicon/favicon-16x16.png create mode 100644 pkgdown/favicon/favicon-32x32.png create mode 100644 pkgdown/favicon/favicon.ico diff --git a/.Rbuildignore b/.Rbuildignore index 0a838f19..168ca9b8 100644 --- a/.Rbuildignore +++ b/.Rbuildignore @@ -11,3 +11,4 @@ ^docs$ ^pkgdown$ ^vignettes$ +^logo$ diff --git a/README.md b/README.md index cbaea8dc..42ba5bf8 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ Downloads](https://cranlogs.r-pkg.org/badges/grand-total/htmltools)](https://www experimental](https://img.shields.io/badge/lifecycle-stable-brightgreen.svg)](https://www.tidyverse.org/lifecycle/#stable) -# htmltools +# htmltools Tools for creating, manipulating, and writing HTML from R. diff --git a/logo/.gitignore b/logo/.gitignore new file mode 100644 index 00000000..3367afdb --- /dev/null +++ b/logo/.gitignore @@ -0,0 +1 @@ +old diff --git a/logo/htmltools.png b/logo/htmltools.png new file mode 100644 index 0000000000000000000000000000000000000000..3b64ba7fc15859e21a3efab1fdaa2bfe29485ce4 GIT binary patch literal 125055 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%0A$#IRUU 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

;kR0(+eey+ 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 literal 0 HcmV?d00001 diff --git a/logo/htmltools.svg b/logo/htmltools.svg new file mode 100644 index 00000000..a16358e0 --- /dev/null +++ b/logo/htmltools.svg @@ -0,0 +1,157 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/logo/htmltools_src.svg b/logo/htmltools_src.svg new file mode 100644 index 00000000..11ca7fc3 --- /dev/null +++ b/logo/htmltools_src.svg @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + <html + + + tools> + + + diff --git a/man/figures/logo.png b/man/figures/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..03e8c6894e03b12239ef70e77e8772010d167137 GIT binary patch literal 13446 zcmd_RRZv_{^sYTKI0Uyqut0#|?gZE1GPpzVfgv~qhd^)*9^47;5(WZ+kYK^xHMkQ7 zIQ#eiuD**?b*|4Ts@O1lbx(J%w^y&d`{_@b>WaA7l-M8;2v=E2P8+zMf8zEs)j%LWCJ-n%90d9Yd=$I~0(o+SKnE5ekZ1-7 zL=MmC&=Lo3pj)XZ%7LE#`^oPvO94K?@K9Eh$Jj@ANf%u}8<)n3e7LIa# zJxr!^cO`pnDqqbjtkl)lGuDBz;Lo0O+v48Gqf#qnc+g?tDq~_fjb3_a_;qNMb=s+W z+l}@fsu2+>1%oM5epFiuIdR7r;{I4uI}eTXcB-hnS#f=At9{E@VWVI17J9XHFY+?K zvUS;iwe|S=eud58Bks}H5zuR41Qy~+#!dd#4jN!62hpUYF*e#)Y)-|jxI{6Cwu3q~ zvM532Y_j_B(Kt~`!y~Jqx7~=kU0e+FR1jFuTDO=(SCo1A>|FcaM0RBNdN z2w1cP-4N47($}P-SW>v0gDXq{oJtuo32l>Jv!QFb;G5P@L!)296tA-HoB1&@fGup* z)XHCgS)>@B1T!_pN9oo}j0v7VGjr_hW5^d0t9z}f75rmRvdV*q1I+@Big3XwTTRCh zEnhmEjiiRap8fn!mDaI#n@TwJH7Y_vZzu_YEey$+cqeb!L`M#pg+!*G(^PhG^^Lr~ zAZUT`i`&YZ92tQ1d0+)_v`=g~m`ikUbj_5pyGqK(bh^7V4{RU2HbNg|s17JDVe!$e z*$T}h0%&0fW_e-XtW+TDUaRzvZGT(=owrdaA+F)l=~ zM82st3(N}AVyZd~SbH0?A7Q2fbu%e&i4Dus)iyP%ON4r&4b{rGy)Zz*j)EEK6Y3$L zGR98sMzlpz#`4>u;YAcS6eVcl?=>(6Ns-0Lhq_%ZBm7@4NE$-FeH~H@m8cxT<2$mE zH^BvZ@}C>zq|wN?3_|{rjuMx%?Q~Fp6TIP8$#=h1q$(K@8YTBbzBdV-(s_Dm?T}jf z+wwma+0�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 literal 0 HcmV?d00001 diff --git a/pkgdown/favicon/apple-touch-icon-120x120.png b/pkgdown/favicon/apple-touch-icon-120x120.png new file mode 100644 index 0000000000000000000000000000000000000000..4a87476c7cbc0b52c3b0bf2cf07bb50b5f7448b5 GIT binary patch literal 5645 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 ZSmWmm*wt2iO?w64mY%6@jkeR%{{bnas2l(Q literal 0 HcmV?d00001 diff --git a/pkgdown/favicon/apple-touch-icon-152x152.png b/pkgdown/favicon/apple-touch-icon-152x152.png new file mode 100644 index 0000000000000000000000000000000000000000..124e7959c9432ad1ff7d39da4a1cc33b1a811c8c GIT binary patch literal 7616 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 bRX)IPItrh^av~|XUjS4TpDR?$A;SLyb+R~P literal 0 HcmV?d00001 diff --git a/pkgdown/favicon/apple-touch-icon-180x180.png b/pkgdown/favicon/apple-touch-icon-180x180.png new file mode 100644 index 0000000000000000000000000000000000000000..7395999fa61424629aebc3f8f0990eaa938f8649 GIT binary patch literal 9188 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 literal 0 HcmV?d00001 diff --git a/pkgdown/favicon/apple-touch-icon-60x60.png b/pkgdown/favicon/apple-touch-icon-60x60.png new file mode 100644 index 0000000000000000000000000000000000000000..29885e4ceceb34fafe768a6965695c5590591355 GIT binary patch literal 2387 zcmZ`*dpOgJ8~@HWm$el(S*~+jBDdLbYlOv`OD-q3aojIuMTRN&vk7sclu`~gm&oNX za!uHZhRC&u92PMN>2x0EnEI{1e$VfZ-}652`+45a^L*a-ulJJU=0ZZi6kq@VARO%N z&WOS!AF%y2dXlBMI=@cP+c1{!ZVE#GyK--x+fUuZyRNM~aQ0 z;a^QZvn}aQCu$~$M?sVz{YE&|DXIkBa=QI$+A|&>g>(ZRfK@yad^pjp-iRAIRPG7| z-XWC$bbupN<{BV+*H4(1E%H&;tm-nw1la^~@S)PsBP*d|B@U0=q!~m`DyQ4#9d$zj zd>8fa?{_`Z%hO{4f##O^L6TJIzH951X^7w9o+z0QspM4|mW2`)fDwNA@;GbkDC8T+ z5)vu?7--G-x0+rtMlACQ(ri4a$Zc{QQI+ZFi{gxuc9m~|+r$)6f2rAHeOGH4QG#0C z0o;1VZLW)(T%9cM*6GrO_Sn--k_mcocu}j+tI23hF+(NQol-F&MZke7_M=vLzhk0Bj>i9<~pYWAu%=;+wXUk z%iEAkX7yTak;_j;o$l+#ZVF)@<0`20oIEvU?4xh|Ae*{atC_0a@YK#JTMYZfyGTSm4}><>JsL=eK>I;qTkO3S@d# z0?}l-qGY*Ek`;9FT>0lNX!lV9WT!2e`dRqX`)jTC(2LIW&w{&QXmY<)>)A;OrEqPhCCBI^;pZOrKp--QA zbiRmNV&xb!DQ^7kN{dU`$9YxRu3gxC+zneJZ(IAiMZblduiuJ|SZuqFjmr^%hS}0q zY^ml?&=Tl1qwjy^4R$#TUNUd4c%7b3ORr}Ws{#muVb(9m#o=GWY;kbO?Z|#5i|n1X z$v$fVCB6Nd%eV2*izDp9zWaf_MJJeZ>I+(yf@oQ@1@J|2lUE5QNw$B7rUN^LZw79E+$JNEgxG9MR2+s0jwvsZHJ{8=vCds- zULC)z#%v7?@5?G)*^3f5=m=jvZ%m0jwjA*=Xxh{*l(ZWrUSy0gYt7xVwvf8?9r0@W z)U-lUdL8B}H0h}nGgbC0cW4YZn03DG&~pG`*^+PT8kQecfY=XF(f_dV;nqg;M%;e2 zH{H)#0TMB4WDK`Y5&SG*SrKF3Oy?MO-w9f{fNHloIqAPpzO4Ic{AA&C)hQ(Vz3J;6 zD$D2kyQa6-FD~dj?@j)4*)ngtq_IT)ttYx`DMooq44c}qbxLkA=3$hfz$Wlh)9vN_Dl7_1fWaD!ar(M0hy}7 z?{u$V-frn+WNh3 z!H#X={#+(JlQD1|vXzwZ&$_go8*x6za^!AaP3FMsiGcc=O26=ir<94+w#wM}O7{dc z4Sxed=pPdSXZ?;Kd9hTE+$Tm)!jvu2=TM!VGP?DNvG3d;FOtetgXV@S*QL?cag-;W zUX@e`O=+VSX7YxZn?vXt!biR!tc6I+$6QxHjx|+o$*Y0nqmh`LdYVvWdAH1|~rd-}rQ_VphRg(i(iriBI5!ttT8;UWNV zSe%hQ)Vd=JaX;ft%yh9>JQiE?m-NT~5ky6YQO+j*f5BUNwWmnno#a8Loe92- zp~gm^r9_5fXo=Kt424Py1%QN-rN7~lF<#o*FI|U*9hT1kVD$qAp$CxaNT&i67>Ti? x8~28R)tfyqLvLRX^bPkZ^+^oT?ModEfHip;+TSNl?uc3d4n!BbhLb_*{{a_-JZ1m@ literal 0 HcmV?d00001 diff --git a/pkgdown/favicon/apple-touch-icon-76x76.png b/pkgdown/favicon/apple-touch-icon-76x76.png new file mode 100644 index 0000000000000000000000000000000000000000..a028cfe8f6a15a5a5ed49c29345abefbc481367c GIT binary patch literal 3058 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 NSerW#9-ESr{sY6=Z|492 literal 0 HcmV?d00001 diff --git a/pkgdown/favicon/apple-touch-icon.png b/pkgdown/favicon/apple-touch-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..f7c00258491f2be217445ec4b234d8c86e613003 GIT binary patch literal 9188 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 literal 0 HcmV?d00001 diff --git a/pkgdown/favicon/favicon.ico b/pkgdown/favicon/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..8309e7b2e506cb63a20644ede22c7ab00c92b4eb GIT binary patch literal 15086 zcmeHO4R96J6<$QEPCEss<2ZH3lBvmi3C4h^6$(;yC^V1+h)5s=34sVA|3V5B5()w) z222!$0`f0^!r&wn1qltLilw%VSnE$)i^+St?-5#(y!^Z*nA7j{s zWLYfs4Q?U~pKh?Us%nH_-cs#gz5}*9c%UaLp2D zC(C%5a9jec2JQ=VjE?FLZubC7fqw(cS>+x+{W@6_%g8cL{ubAYffV3}9mT`!eZl4X zKmx$Fas$w~>--~Tlv6}%1*v)@ILR{B?zY6JHZ=lmzh`#5V6}&h_r~}`fnz{5@Evb) z4BHX>SqD{*Y5ybZ1P58-Y}cX3HuQ@Oi!JRHEMETH2V;f;8-c4pbKc^b5B{niGs`JF z=^9z$74G{V=&>B~$Nr2RNE?jtBVZoz1<(kz%$5ihX<{d?U_YgJ*F9_a6@6OAy!}?r{t7C~yF90siq8 z?fm5L@r`HytNBK(tqgkPVmwQGeFJ^_0qX!6FvnZ8H_6}2H_#X3eg!N7?)LXdplx3u z6}SwvZR3KgFWDac!Z%W2Q;wQv)9h+vxwCcKqOMeV>Ci;re?=v@vTx%sX_9jUJg~ zqXEy@DE)PXV&~dvd5%Kk=G!TIw?a#@Wm>aUq0w{fl(AWp>5z*uWty?ZLD4HJ>G1hF zO3JLFE%|PG=fiqRSYJg+SyhzxQ9b3HaMSoTF3LVuOY`5TrlS|?Xx*V&8oIcW2Fu4<%e$qe}K5bBWzV~rG?ReKsf50{7 zpAVgi{@y_IaJ=tq9WB^eO^+?8RG)yk6a_(0do0 z>YSgu=%mSMb}IPLNw4fu=!1`)l=Y@Uc?C{-fq(YV8kPTgoqs0tzuZ7YP52iS z*VDF>Zu;cw8IX!|KQMXz+3o_}7)u@3wk9-3&QCsOQ`yI-OBMRwIEh9uck zKNtwV;I`pobqRlFpGkw=E!yayN$Xs+a9fSXhMW2?`|{{jE;>+9M-Ri^`S<>?^(gr9 zh~+NT_qmN(`)5u9tOq0NX&Xh1^S&kd;c_jkBIk)kj$BVRlI}5b-~+n zZo+<=^5JvDFg-f?`rYdq^ldZ#V0b^ar6cQL#(%xInBi?~+eN-#?bfA-zvuhhV?QQ* zSNK4W{g|_Ir%%I9)|qs+juSkIH;A3#{r+(mXXg3|`GW7#SJz!HU*Kfph?fMOGJ#4pOj=kz< zaNaEEW9-x3W1_|kB1s~0_tRu~A59ka)+GN1+>oRUmTpPv$MT9K-Oh3~>Wd&7nE)i3 zNzyMQshnjWNxH)F_Ua@WC zcLR%oe*#*(M(pFXdXGl%xs16;1rJzgaQ-mXoxsxo&$V|Q6ZDVJ>zH^S9yTSN z4IZPwqut*DjKy_GgMlM}@9$`n@kKn0bIPbiDn~8SPH>0F zl?T@nP2pN1xdiuE2@dzR^+eF_E?^Gu51{QDiPpv!F*vSwm~{19)B^nl&txiiG^+!` zSiDD!2F?NXK+8HCfAeR2ori84Zq)MRo1UAa`U2*m^ zn1?$A2aLxK*gS^Mj7gf?zzpkXX)h^@^D!=2`hW&BAWx`@RB_p4=Y?DIEURCJjor;=6 z%7k2nT;ir1Z#zNebv*Ho!2DqciuP-aNea-@Mmr_x@WA({Sj3!w7mvfkO=( zcDRBFOQ9t74g`D;zLzI_!DH}PJSIXF9H-#0L>CTfZDiQUEBt=G7x)xt zzDM)XU+jTVYkw*1SKlz_`+zS^_2>1%l(J&<=lz-5-)sFX*5qULdK$wTT&%^7{n7W! zM!ggm)oR}*J8Ed{!CJNd6x8NLK6|bB{*m7N9~f_={-_t?wKnS$IYFie zpVRgcJa1ehrp_OZtxfcAY5usLQS>oNgZ_a&zkuw=V2ACpU(sJk%me!m|6eat{v-Oc mMBnEA`?zoaZOo7)cR$2`5m_sQhp)yLtu=5PZp(eR@Baa;8=^D- literal 0 HcmV?d00001 From 692f348d66d334ba384d2e4c46d4a90231e579bc Mon Sep 17 00:00:00 2001 From: Barret Schloerke Date: Thu, 6 May 2021 16:40:00 -0400 Subject: [PATCH 18/31] Remove "All Vignettes" header from Articles page --- pkgdown/_pkgdown.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pkgdown/_pkgdown.yml b/pkgdown/_pkgdown.yml index 0fd76035..8e67b1bc 100644 --- a/pkgdown/_pkgdown.yml +++ b/pkgdown/_pkgdown.yml @@ -36,6 +36,12 @@ navbar: icon: fab fa-github fa-lg href: https://github.com/rstudio/htmltools +articles: +- title: ~ + desc: ~ + contents: + - '`htmltools`' + - '`tagQuery`' reference: From cf285c4ce24f1e76372e516a4bd914b2f8df2d53 Mon Sep 17 00:00:00 2001 From: Barret Schloerke Date: Thu, 6 May 2021 16:44:18 -0400 Subject: [PATCH 19/31] Comment development mode and set to auto --- pkgdown/_pkgdown.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgdown/_pkgdown.yml b/pkgdown/_pkgdown.yml index 8e67b1bc..2cc38e1d 100644 --- a/pkgdown/_pkgdown.yml +++ b/pkgdown/_pkgdown.yml @@ -1,7 +1,7 @@ url: https://rstudio.github.io/htmltools/ -development: - mode: release +# development: +# mode: auto toc: depth: 2 From 467284603f648c954eb44e514b307dc56ec3b1b6 Mon Sep 17 00:00:00 2001 From: Barret Schloerke Date: Mon, 10 May 2021 17:39:30 -0400 Subject: [PATCH 20/31] Do not flatten tag attribs here --- R/tag_query.R | 3 --- 1 file changed, 3 deletions(-) diff --git a/R/tag_query.R b/R/tag_query.R index 533d75b9..9b3aa067 100644 --- a/R/tag_query.R +++ b/R/tag_query.R @@ -263,9 +263,6 @@ asTagEnv_ <- function(x, parent = NULL) { x$envKey <- obj_address(x) } - # Make sure all attribs are unique - x$attribs <- flattenTagAttribs(x$attribs) - # Recurse through children if (length(x$children) != 0) { # Possible optimization... name the children tags to the formatted values. From 2b0a0a886a871979194af8617515e88ecbb7610d Mon Sep 17 00:00:00 2001 From: Barret Schloerke Date: Mon, 10 May 2021 17:39:45 -0400 Subject: [PATCH 21/31] Add performance section draft --- vignettes/tagQuery.Rmd | 136 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 128 insertions(+), 8 deletions(-) diff --git a/vignettes/tagQuery.Rmd b/vignettes/tagQuery.Rmd index 364b98fc..7e3ad56f 100644 --- a/vignettes/tagQuery.Rmd +++ b/vignettes/tagQuery.Rmd @@ -14,11 +14,11 @@ opts_chunk$set( ) # Show R console output instead of embedding the HTML registerS3method( - "knit_print", "shiny.tag", + "knit_print", "shiny.tag", getS3method("print", "shiny.tag") ) registerS3method( - "knit_print", "shiny.tag.list", + "knit_print", "shiny.tag.list", getS3method("print", "shiny.tag.list") ) ``` @@ -149,7 +149,7 @@ tagQ$ allTags() ``` -The [mutable](https://en.wikipedia.org/wiki/Immutable_object) behavior of modifier methods not only allows us to modify child tags without losing a reference to the root tag, but it also makes modifications more [performant](#performance) than they'd otherwise be. +The [mutable](https://en.wikipedia.org/wiki/Immutable_object) behavior of modifier methods not only allows us to modify child tags without losing a reference to the root tag, but it also makes modifications more [performant](#mutate-in-place) than they'd otherwise be. ### Attributes {#modify-attrs} @@ -190,7 +190,7 @@ tagQ$ allTags() ``` -If you'd like to replace all the children, then you can first call `$empty()` before `$append()`. If you like to just remove particular child elements, then you should call `$children()` + the `$remove()` sibling method. +If you'd like to replace all the children, then you can first call `$empty()` before `$append()`. If you like to just remove particular child tags, then you should call `$children()` + the `$remove()` sibling method. ### Siblings {#modify-sibling} @@ -211,7 +211,7 @@ tagQ$ ## Replace {#replace} -As with `tagQuery()`'s modifier methods, its replace methods also modify their input. They also empty selected tags, so you may want to `$resetSelection()` if you want to make more queries or modifications after-the-fact. +As with `tagQuery()`'s modifier methods, its replace methods also modify their input. They also empty selected tags, so you may want to `$resetSelection()` if you want to make more queries or modifications after-the-fact. Use `$replaceWith()` to replace selected tags with some other content. @@ -246,8 +246,128 @@ tagQ$find("span")$empty()$allTags() ``` - + +`tagQuery()` is very fast, given the dynamic usage. Let's look at performance to add a set the main button class for a `shiny` action button: + +```{r} +# Show the original tags +shiny::actionButton("myID", "myLabel") +# Benchmark operations +microbenchmark::microbenchmark( + create_button = + shiny::actionButton("myID", "myLabel"), + and_call_tagQuery = + tagQuery(shiny::actionButton("myID", "myLabel"))$ + allTags(), + and_add_class = + tagQuery(shiny::actionButton("myID", "myLabel"))$ + addClass("btn-primary")$ + allTags(), + and_remove_class = + tagQuery(shiny::actionButton("myID", "myLabel"))$ + addClass("btn-primary")$ + removeClass("btn-default")$ + allTags() +) +# Final output +tagQuery(shiny::actionButton("myID", "myLabel"))$ + addClass("btn-primary")$ + removeClass("btn-default")$ + allTags() +``` + +While there is some overhead to turn a standard `tag` object into a `tagQuery()` object, all operations finish within 1 millisecond. + +While adding the `btn-primary` class could be accomplished using `tagAddAttributes(myButton, class="btn-primary")`, removing a class is currently not possible without reaching into the attributes of the button object. + +Let's look at a more complicated example: `shiny::dateRangeInput()`. For this example, we are going to update the `title` attribute for each date input say `"Start Date"` or `"End Date"`. + +```{r} +# Show the original tags +startDate <- "2001-01-01"; endDate <- "2010-12-31" +(dateRange <- shiny::dateRangeInput( + "daterange", "Date range:", + start = startDate, end = endDate +)) +# Benchmark operations +microbenchmark::microbenchmark( + call_tagQuery = + tagQuery(dateRange)$allTags(), + and_find_input = + tagQuery(dateRange)$find("input")$allTags(), + and_remove_title = + tagQuery(dateRange)$find("input")$removeAttrs("title")$allTags(), + and_call_each = + tagQuery(dateRange)$ + find("input")$ + removeAttrs("title")$ + each(function(el, i){ + newTitle <- c("Start Date", "End Date")[i] + tagAppendAttributes(el, title = newTitle) + })$ + allTags() +) +# Final output +dateRangeQ <- tagQuery(dateRange)$ + find("input")$ + removeAttrs("title")$ + each(function(el, i) { + newTitle <- c("Start Date", "End Date")[i] + tagAppendAttributes(el, title = newTitle) + })$ + allTags() +dateRangeQ + +# waldo::compare(dateRange, dateRangeQ) +#> `old$children[[2]]$children[[1]]$attribs$title`: "Date format: yyyy-mm-dd" +#> `new$children[[2]]$children[[1]]$attribs$title`: "Start Date" +#> `old$children[[2]]$children[[3]]$attribs$title`: "Date format: yyyy-mm-dd" +#> `new$children[[2]]$children[[3]]$attribs$title`: "End Date" +``` + + +Updating the `title`s for each of the `` could be done manually. However, this is brittle to developer changes and is difficult to debug. Taking a quick glance at the code below. We can tell the `title` attributes are being updated, but there is no hint as to which tags are being operated on. This would only become more difficult to determine the larger the tag objects grow. + +```{r} +microbenchmark::microbenchmark( + unclear_and_brittle = { + # Unclear and very brittle operations + dateRange$children[[2]]$children[[1]]$attribs$title <- "Start Date" + dateRange$children[[2]]$children[[3]]$attribs$title <- "End Date" + dateRange + } +) +``` + +If you would like to do more complicated alterations to the selected tag, you can send `el` to `tagQuery()` and immediately gain access to all selected element methods. While `tagAppendAttributes()` was sufficient above, `tagQuery(el)$addAttrs(title = newTitle)` could also be used at a slight speed cost (~500 μs). + +```{r} +microbenchmark::microbenchmark( + "$addAttrs()" = { + tagQuery(dateRange)$ + find("input")$ + removeAttrs("title")$ + each(function(el, i) { + newTitle <- c("Start Date", "End Date")[i] + tagQuery(el)$addAttrs(title = newTitle) + })$ + allTags() + } +) +``` + + +At worst, using `tagQuery()` in this example increases the execution time by roughly 3-4 times, from 1.5 milliseconds to 5.5 milliseconds. + +### Mutate in-place {#mutate-in-place} + +The final output of the `dateRangeInput` example above uses multiple steps. Let's break down cost and benefits of each one. + +1. `tagQuery(dateRange)`: ~500 μs. Convert all tag objects into `tagQuery()` objects. The cost advantage is to be able to keep track of all tag objects and be able to alter the `tagQuery()` object in place. This method also unifies the `children` tag structure by flattening all children into a single level. This makes traversing **much** faster. +2. `$find("input")`: ~500μs. Finds **all** matching tags that match the css selector `"input"`. This method updates the selected items to the two `` tags. +3. `$removeAttrs("title")`: ~50μs. Removes the `"title"` attribute from both of the selected `` tags. Since the selected items have already been flagged, this method is fairly quick! This method maintains the selected tags and passes on the `tagQuery()` structure. +4. `$each(fn)`: ~250μs. Given a selected tag, the function calls `tagQuery()` on the selected tag to leverage `tagQuery()`s methods to remove and add attributes on the selected tags. Since a "modify in place" selected tag is provided directly to `tagQuery()`, it is fast to create. However, extra care is taken by `tagQuery()` after calling `$each()` to make sure the internal `tagQuery()` structure is still intact. This is due to `$each()` being able to reach into and alter all parent and children tags of the selected tags. This extra bit of housekeeping has a necessary, but small overhead. + +If `htmltools` was to not leverage "modify in place" data structures, `htmltools` would have to constantly search every time a selected element was to be modified. Using the same example as above, finding the `` tags will still require ~500 μs to prep the tag objects. It would also require ~500 μs to find each of the selected tags. Once the selected items are found, removing the attributes would be fast. But the real speed improvement is utilized when handing off to another selected element method. If the selected element information could not be handed off, `htmltools` would need to prep (~500 μs) and find (~500 μs) the selected tags again to be able to have composable functions. This approach would require a full `1000 μs` in addition to any exection done by the `fn` supplied to `$each`, whereas `$each()` currently finished within ~250 μs. Using a standard tag approach would also make finding `$parent()` tags very slow as all of the selected tags would need to be found before returning any ancestor tags. From fe80dca26c1e45ea852bf034500dc2e7d4cfcd1f Mon Sep 17 00:00:00 2001 From: Barret Schloerke Date: Mon, 10 May 2021 17:45:31 -0400 Subject: [PATCH 22/31] Install `microbenchmark` --- .github/workflows/pkgdown.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/pkgdown.yaml b/.github/workflows/pkgdown.yaml index 54e73c8a..11ec3e33 100644 --- a/.github/workflows/pkgdown.yaml +++ b/.github/workflows/pkgdown.yaml @@ -50,12 +50,13 @@ jobs: run: | pak::local_system_requirements(execute = TRUE) pak::pkg_system_requirements("pkgdown", execute = TRUE) + pak::pkg_system_requirements("microbenchmark", execute = TRUE) - name: Install dependencies shell: Rscript {0} run: | pak::local_install_dev_deps(upgrade = TRUE) - pak::pkg_install("pkgdown") + pak::pkg_install(c("pkgdown", "microbenchmark"), upgrade = TRUE) - name: Install package run: R CMD INSTALL . From 29e75ca0c0c52d1588b436eec93255d0f9471ac5 Mon Sep 17 00:00:00 2001 From: Carson Date: Tue, 11 May 2021 11:01:18 -0500 Subject: [PATCH 23/31] Add .cssSelector arg to 'top-level' append functions; add docs --- NAMESPACE | 1 + R/tag_query.R | 2 +- R/tags.R | 75 ++++++++++++++++++--- man/tag.Rd | 15 +++-- man/tagQuery.Rd | 5 +- vignettes/tagQuery.Rmd | 143 ++++++----------------------------------- 6 files changed, 102 insertions(+), 139 deletions(-) diff --git a/NAMESPACE b/NAMESPACE index f8d90f92..65f3724a 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -97,6 +97,7 @@ export(tagAppendChildren) export(tagFunction) export(tagGetAttribute) export(tagHasAttribute) +export(tagInsertChildren) export(tagList) export(tagQuery) export(tagSetChildren) diff --git a/R/tag_query.R b/R/tag_query.R index 9b3aa067..caefc096 100644 --- a/R/tag_query.R +++ b/R/tag_query.R @@ -709,7 +709,7 @@ tagQuery_ <- function( #' ## Replace methods #' - #' #' * `$replaceWith(...)`: Replace all selected tags with `...` in the + #' * `$replaceWith(...)`: Replace all selected tags with `...` in the #' root tag and clear the selection. replaceWith = function(...) { tagQuerySiblingReplaceWith(selected_, ...) diff --git a/R/tags.R b/R/tags.R index 2db37698..1935152f 100644 --- a/R/tags.R +++ b/R/tags.R @@ -393,8 +393,14 @@ tagAddRenderHook <- function(tag, func, replace = FALSE) { #' @rdname tag #' @export -tagAppendAttributes <- function(tag, ...) { +tagAppendAttributes <- function(tag, ..., .cssSelector = NULL) { throw_if_tag_function(tag) + if (!is.null(.cssSelector)) { + return( + tagQuery(tag)$find(.cssSelector)$addAttrs(...)$allTags() + ) + } + newAttribs <- dropNullsOrEmpty(dots_list(...)) if (any(!nzchar(names2(newAttribs)))) { stop( @@ -435,33 +441,84 @@ tagGetAttribute <- function(tag, attr) { } #' @rdname tag +#' @param .cssSelector A character string containing a [CSS selector](https://developer.mozilla.org/en-US/docs/Learn/CSS/Building_blocks/Selectors) for targeting particular (inner) tags of interest. At the moment, only a combination of [type](https://www.w3.org/TR/CSS22/selector.html#type-selectors) (e.g, `div`), [class](https://www.w3.org/TR/CSS22/selector.html#class-html) (e.g., `.my-class`), [id](https://www.w3.org/TR/CSS22/selector.html#id-selectors) (e.g., `#myID`), and [universal](https://www.w3.org/TR/CSS22/selector.html#universal-selector) (`*`) selectors within a given [simple selector](https://www.w3.org/TR/CSS22/selector.html#selector-syntax) is supported. #' @export -tagAppendChild <- function(tag, child) { +tagAppendChild <- function(tag, child, .cssSelector = NULL) { throw_if_tag_function(tag) + + if (!is.null(.cssSelector)) { + return( + tagAppendChildren(tag, child, .cssSelector = .cssSelector) + ) + } + tag$children[[length(tag$children)+1]] <- child tag } + + #' @rdname tag #' @export -tagAppendChildren <- function(tag, ..., list = NULL) { +tagAppendChildren <- function(tag, ..., .cssSelector = NULL, list = NULL) { throw_if_tag_function(tag) - tag$children <- unname(c(tag$children, c(dots_list(...), list))) + + children <- unname(c(dots_list(...), list)) + + if (!is.null(.cssSelector)) { + return( + tagQuery(tag)$ + find(.cssSelector)$ + append(!!!children)$ + allTags() + ) + } + + tag$children <- unname(c(tag$children, children)) tag } #' @rdname tag #' @export -tagSetChildren <- function(tag, ..., list = NULL) { +tagSetChildren <- function(tag, ..., .cssSelector = NULL, list = NULL) { throw_if_tag_function(tag) - tag$children <- unname(c(dots_list(...), list)) + + children <- unname(c(dots_list(...), list)) + + if (!is.null(.cssSelector)) { + return( + tagQuery(tag)$ + find(.cssSelector)$ + empty()$ + append(!!!children)$ + allTags() + ) + } + + tag$children <- children tag } -# (Please make an issue if you'd like this method to be exported) -tagInsertChildren <- function(tag, after, ..., list = NULL) { +#' @rdname tag +#' @param after an integer value (i.e., subscript) referring to the child position to append after. +#' @export +tagInsertChildren <- function(tag, after, ..., .cssSelector = NULL, list = NULL) { throw_if_tag_function(tag) - tag$children <- unname(append(tag$children, c(dots_list(...), list), after)) + + children <- unname(c(dots_list(...), list)) + + if (!is.null(.cssSelector)) { + return( + tagQuery(tag)$ + find(.cssSelector)$ + each(function(x, i) { + tagInsertChildren(x, after = after, !!!children) + })$ + allTags() + ) + } + + tag$children <- unname(append(tag$children, children, after)) tag } diff --git a/man/tag.Rd b/man/tag.Rd index 621bd257..c6fa69d1 100644 --- a/man/tag.Rd +++ b/man/tag.Rd @@ -9,21 +9,24 @@ \alias{tagAppendChild} \alias{tagAppendChildren} \alias{tagSetChildren} +\alias{tagInsertChildren} \title{HTML Tag Object} \usage{ tagList(...) -tagAppendAttributes(tag, ...) +tagAppendAttributes(tag, ..., .cssSelector = NULL) tagHasAttribute(tag, attr) tagGetAttribute(tag, attr) -tagAppendChild(tag, child) +tagAppendChild(tag, child, .cssSelector = NULL) -tagAppendChildren(tag, ..., list = NULL) +tagAppendChildren(tag, ..., .cssSelector = NULL, list = NULL) -tagSetChildren(tag, ..., list = NULL) +tagSetChildren(tag, ..., .cssSelector = NULL, list = NULL) + +tagInsertChildren(tag, after, ..., .cssSelector = NULL, list = NULL) tag(`_tag_name`, varArgs, .noWS = NULL, .renderHook = NULL) } @@ -32,6 +35,8 @@ tag(`_tag_name`, varArgs, .noWS = NULL, .renderHook = NULL) \item{tag}{A tag to append child elements to.} +\item{.cssSelector}{A character string containing a \href{https://developer.mozilla.org/en-US/docs/Learn/CSS/Building_blocks/Selectors}{CSS selector} for targeting particular (inner) tags of interest. At the moment, only a combination of \href{https://www.w3.org/TR/CSS22/selector.html#type-selectors}{type} (e.g, \code{div}), \href{https://www.w3.org/TR/CSS22/selector.html#class-html}{class} (e.g., \code{.my-class}), \href{https://www.w3.org/TR/CSS22/selector.html#id-selectors}{id} (e.g., \verb{#myID}), and \href{https://www.w3.org/TR/CSS22/selector.html#universal-selector}{universal} (\code{*}) selectors within a given \href{https://www.w3.org/TR/CSS22/selector.html#selector-syntax}{simple selector} is supported.} + \item{attr}{The name of an attribute.} \item{child}{A child element to append to a parent tag.} @@ -39,6 +44,8 @@ tag(`_tag_name`, varArgs, .noWS = NULL, .renderHook = NULL) \item{list}{An optional list of elements. Can be used with or instead of the \code{...} items.} +\item{after}{an integer value (i.e., subscript) referring to the child position to append after.} + \item{_tag_name}{HTML tag name} \item{varArgs}{List of attributes and children of the element. Named list diff --git a/man/tagQuery.Rd b/man/tagQuery.Rd index 2918633b..72d8e35a 100644 --- a/man/tagQuery.Rd +++ b/man/tagQuery.Rd @@ -131,10 +131,9 @@ object. } \subsection{Replace methods}{ - -#' * \verb{$replaceWith(...)}: Replace all selected tags with \code{...} in the -root tag and clear the selection. \itemize{ +\item \verb{$replaceWith(...)}: Replace all selected tags with \code{...} in the +root tag and clear the selection. \item \verb{$remove(...)}: Remove all selected tags from the root tag and clear the current selection. \item \verb{$empty()}: Remove any children of each selected tag. Use this diff --git a/vignettes/tagQuery.Rmd b/vignettes/tagQuery.Rmd index 7e3ad56f..45ba71e5 100644 --- a/vignettes/tagQuery.Rmd +++ b/vignettes/tagQuery.Rmd @@ -23,7 +23,9 @@ registerS3method( ) ``` -`tagQuery()` provides a [jQuery](https://jquery.com) inspired interface to query and modify HTML fragments in R. To start, pass either a `tag()` (e.g., `div()`) or `tagList()` object to `tagQuery()`: +`tagQuery()` provides a [jQuery](https://jquery.com) inspired interface to query and modify HTML fragments in R. Learning how to use `tagQuery()` directly gives you a very fast and flexible way to extract and modify HTML tags. Some other `{htmltools}` functions like `tagAppendAttributes()` actually use `tagQuery()` when `.cssSelector` is supplied, but only offer a subset of the functionality the `tagQuery()` API provides, and can be significantly slower when multiple modifications are needed (for details, see [performance](#performance)). + +To create a `tagQuery()` object, pass it either a `tag()` (e.g., `div()`) or `tagList()`: ```{r} library(htmltools) @@ -99,8 +101,6 @@ tagQ$find("a.foo")$parent()$selectedTags() tagQ$find("a.foo")$parents()$selectedTags() ``` - - ### Filter The `$filter()` method can be used to subset selected tags using an R function or CSS selector. When combined with the universal selector (`*`), `$filter()` is particularly useful as a workaround for the fact that `tagQuery()` doesn't fully support the entire CSS selector specification. For example, here's a workaround for `tagQuery()`'s current lack of support for [attribute selectors](https://www.w3.org/TR/CSS22/selector.html#attribute-selectors): @@ -149,7 +149,7 @@ tagQ$ allTags() ``` -The [mutable](https://en.wikipedia.org/wiki/Immutable_object) behavior of modifier methods not only allows us to modify child tags without losing a reference to the root tag, but it also makes modifications more [performant](#mutate-in-place) than they'd otherwise be. +The [mutable](https://en.wikipedia.org/wiki/Immutable_object) behavior of modifier methods not only allows us to modify child tags without losing a reference to the root tag, but it also makes modifications more [performant](#performance) than they'd otherwise be. ### Attributes {#modify-attrs} @@ -161,6 +161,7 @@ Use `$addAttrs()` to add and `$removeAttrs()` to remove any HTML attribute from ```{r} tagQ <- tagQuery(html) +# Equivalent to tagAppendAttributes(html, .cssSelector = "span", "data-bar" = "foo") tagQ$ find("span")$ addAttrs("data-bar" = "foo")$ @@ -183,6 +184,7 @@ Use `$prepend()` to insert content before the children of each selected tag and ```{r} tagQ <- tagQuery(html) +# Equivalent to html %>% tagInsertChildren(.cssSelector = "p", after = 0, span()) %>% tagAppendChildren(.cssSelector = "p", tags$table()) tagQ$ find("p")$ prepend(span())$ @@ -248,126 +250,23 @@ tagQ$find("span")$empty()$allTags() ## Performance {#performance} +One main reason why `tagQuery()` is fast is that it converts the underlying `tag()` list structure into a environment (i.e., [reference object](https://en.wikipedia.org/wiki/Reference_(computer_science))). As a result, `tagQuery()` is able to keep a reference to selected tags and modify them without having to re-find tags for each modification. This is why, even if you can achieve multiple modifications via multiple calls to `tagAppendAttributes()`, `tagInsertChildren()`, etc., you should consider using `tagQuery()` directly instead. -`tagQuery()` is very fast, given the dynamic usage. Let's look at performance to add a set the main button class for a `shiny` action button: +For a basic example, since `tagQuery()` can prepend and append in one shot, it's twice as fast as using `tagInsertChildren()` + then `tagAppendChildren()` (with the same `.cssSelector`). Internally, the latter approach calls `tagQuery(html)$find()` twice, which is why it's 2 times slower. ```{r} -# Show the original tags -shiny::actionButton("myID", "myLabel") -# Benchmark operations -microbenchmark::microbenchmark( - create_button = - shiny::actionButton("myID", "myLabel"), - and_call_tagQuery = - tagQuery(shiny::actionButton("myID", "myLabel"))$ - allTags(), - and_add_class = - tagQuery(shiny::actionButton("myID", "myLabel"))$ - addClass("btn-primary")$ - allTags(), - and_remove_class = - tagQuery(shiny::actionButton("myID", "myLabel"))$ - addClass("btn-primary")$ - removeClass("btn-default")$ - allTags() +html <- div(p(a())) +bench::mark( + tagQuery = tagQuery(html)$ + find("p")$ + prepend(span())$ + append(tags$table())$ + allTags(), + tagAppend = html %>% + tagInsertChildren(.cssSelector = "p", after = 0, span()) %>% + tagAppendChildren(.cssSelector = "p", tags$table()), + check = FALSE, + time_unit = "us" ) -# Final output -tagQuery(shiny::actionButton("myID", "myLabel"))$ - addClass("btn-primary")$ - removeClass("btn-default")$ - allTags() ``` - -While there is some overhead to turn a standard `tag` object into a `tagQuery()` object, all operations finish within 1 millisecond. - -While adding the `btn-primary` class could be accomplished using `tagAddAttributes(myButton, class="btn-primary")`, removing a class is currently not possible without reaching into the attributes of the button object. - -Let's look at a more complicated example: `shiny::dateRangeInput()`. For this example, we are going to update the `title` attribute for each date input say `"Start Date"` or `"End Date"`. - -```{r} -# Show the original tags -startDate <- "2001-01-01"; endDate <- "2010-12-31" -(dateRange <- shiny::dateRangeInput( - "daterange", "Date range:", - start = startDate, end = endDate -)) -# Benchmark operations -microbenchmark::microbenchmark( - call_tagQuery = - tagQuery(dateRange)$allTags(), - and_find_input = - tagQuery(dateRange)$find("input")$allTags(), - and_remove_title = - tagQuery(dateRange)$find("input")$removeAttrs("title")$allTags(), - and_call_each = - tagQuery(dateRange)$ - find("input")$ - removeAttrs("title")$ - each(function(el, i){ - newTitle <- c("Start Date", "End Date")[i] - tagAppendAttributes(el, title = newTitle) - })$ - allTags() -) -# Final output -dateRangeQ <- tagQuery(dateRange)$ - find("input")$ - removeAttrs("title")$ - each(function(el, i) { - newTitle <- c("Start Date", "End Date")[i] - tagAppendAttributes(el, title = newTitle) - })$ - allTags() -dateRangeQ - -# waldo::compare(dateRange, dateRangeQ) -#> `old$children[[2]]$children[[1]]$attribs$title`: "Date format: yyyy-mm-dd" -#> `new$children[[2]]$children[[1]]$attribs$title`: "Start Date" -#> `old$children[[2]]$children[[3]]$attribs$title`: "Date format: yyyy-mm-dd" -#> `new$children[[2]]$children[[3]]$attribs$title`: "End Date" -``` - - -Updating the `title`s for each of the `` could be done manually. However, this is brittle to developer changes and is difficult to debug. Taking a quick glance at the code below. We can tell the `title` attributes are being updated, but there is no hint as to which tags are being operated on. This would only become more difficult to determine the larger the tag objects grow. - -```{r} -microbenchmark::microbenchmark( - unclear_and_brittle = { - # Unclear and very brittle operations - dateRange$children[[2]]$children[[1]]$attribs$title <- "Start Date" - dateRange$children[[2]]$children[[3]]$attribs$title <- "End Date" - dateRange - } -) -``` - -If you would like to do more complicated alterations to the selected tag, you can send `el` to `tagQuery()` and immediately gain access to all selected element methods. While `tagAppendAttributes()` was sufficient above, `tagQuery(el)$addAttrs(title = newTitle)` could also be used at a slight speed cost (~500 μs). - -```{r} -microbenchmark::microbenchmark( - "$addAttrs()" = { - tagQuery(dateRange)$ - find("input")$ - removeAttrs("title")$ - each(function(el, i) { - newTitle <- c("Start Date", "End Date")[i] - tagQuery(el)$addAttrs(title = newTitle) - })$ - allTags() - } -) -``` - - -At worst, using `tagQuery()` in this example increases the execution time by roughly 3-4 times, from 1.5 milliseconds to 5.5 milliseconds. - -### Mutate in-place {#mutate-in-place} - -The final output of the `dateRangeInput` example above uses multiple steps. Let's break down cost and benefits of each one. - -1. `tagQuery(dateRange)`: ~500 μs. Convert all tag objects into `tagQuery()` objects. The cost advantage is to be able to keep track of all tag objects and be able to alter the `tagQuery()` object in place. This method also unifies the `children` tag structure by flattening all children into a single level. This makes traversing **much** faster. -2. `$find("input")`: ~500μs. Finds **all** matching tags that match the css selector `"input"`. This method updates the selected items to the two `` tags. -3. `$removeAttrs("title")`: ~50μs. Removes the `"title"` attribute from both of the selected `` tags. Since the selected items have already been flagged, this method is fairly quick! This method maintains the selected tags and passes on the `tagQuery()` structure. -4. `$each(fn)`: ~250μs. Given a selected tag, the function calls `tagQuery()` on the selected tag to leverage `tagQuery()`s methods to remove and add attributes on the selected tags. Since a "modify in place" selected tag is provided directly to `tagQuery()`, it is fast to create. However, extra care is taken by `tagQuery()` after calling `$each()` to make sure the internal `tagQuery()` structure is still intact. This is due to `$each()` being able to reach into and alter all parent and children tags of the selected tags. This extra bit of housekeeping has a necessary, but small overhead. - -If `htmltools` was to not leverage "modify in place" data structures, `htmltools` would have to constantly search every time a selected element was to be modified. Using the same example as above, finding the `` tags will still require ~500 μs to prep the tag objects. It would also require ~500 μs to find each of the selected tags. Once the selected items are found, removing the attributes would be fast. But the real speed improvement is utilized when handing off to another selected element method. If the selected element information could not be handed off, `htmltools` would need to prep (~500 μs) and find (~500 μs) the selected tags again to be able to have composable functions. This approach would require a full `1000 μs` in addition to any exection done by the `fn` supplied to `$each`, whereas `$each()` currently finished within ~250 μs. Using a standard tag approach would also make finding `$parent()` tags very slow as all of the selected tags would need to be found before returning any ancestor tags. From 2c95a283d2bec5f2389006ab6f2228dd3443730e Mon Sep 17 00:00:00 2001 From: Carson Date: Tue, 11 May 2021 16:41:18 -0500 Subject: [PATCH 24/31] Improve tag creation and modifier Rd docs --- R/tags.R | 464 ++++++++++++++++++------------------- man/HTML.Rd | 2 +- man/builder.Rd | 88 +++---- man/renderTags.Rd | 19 +- man/tag.Rd | 107 --------- man/tagAppendAttributes.Rd | 40 ++++ man/tagAppendChild.Rd | 50 ++++ man/tagList.Rd | 22 ++ 8 files changed, 386 insertions(+), 406 deletions(-) delete mode 100644 man/tag.Rd create mode 100644 man/tagAppendAttributes.Rd create mode 100644 man/tagAppendChild.Rd create mode 100644 man/tagList.Rd diff --git a/R/tags.R b/R/tags.R index 1935152f..7ab59722 100644 --- a/R/tags.R +++ b/R/tags.R @@ -258,13 +258,21 @@ normalizeText <- function(text) { text else htmlEscape(text, attribute=FALSE) - } -#' @name tag -#' @rdname tag -#' @import rlang +#' Create a list of tags +#' +#' Create a `list()` of [tag]s with methods for [print()], [as.character()], +#' etc. +#' +#' @param ... A collection of [tag]s. #' @export +#' @examples +#' tagList( +#' h1("Title"), +#' h2("Header text"), +#' p("Text here") +#' ) tagList <- function(...) { lst <- dots_list(...) class(lst) <- c("shiny.tag.list", "list") @@ -391,13 +399,34 @@ tagAddRenderHook <- function(tag, func, replace = FALSE) { } -#' @rdname tag +#' Append tag attributes +#' +#' Append (`tagAppendAttributes()`), check existence (`tagHasAttribute()`), +#' and obtain the value (`tagGetAttribute()`) of HTML attribute(s). +#' #' @export +#' @param tag a [tag] object. +#' @param ... a collection of attributes. +#' @param .cssSelector A character string containing a [CSS selector](https://developer.mozilla.org/en-US/docs/Learn/CSS/Building_blocks/Selectors) for targeting particular (inner) tags of interest. At the moment, only a combination of [type](https://www.w3.org/TR/CSS22/selector.html#type-selectors) (e.g, `div`), [class](https://www.w3.org/TR/CSS22/selector.html#class-html) (e.g., `.my-class`), [id](https://www.w3.org/TR/CSS22/selector.html#id-selectors) (e.g., `#myID`), and [universal](https://www.w3.org/TR/CSS22/selector.html#universal-selector) (`*`) selectors within a given [simple selector](https://www.w3.org/TR/CSS22/selector.html#selector-syntax) is supported. +#' @seealso [tagAppendChildren()] +#' @examples +#' +#' html <- div(a()) +#' tagAppendAttributes(html, class = "foo") +#' tagAppendAttributes(html, .cssSelector = "a", class = "bar") +#' +#' tagHasAttribute(div(foo = "bar"), "foo") +#' tagGetAttribute(div(foo = "bar"), "foo") +#' tagAppendAttributes <- function(tag, ..., .cssSelector = NULL) { throw_if_tag_function(tag) + if (!is.null(.cssSelector)) { return( - tagQuery(tag)$find(.cssSelector)$addAttrs(...)$allTags() + tagQuery(tag)$ + find(.cssSelector)$ + addAttrs(...)$ + allTags() ) } @@ -412,8 +441,8 @@ tagAppendAttributes <- function(tag, ..., .cssSelector = NULL) { tag } +#' @rdname tagAppendAttributes #' @param attr The name of an attribute. -#' @rdname tag #' @export tagHasAttribute <- function(tag, attr) { throw_if_tag_function(tag) @@ -421,7 +450,7 @@ tagHasAttribute <- function(tag, attr) { result } -#' @rdname tag +#' @rdname tagAppendAttributes #' @export tagGetAttribute <- function(tag, attr) { throw_if_tag_function(tag) @@ -440,9 +469,27 @@ tagGetAttribute <- function(tag, attr) { result } -#' @rdname tag -#' @param .cssSelector A character string containing a [CSS selector](https://developer.mozilla.org/en-US/docs/Learn/CSS/Building_blocks/Selectors) for targeting particular (inner) tags of interest. At the moment, only a combination of [type](https://www.w3.org/TR/CSS22/selector.html#type-selectors) (e.g, `div`), [class](https://www.w3.org/TR/CSS22/selector.html#class-html) (e.g., `.my-class`), [id](https://www.w3.org/TR/CSS22/selector.html#id-selectors) (e.g., `#myID`), and [universal](https://www.w3.org/TR/CSS22/selector.html#universal-selector) (`*`) selectors within a given [simple selector](https://www.w3.org/TR/CSS22/selector.html#selector-syntax) is supported. +#' Modify tag contents +#' +#' Modify the contents (aka children) of a [tag] object. +#' +#' @inheritParams tagAppendAttributes +#' @param child A child element to append to a parent tag. #' @export +#' @seealso [tagAppendAttributes()] +#' @examples +#' +#' html <- div(a(), h1()) +#' tagAppendChild(html, span()) +#' tagAppendChild(html, .cssSelector = "a", span()) +#' +#' tagAppendChildren(html, span(), p()) +#' tagAppendChildren(html, .cssSelector = "a", span(), p()) +#' +#' tagSetChildren(html, span(), p()) +#' +#' tagInsertChildren(html, after = 1, span(), p()) +#' tagAppendChild <- function(tag, child, .cssSelector = NULL) { throw_if_tag_function(tag) @@ -457,8 +504,9 @@ tagAppendChild <- function(tag, child, .cssSelector = NULL) { } - -#' @rdname tag +#' @rdname tagAppendChild +#' @param ... a collection of `child` elements. +#' @param list Deprecated. Use `!!!` instead to splice into `...`. #' @export tagAppendChildren <- function(tag, ..., .cssSelector = NULL, list = NULL) { throw_if_tag_function(tag) @@ -478,7 +526,7 @@ tagAppendChildren <- function(tag, ..., .cssSelector = NULL, list = NULL) { tag } -#' @rdname tag +#' @rdname tagAppendChild #' @export tagSetChildren <- function(tag, ..., .cssSelector = NULL, list = NULL) { throw_if_tag_function(tag) @@ -499,7 +547,7 @@ tagSetChildren <- function(tag, ..., .cssSelector = NULL, list = NULL) { tag } -#' @rdname tag +#' @rdname tagAppendChild #' @param after an integer value (i.e., subscript) referring to the child position to append after. #' @export tagInsertChildren <- function(tag, after, ..., .cssSelector = NULL, list = NULL) { @@ -527,25 +575,159 @@ throw_if_tag_function <- function(tag) { stop("`tag` can not be a `tagFunction()`") } -#' HTML Tag Object -#' -#' `tag()` creates an HTML tag definition. Note that all of the valid HTML5 -#' tags are already defined in the [tags()] environment so these -#' functions should only be used to generate additional tags. -#' `tagAppendChild()` and `tagList()` are for supporting package -#' authors who wish to create their own sets of tags; see the contents of -#' bootstrap.R for examples. -#' @param _tag_name HTML tag name -#' @param varArgs List of attributes and children of the element. Named list -#' items become attributes, and unnamed list items become children. Valid -#' children are tags, single-character character vectors (which become text -#' nodes), and raw HTML (see [HTML()]). You can also pass lists that -#' contain tags, text nodes, and HTML. -#' @param tag A tag to append child elements to. -#' @param child A child element to append to a parent tag. -#' @param ... Unnamed items that comprise this list of tags. -#' @param list An optional list of elements. Can be used with or instead of the -#' `...` items. + +# Use `known_tags` from `known_tags.R` +# Then remove `known_tags` once done creating tag functions +#' @include known_tags.R +names(known_tags) <- known_tags + +#' Create HTML tags +#' +#' Create an R object that represents an HTML tag. For convenience, common HTML +#' tags (e.g., `

`) can be created by calling for their tag name directly +#' (e.g., `div()`). To create less common HTML5 (or SVG) tags (e.g., +#' `
`), use the `tags` list collection (e.g., `tags$article()`). To +#' create other non HTML/SVG tags, use the lower-level `tag()` constructor. +#' +#' @name builder +#' @param ... Tag attributes (named arguments) and children (unnamed arguments). +#' A named argument with an `NA` value is rendered as a boolean attributes +#' (see example). Children may include any combination of: +#' * Other tags objects +#' * [HTML()] strings +#' * [htmlDependency()]s +#' * Single-element atomic vectors +#' * `list()`s containing any combination of the above +#' @return A `list()` with a `shiny.tag` class that can be converted into an +#' HTML string via `as.character()` and saved to a file with `save_html()`. +#' @seealso [tagList()], [withTags()], [tagAppendAttributes()], [tagQuery()] +#' @examples +#' tags$html( +#' tags$head( +#' tags$title('My first page') +#' ), +#' tags$body( +#' h1('My first heading'), +#' p('My first paragraph, with some ', strong('bold'), ' text.'), +#' div( +#' id = 'myDiv', class = 'simpleDiv', +#' 'Here is a div with some attributes.' +#' ) +#' ) +#' ) +#' +#' # html5