mirror of
https://github.com/rstudio/shiny.git
synced 2026-01-11 16:08:19 -05:00
Compare commits
35 Commits
tab-docs
...
wch-css-lo
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fe909f9fc9 | ||
|
|
16e0d9e355 | ||
|
|
d430b80191 | ||
|
|
2ffa8707ea | ||
|
|
cbd06cbd8e | ||
|
|
d3aa1acfbf | ||
|
|
c2232ae07a | ||
|
|
cf0a865d6f | ||
|
|
4942b3e6ad | ||
|
|
f374a1512a | ||
|
|
1558c848f4 | ||
|
|
4a2bb8fc43 | ||
|
|
fad21af146 | ||
|
|
850a628978 | ||
|
|
4d2311841d | ||
|
|
5c4175cd5f | ||
|
|
2931e40c7b | ||
|
|
6a6eae1ce1 | ||
|
|
210642e96c | ||
|
|
c97fad30ef | ||
|
|
268c9afec3 | ||
|
|
5c919ae565 | ||
|
|
e29d92c5ff | ||
|
|
0a331e3366 | ||
|
|
32d0e146ad | ||
|
|
c94f411fc6 | ||
|
|
22d408aa7b | ||
|
|
a44fdc1b11 | ||
|
|
50ca830ec6 | ||
|
|
e643cd3824 | ||
|
|
2660a50d31 | ||
|
|
927912efe3 | ||
|
|
9b49a24e74 | ||
|
|
0824b22532 | ||
|
|
000feead00 |
@@ -3,11 +3,11 @@ Type: Package
|
||||
Title: Web Application Framework for R
|
||||
Version: 1.6.0.9000
|
||||
Authors@R: c(
|
||||
person("Winston", "Chang", role = c("aut", "cre"), email = "winston@rstudio.com"),
|
||||
person("Winston", "Chang", role = c("aut", "cre"), email = "winston@rstudio.com", comment = c(ORCID = "0000-0002-1576-2126")),
|
||||
person("Joe", "Cheng", role = "aut", email = "joe@rstudio.com"),
|
||||
person("JJ", "Allaire", role = "aut", email = "jj@rstudio.com"),
|
||||
person("Carson", "Sievert", role = "aut", email = "carson@rstudio.com"),
|
||||
person("Barret", "Schloerke", role = "aut", email = "barret@rstudio.com"),
|
||||
person("Carson", "Sievert", role = "aut", email = "carson@rstudio.com", comment = c(ORCID = "0000-0002-4958-2844")),
|
||||
person("Barret", "Schloerke", role = "aut", email = "barret@rstudio.com", comment = c(ORCID = "0000-0001-9986-114X")),
|
||||
person("Yihui", "Xie", role = "aut", email = "yihui@rstudio.com"),
|
||||
person("Jeff", "Allen", role = "aut", email = "jeff@rstudio.com"),
|
||||
person("Jonathan", "McPherson", role = "aut", email = "jonathan@rstudio.com"),
|
||||
|
||||
@@ -222,6 +222,7 @@ export(reactlogReset)
|
||||
export(reactlogShow)
|
||||
export(registerInputHandler)
|
||||
export(registerThemeDependency)
|
||||
export(register_devmode_option)
|
||||
export(removeInputHandler)
|
||||
export(removeModal)
|
||||
export(removeNotification)
|
||||
@@ -408,6 +409,7 @@ importFrom(rlang,is_false)
|
||||
importFrom(rlang,is_missing)
|
||||
importFrom(rlang,is_na)
|
||||
importFrom(rlang,is_quosure)
|
||||
importFrom(rlang,list2)
|
||||
importFrom(rlang,maybe_missing)
|
||||
importFrom(rlang,missing_arg)
|
||||
importFrom(rlang,new_function)
|
||||
|
||||
17
NEWS.md
17
NEWS.md
@@ -7,7 +7,18 @@ shiny 1.6.0.9000
|
||||
|
||||
* The `format` and `locale` arguments to `sliderInput()` have been removed. They have been deprecated since 0.10.2.2 (released on 2014-12-08).
|
||||
|
||||
### Minor new features and improvements
|
||||
### New features and improvements
|
||||
|
||||
* All uses of `list(...)` have been replaced with `rlang::list2(...)`. This means that you can use trailing `,` without error and use rlang's `!!!` operator to "splice" a list of argument values into `...`. We think this'll be particularly useful for passing a list of `tabPanel()` to their consumers (i.e., `tabsetPanel()`, `navbarPage()`, etc). For example, `tabs <- list(tabPanel("A", "a"), tabPanel("B", "b")); navbarPage(!!!tabs)`. (#3315 and #3328)
|
||||
|
||||
* Numerous improvements tabset panels (i.e., `tabPanel()`, `navbarMenu()`, `tabsetPanel()`, `navbarPage()`, etc) (#3315):
|
||||
* Closed #3322: `tabsetPanel()` and `navlistPanel()` gain `header`/`footer` arguments (inspired by `navbarPage()`'s already existing `header`/`footer`), making it easier to include content that should appear on every tab.
|
||||
* Closed #3313 and #1823: More informative error when non-`tabPanel()`/`shiny.tag` objects are supplied to `...`.
|
||||
* Closed #3321: New informative warning when `shiny.tag` object(s) are supplied to `...`. In this case we will continue to create an "empty" nav item and include the content on every tab, but the warning will mention the (new) `header`/`footer` args, which is likely what the user wants.
|
||||
* Closed #3320: The HTML markup that `tabPanel()` et. al generate (for Bootstrap nav) is now Bootstrap 4+ compliant when used with `theme = bslib::bs_theme()`.
|
||||
* Closed #1928: `NULL` values are now dropped instead of producing an empty nav item.
|
||||
|
||||
### Other improvements
|
||||
|
||||
* Shiny's core JavaScript code was converted to TypeScript. For the latest development information, please see the [README.md in `./srcts`](https://github.com/rstudio/shiny/tree/master/srcts). (#3296)
|
||||
|
||||
@@ -17,6 +28,10 @@ shiny 1.6.0.9000
|
||||
|
||||
### Bug fixes
|
||||
|
||||
* Closed #3374: `quoToFunction()` now works correctly with nested quosures; and as a result, quasi-quotation with rendering function (e.g., `renderPrint()`, `renderPlot()`, etc) now works as expected with nested quosures. (#3373)
|
||||
|
||||
* Exported `register_devmode_option()`. This method was described in the documentation for `devmode()` but was never exported. See `?devmode()` for more details on how to register Shiny Developer options using `register_devmode_option()`. (#3364)
|
||||
|
||||
### Library updates
|
||||
|
||||
* Closed #3286: Updated to Font-Awesome 5.15.2. (#3288)
|
||||
|
||||
@@ -396,7 +396,7 @@ mainPanel <- function(..., width = 8) {
|
||||
#' }
|
||||
#' @export
|
||||
verticalLayout <- function(..., fluid = TRUE) {
|
||||
lapply(list(...), function(row) {
|
||||
lapply(list2(...), function(row) {
|
||||
col <- column(12, row)
|
||||
if (fluid)
|
||||
fluidRow(col)
|
||||
@@ -433,7 +433,7 @@ verticalLayout <- function(..., fluid = TRUE) {
|
||||
#' @export
|
||||
flowLayout <- function(..., cellArgs = list()) {
|
||||
|
||||
children <- list(...)
|
||||
children <- list2(...)
|
||||
childIdx <- !nzchar(names(children) %||% character(length(children)))
|
||||
attribs <- children[!childIdx]
|
||||
children <- children[childIdx]
|
||||
@@ -516,7 +516,7 @@ inputPanel <- function(...) {
|
||||
#' @export
|
||||
splitLayout <- function(..., cellWidths = NULL, cellArgs = list()) {
|
||||
|
||||
children <- list(...)
|
||||
children <- list2(...)
|
||||
childIdx <- !nzchar(names(children) %||% character(length(children)))
|
||||
attribs <- children[!childIdx]
|
||||
children <- children[childIdx]
|
||||
@@ -614,7 +614,7 @@ fillCol <- function(..., flex = 1, width = "100%", height = "100%") {
|
||||
}
|
||||
|
||||
flexfill <- function(..., direction, flex, width = width, height = height) {
|
||||
children <- list(...)
|
||||
children <- list2(...)
|
||||
attrs <- list()
|
||||
|
||||
if (!is.null(names(children))) {
|
||||
|
||||
425
R/bootstrap.R
425
R/bootstrap.R
@@ -43,13 +43,14 @@ bootstrapPage <- function(..., title = NULL, responsive = deprecated(), theme =
|
||||
}
|
||||
|
||||
args <- list(
|
||||
jqueryDependency(),
|
||||
if (!is.null(title)) tags$head(tags$title(title)),
|
||||
if (is.character(theme)) {
|
||||
if (length(theme) > 1) stop("`theme` must point to a single CSS file, not multiple files.")
|
||||
tags$head(tags$link(rel="stylesheet", type="text/css", href=theme))
|
||||
},
|
||||
# remainder of tags passed to the function
|
||||
list(...)
|
||||
list2(...)
|
||||
)
|
||||
|
||||
# If theme is a bslib::bs_theme() object, bootstrapLib() needs to come first
|
||||
@@ -91,6 +92,10 @@ getLang <- function(ui) {
|
||||
#' @export
|
||||
bootstrapLib <- function(theme = NULL) {
|
||||
tagFunction(function() {
|
||||
if (isRunning()) {
|
||||
setCurrentTheme(theme)
|
||||
}
|
||||
|
||||
# If we're not compiling Bootstrap Sass (from bslib), return the
|
||||
# static Bootstrap build.
|
||||
if (!is_bs_theme(theme)) {
|
||||
@@ -112,7 +117,6 @@ bootstrapLib <- function(theme = NULL) {
|
||||
# Note also that since this is shinyOptions() (and not options()), the
|
||||
# option is automatically reset when the app (or session) exits
|
||||
if (isRunning()) {
|
||||
setCurrentTheme(theme)
|
||||
registerThemeDependency(bs_theme_deps)
|
||||
|
||||
} else {
|
||||
@@ -163,6 +167,15 @@ getCurrentTheme <- function() {
|
||||
getShinyOption("bootstrapTheme", default = NULL)
|
||||
}
|
||||
|
||||
getCurrentVersion <- function() {
|
||||
theme <- getCurrentTheme()
|
||||
if (bslib::is_bs_theme(theme)) {
|
||||
bslib::theme_version(theme)
|
||||
} else {
|
||||
strsplit(bootstrapVersion, ".", fixed = TRUE)[[1]][[1]]
|
||||
}
|
||||
}
|
||||
|
||||
setCurrentTheme <- function(theme) {
|
||||
shinyOptions(bootstrapTheme = theme)
|
||||
}
|
||||
@@ -211,7 +224,7 @@ registerThemeDependency <- function(func) {
|
||||
|
||||
bootstrapDependency <- function(theme) {
|
||||
htmlDependency(
|
||||
"bootstrap", "3.4.1",
|
||||
"bootstrap", bootstrapVersion,
|
||||
c(
|
||||
href = "shared/bootstrap",
|
||||
file = system.file("www/shared/bootstrap", package = "shiny")
|
||||
@@ -230,6 +243,8 @@ bootstrapDependency <- function(theme) {
|
||||
)
|
||||
}
|
||||
|
||||
bootstrapVersion <- "3.4.1"
|
||||
|
||||
|
||||
#' @rdname bootstrapPage
|
||||
#' @export
|
||||
@@ -342,13 +357,20 @@ collapseSizes <- function(padding) {
|
||||
#' Create a page with a top level navigation bar
|
||||
#'
|
||||
#' Create a page that contains a top level navigation bar that can be used to
|
||||
#' toggle a set of [tabPanel()] elements. `navbarMenu()` can be used to create
|
||||
#' an embedded menu within the navbar that in turns includes additional
|
||||
#' `tabPanels`.
|
||||
#' toggle a set of [tabPanel()] elements.
|
||||
#'
|
||||
#' @inheritParams navlistPanel
|
||||
#' @inheritParams bootstrapPage
|
||||
#' @param title The title to display in the navbar
|
||||
#' @param ... [tabPanel()] elements to include in the page. The
|
||||
#' `navbarMenu` function also accepts strings, which will be used as menu
|
||||
#' section headers. If the string is a set of dashes like `"----"` a
|
||||
#' horizontal separator will be displayed in the menu.
|
||||
#' @param id If provided, you can use `input$`*`id`* in your
|
||||
#' server logic to determine which of the current tabs is active. The value
|
||||
#' will correspond to the `value` argument that is passed to
|
||||
#' [tabPanel()].
|
||||
#' @param selected The `value` (or, if none was supplied, the `title`)
|
||||
#' of the tab that should be selected by default. If `NULL`, the first
|
||||
#' tab will be selected.
|
||||
#' @param position Determines whether the navbar should be displayed at the top
|
||||
#' of the page with normal scrolling behavior (`"static-top"`), pinned at
|
||||
#' the top (`"fixed-top"`), or pinned at the bottom
|
||||
@@ -366,13 +388,26 @@ collapseSizes <- function(padding) {
|
||||
#' elements into a menu when the width of the browser is less than 940 pixels
|
||||
#' (useful for viewing on smaller touchscreen device)
|
||||
#' @param collapsable Deprecated; use `collapsible` instead.
|
||||
#' @param fluid `TRUE` to use a fluid layout. `FALSE` to use a fixed
|
||||
#' layout.
|
||||
#' @param responsive This option is deprecated; it is no longer optional with
|
||||
#' Bootstrap 3.
|
||||
#' @param windowTitle The title that should be displayed by the browser window.
|
||||
#' Useful if `title` is not a string.
|
||||
#' @inheritParams bootstrapPage
|
||||
#' @param icon Optional icon to appear on a `navbarMenu` tab.
|
||||
#'
|
||||
#' @seealso [updateNavbarPage()], [insertTab()], [showTab()]
|
||||
#' @return A UI defintion that can be passed to the [shinyUI] function.
|
||||
#'
|
||||
#' @details The `navbarMenu` function can be used to create an embedded
|
||||
#' menu within the navbar that in turns includes additional tabPanels (see
|
||||
#' example below).
|
||||
#'
|
||||
#' @seealso [tabPanel()], [tabsetPanel()],
|
||||
#' [updateNavbarPage()], [insertTab()],
|
||||
#' [showTab()]
|
||||
#'
|
||||
#' @family layout functions
|
||||
#' @family tab layouts
|
||||
#'
|
||||
#' @examples
|
||||
#' navbarPage("App Title",
|
||||
@@ -416,10 +451,11 @@ navbarPage <- function(title,
|
||||
pageTitle <- title
|
||||
|
||||
# navbar class based on options
|
||||
# TODO: tagFunction() the navbar logic?
|
||||
navbarClass <- "navbar navbar-default"
|
||||
position <- match.arg(position)
|
||||
if (!is.null(position))
|
||||
navbarClass <- paste(navbarClass, " navbar-", position, sep = "")
|
||||
navbarClass <- paste0(navbarClass, " navbar-", position)
|
||||
if (inverse)
|
||||
navbarClass <- paste(navbarClass, "navbar-inverse")
|
||||
|
||||
@@ -427,21 +463,14 @@ navbarPage <- function(title,
|
||||
selected <- restoreInput(id = id, default = selected)
|
||||
|
||||
# build the tabset
|
||||
tabs <- list(...)
|
||||
tabset <- buildTabset(tabs, "nav navbar-nav", NULL, id, selected)
|
||||
tabset <- buildTabset(..., ulClass = "nav navbar-nav", id = id, selected = selected)
|
||||
|
||||
# function to return plain or fluid class name
|
||||
className <- function(name) {
|
||||
if (fluid)
|
||||
paste(name, "-fluid", sep="")
|
||||
else
|
||||
name
|
||||
}
|
||||
containerClass <- paste0("container", if (fluid) "-fluid")
|
||||
|
||||
# built the container div dynamically to support optional collapsibility
|
||||
if (collapsible) {
|
||||
navId <- paste("navbar-collapse-", p_randomInt(1000, 10000), sep="")
|
||||
containerDiv <- div(class=className("container"),
|
||||
navId <- paste0("navbar-collapse-", p_randomInt(1000, 10000))
|
||||
containerDiv <- div(class=containerClass,
|
||||
div(class="navbar-header",
|
||||
tags$button(type="button", class="navbar-toggle collapsed",
|
||||
`data-toggle`="collapse", `data-target`=paste0("#", navId),
|
||||
@@ -455,7 +484,7 @@ navbarPage <- function(title,
|
||||
div(class="navbar-collapse collapse", id=navId, tabset$navList)
|
||||
)
|
||||
} else {
|
||||
containerDiv <- div(class=className("container"),
|
||||
containerDiv <- div(class=containerClass,
|
||||
div(class="navbar-header",
|
||||
span(class="navbar-brand", pageTitle)
|
||||
),
|
||||
@@ -464,7 +493,7 @@ navbarPage <- function(title,
|
||||
}
|
||||
|
||||
# build the main tab content div
|
||||
contentDiv <- div(class=className("container"))
|
||||
contentDiv <- div(class=containerClass)
|
||||
if (!is.null(header))
|
||||
contentDiv <- tagAppendChild(contentDiv, div(class="row", header))
|
||||
contentDiv <- tagAppendChild(contentDiv, tabset$content)
|
||||
@@ -491,11 +520,15 @@ navbarPage <- function(title,
|
||||
navbarMenu <- function(title, ..., menuName = title, icon = NULL) {
|
||||
structure(list(title = title,
|
||||
menuName = menuName,
|
||||
tabs = list(...),
|
||||
tabs = list2(...),
|
||||
iconClass = iconClass(icon)),
|
||||
class = "shiny.navbarmenu")
|
||||
}
|
||||
|
||||
isNavbarMenu <- function(x) {
|
||||
inherits(x, "shiny.navbarmenu")
|
||||
}
|
||||
|
||||
#' Create a well panel
|
||||
#'
|
||||
#' Creates a panel with a slightly inset border and grey background. Equivalent
|
||||
@@ -603,10 +636,6 @@ helpText <- function(...) {
|
||||
|
||||
#' Create a tab panel
|
||||
#'
|
||||
#' `tabPanel()` creates a tab panel that can be included within a
|
||||
#' [tabsetPanel()], [navListPanel()], or [navbarPage()]. `tabPanelBody()`
|
||||
#' drops the `title`, making it most suitable for use within
|
||||
#' `tabsetPanel(type = "hidden")`.
|
||||
#'
|
||||
#' @param title Display title for tab
|
||||
#' @param ... UI elements to include within the tab
|
||||
@@ -615,8 +644,9 @@ helpText <- function(...) {
|
||||
#' `id`, then the title will be used.
|
||||
#' @param icon Optional icon to appear on the tab. This attribute is only
|
||||
#' valid when using a `tabPanel` within a [navbarPage()].
|
||||
#' @return A tab that can be passed to [tabsetPanel()]
|
||||
#'
|
||||
#' @family tab layouts
|
||||
#' @seealso [tabsetPanel()]
|
||||
#'
|
||||
#' @examples
|
||||
#' # Show a tabset that includes a plot, summary, and
|
||||
@@ -629,6 +659,7 @@ helpText <- function(...) {
|
||||
#' )
|
||||
#' )
|
||||
#' @export
|
||||
#' @describeIn tabPanel Create a tab panel that can be included within a [tabsetPanel()] or a [navbarPage()].
|
||||
tabPanel <- function(title, ..., value = title, icon = NULL) {
|
||||
div(
|
||||
class = "tab-pane",
|
||||
@@ -638,8 +669,16 @@ tabPanel <- function(title, ..., value = title, icon = NULL) {
|
||||
...
|
||||
)
|
||||
}
|
||||
|
||||
isTabPanel <- function(x) {
|
||||
if (!inherits(x, "shiny.tag")) return(FALSE)
|
||||
class <- tagGetAttribute(x, "class") %||% ""
|
||||
"tab-pane" %in% strsplit(class, "\\s+")[[1]]
|
||||
}
|
||||
|
||||
#' @export
|
||||
#' @rdname tabPanel
|
||||
#' @describeIn tabPanel Create a tab panel that drops the title argument.
|
||||
#' This function should be used within `tabsetPanel(type = "hidden")`. See [tabsetPanel()] for example usage.
|
||||
tabPanelBody <- function(value, ..., icon = NULL) {
|
||||
if (
|
||||
!is.character(value) ||
|
||||
@@ -674,8 +713,11 @@ tabPanelBody <- function(value, ..., icon = NULL) {
|
||||
#' }
|
||||
#' @param position This argument is deprecated; it has been discontinued in
|
||||
#' Bootstrap 3.
|
||||
#' @seealso [updateTabsetPanel()], [insertTab()], [showTab()]
|
||||
#' @family tab layouts
|
||||
#' @inheritParams navbarPage
|
||||
#' @return A tabset that can be passed to [mainPanel()]
|
||||
#'
|
||||
#' @seealso [tabPanel()], [updateTabsetPanel()],
|
||||
#' [insertTab()], [showTab()]
|
||||
#'
|
||||
#' @examples
|
||||
#' # Show a tabset that includes a plot, summary, and
|
||||
@@ -721,6 +763,8 @@ tabsetPanel <- function(...,
|
||||
id = NULL,
|
||||
selected = NULL,
|
||||
type = c("tabs", "pills", "hidden"),
|
||||
header = NULL,
|
||||
footer = NULL,
|
||||
position = deprecated()) {
|
||||
if (lifecycle::is_present(position)) {
|
||||
shinyDeprecated(
|
||||
@@ -732,18 +776,18 @@ tabsetPanel <- function(...,
|
||||
if (!is.null(id))
|
||||
selected <- restoreInput(id = id, default = selected)
|
||||
|
||||
# build the tabset
|
||||
tabs <- list(...)
|
||||
type <- match.arg(type)
|
||||
tabset <- buildTabset(..., ulClass = paste0("nav nav-", type), id = id, selected = selected)
|
||||
|
||||
tabset <- buildTabset(tabs, paste0("nav nav-", type), NULL, id, selected)
|
||||
|
||||
# create the content
|
||||
first <- tabset$navList
|
||||
second <- tabset$content
|
||||
|
||||
# create the tab div
|
||||
tags$div(class = "tabbable", first, second)
|
||||
tags$div(
|
||||
class = "tabbable",
|
||||
!!!dropNulls(list(
|
||||
tabset$navList,
|
||||
header,
|
||||
tabset$content,
|
||||
footer
|
||||
))
|
||||
)
|
||||
}
|
||||
|
||||
#' Create a navigation list panel
|
||||
@@ -751,20 +795,31 @@ tabsetPanel <- function(...,
|
||||
#' Create a navigation list panel that provides a list of links on the left
|
||||
#' which navigate to a set of tabPanels displayed to the right.
|
||||
#'
|
||||
#' @inheritParams tabsetPanel
|
||||
#' @param ... [tabPanel()] elements to include in the navbar.
|
||||
#' Plain strings will be converted to headers.
|
||||
#' @param ... [tabPanel()] elements to include in the navlist
|
||||
#' @param id If provided, you can use `input$`*`id`* in your
|
||||
#' server logic to determine which of the current navlist items is active. The
|
||||
#' value will correspond to the `value` argument that is passed to
|
||||
#' [tabPanel()].
|
||||
#' @param selected The `value` (or, if none was supplied, the `title`)
|
||||
#' of the navigation item that should be selected by default. If `NULL`,
|
||||
#' the first navigation will be selected.
|
||||
#' @param well `TRUE` to place a well (gray rounded rectangle) around the
|
||||
#' navigation list.
|
||||
#' @param fluid `TRUE` to use fluid layout; `FALSE` to use fixed
|
||||
#' layout.
|
||||
#' @param widths Column widths of the navigation list and tabset content areas
|
||||
#' respectively.
|
||||
#' @inheritParams tabsetPanel
|
||||
#' @inheritParams navbarPage
|
||||
#'
|
||||
#' @details
|
||||
#' @details You can include headers within the `navlistPanel` by including
|
||||
#' plain text elements in the list. Versions of Shiny before 0.11 supported
|
||||
#' separators with "------", but as of 0.11, separators were no longer
|
||||
#' supported. This is because version 0.11 switched to Bootstrap 3, which
|
||||
#' doesn't support separators.
|
||||
#'
|
||||
#' @seealso [updateTabsetPanel()], [insertTab()], [showTab()]
|
||||
#' @family tab layouts
|
||||
#' @seealso [tabPanel()], [updateNavlistPanel()],
|
||||
#' [insertTab()], [showTab()]
|
||||
#'
|
||||
#' @examples
|
||||
#' fluidPage(
|
||||
@@ -782,37 +837,30 @@ tabsetPanel <- function(...,
|
||||
navlistPanel <- function(...,
|
||||
id = NULL,
|
||||
selected = NULL,
|
||||
header = NULL,
|
||||
footer = NULL,
|
||||
well = TRUE,
|
||||
fluid = TRUE,
|
||||
widths = c(4, 8)) {
|
||||
|
||||
# text filter for headers
|
||||
textFilter <- function(text) {
|
||||
tags$li(class="navbar-brand", text)
|
||||
}
|
||||
|
||||
if (!is.null(id))
|
||||
selected <- restoreInput(id = id, default = selected)
|
||||
|
||||
# build the tabset
|
||||
tabs <- list(...)
|
||||
tabset <- buildTabset(tabs,
|
||||
"nav nav-pills nav-stacked",
|
||||
textFilter,
|
||||
id,
|
||||
selected)
|
||||
|
||||
# create the columns
|
||||
columns <- list(
|
||||
column(widths[[1]], class=ifelse(well, "well", ""), tabset$navList),
|
||||
column(widths[[2]], tabset$content)
|
||||
tabset <- buildTabset(
|
||||
..., ulClass = "nav nav-pills nav-stacked",
|
||||
textFilter = function(text) tags$li(class = "navbar-brand", text),
|
||||
id = id, selected = selected
|
||||
)
|
||||
|
||||
# return the row
|
||||
if (fluid)
|
||||
fluidRow(columns)
|
||||
else
|
||||
fixedRow(columns)
|
||||
row <- if (fluid) fluidRow else fixedRow
|
||||
|
||||
row(
|
||||
column(widths[[1]], class = if (well) "well", tabset$navList),
|
||||
column(
|
||||
widths[[2]],
|
||||
!!!dropNulls(list(header, tabset$content, footer))
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
# Helpers to build tabsetPanels (& Co.) and their elements
|
||||
@@ -830,14 +878,14 @@ containsSelectedTab <- function(tabs) {
|
||||
}
|
||||
|
||||
findAndMarkSelectedTab <- function(tabs, selected, foundSelected) {
|
||||
tabs <- lapply(tabs, function(div) {
|
||||
if (foundSelected || is.character(div)) {
|
||||
tabs <- lapply(tabs, function(x) {
|
||||
if (foundSelected || is.character(x)) {
|
||||
# Strings are not selectable items
|
||||
|
||||
} else if (inherits(div, "shiny.navbarmenu")) {
|
||||
} else if (isNavbarMenu(x)) {
|
||||
# Recur for navbarMenus
|
||||
res <- findAndMarkSelectedTab(div$tabs, selected, foundSelected)
|
||||
div$tabs <- res$tabs
|
||||
res <- findAndMarkSelectedTab(x$tabs, selected, foundSelected)
|
||||
x$tabs <- res$tabs
|
||||
foundSelected <<- res$foundSelected
|
||||
|
||||
} else {
|
||||
@@ -846,16 +894,16 @@ findAndMarkSelectedTab <- function(tabs, selected, foundSelected) {
|
||||
# mark first available item as selected
|
||||
if (is.null(selected)) {
|
||||
foundSelected <<- TRUE
|
||||
div <- markTabAsSelected(div)
|
||||
x <- markTabAsSelected(x)
|
||||
} else {
|
||||
tabValue <- div$attribs$`data-value` %||% div$attribs$title
|
||||
tabValue <- x$attribs$`data-value` %||% x$attribs$title
|
||||
if (identical(selected, tabValue)) {
|
||||
foundSelected <<- TRUE
|
||||
div <- markTabAsSelected(div)
|
||||
x <- markTabAsSelected(x)
|
||||
}
|
||||
}
|
||||
}
|
||||
return(div)
|
||||
return(x)
|
||||
})
|
||||
return(list(tabs = tabs, foundSelected = foundSelected))
|
||||
}
|
||||
@@ -881,9 +929,10 @@ navbarMenuTextFilter <- function(text) {
|
||||
|
||||
# This function is called internally by navbarPage, tabsetPanel
|
||||
# and navlistPanel
|
||||
buildTabset <- function(tabs, ulClass, textFilter = NULL, id = NULL,
|
||||
buildTabset <- function(..., ulClass, textFilter = NULL, id = NULL,
|
||||
selected = NULL, foundSelected = FALSE) {
|
||||
|
||||
tabs <- dropNulls(list2(...))
|
||||
res <- findAndMarkSelectedTab(tabs, selected, foundSelected)
|
||||
tabs <- res$tabs
|
||||
foundSelected <- res$foundSelected
|
||||
@@ -904,10 +953,10 @@ buildTabset <- function(tabs, ulClass, textFilter = NULL, id = NULL,
|
||||
tabs = tabs, textFilter = textFilter)
|
||||
|
||||
tabNavList <- tags$ul(class = ulClass, id = id,
|
||||
`data-tabsetid` = tabsetId, lapply(tabs, "[[", 1))
|
||||
`data-tabsetid` = tabsetId, !!!lapply(tabs, "[[", "liTag"))
|
||||
|
||||
tabContent <- tags$div(class = "tab-content",
|
||||
`data-tabsetid` = tabsetId, lapply(tabs, "[[", 2))
|
||||
`data-tabsetid` = tabsetId, !!!lapply(tabs, "[[", "divTag"))
|
||||
|
||||
list(navList = tabNavList, content = tabContent)
|
||||
}
|
||||
@@ -919,56 +968,176 @@ buildTabset <- function(tabs, ulClass, textFilter = NULL, id = NULL,
|
||||
buildTabItem <- function(index, tabsetId, foundSelected, tabs = NULL,
|
||||
divTag = NULL, textFilter = NULL) {
|
||||
|
||||
divTag <- if (!is.null(divTag)) divTag else tabs[[index]]
|
||||
divTag <- divTag %||% tabs[[index]]
|
||||
|
||||
# Handles navlistPanel() headers and dropdown dividers
|
||||
if (is.character(divTag) && !is.null(textFilter)) {
|
||||
# text item: pass it to the textFilter if it exists
|
||||
liTag <- textFilter(divTag)
|
||||
divTag <- NULL
|
||||
|
||||
} else if (inherits(divTag, "shiny.navbarmenu")) {
|
||||
# navbarMenu item: build the child tabset
|
||||
tabset <- buildTabset(divTag$tabs, "dropdown-menu",
|
||||
navbarMenuTextFilter, foundSelected = foundSelected)
|
||||
|
||||
# if this navbarMenu contains a selected item, mark it active
|
||||
containsSelected <- containsSelectedTab(divTag$tabs)
|
||||
liTag <- tags$li(
|
||||
class = paste0("dropdown", if (containsSelected) " active"),
|
||||
tags$a(href = "#",
|
||||
class = "dropdown-toggle", `data-toggle` = "dropdown",
|
||||
`data-value` = divTag$menuName,
|
||||
getIcon(iconClass = divTag$iconClass),
|
||||
divTag$title, tags$b(class = "caret")
|
||||
),
|
||||
tabset$navList # inner tabPanels items
|
||||
)
|
||||
# list of tab content divs from the child tabset
|
||||
divTag <- tabset$content$children
|
||||
|
||||
} else {
|
||||
# tabPanel item: create the tab's liTag and divTag
|
||||
tabId <- paste("tab", tabsetId, index, sep = "-")
|
||||
liTag <- tags$li(
|
||||
tags$a(
|
||||
href = paste("#", tabId, sep = ""),
|
||||
`data-toggle` = "tab",
|
||||
`data-value` = divTag$attribs$`data-value`,
|
||||
getIcon(iconClass = divTag$attribs$`data-icon-class`),
|
||||
divTag$attribs$title
|
||||
)
|
||||
)
|
||||
# if this tabPanel is selected item, mark it active
|
||||
if (isTabSelected(divTag)) {
|
||||
liTag$attribs$class <- "active"
|
||||
divTag$attribs$class <- "tab-pane active"
|
||||
}
|
||||
divTag$attribs$id <- tabId
|
||||
divTag$attribs$title <- NULL
|
||||
return(list(liTag = textFilter(divTag), divTag = NULL))
|
||||
}
|
||||
return(list(liTag = liTag, divTag = divTag))
|
||||
|
||||
if (isNavbarMenu(divTag)) {
|
||||
# tabPanelMenu item: build the child tabset
|
||||
tabset <- buildTabset(
|
||||
!!!divTag$tabs, ulClass = "dropdown-menu",
|
||||
textFilter = navbarMenuTextFilter,
|
||||
foundSelected = foundSelected
|
||||
)
|
||||
return(buildDropdown(divTag, tabset))
|
||||
}
|
||||
|
||||
if (isTabPanel(divTag)) {
|
||||
return(buildNavItem(divTag, tabsetId, index))
|
||||
}
|
||||
|
||||
# The behavior is undefined at this point, so construct a condition message
|
||||
msg <- paste0(
|
||||
"Expected a collection `tabPanel()`s",
|
||||
if (is.null(textFilter)) " and `navbarMenu()`.",
|
||||
if (!is.null(textFilter)) ", `navbarMenu()`, and/or character strings.",
|
||||
" Consider using `header` or `footer` if you wish to place content above (or below) every panel's contents"
|
||||
)
|
||||
|
||||
# Luckily this case has never worked, so it's safe to throw here
|
||||
# https://github.com/rstudio/shiny/issues/3313
|
||||
if (!inherits(divTag, "shiny.tag")) {
|
||||
stop(msg, call. = FALSE)
|
||||
}
|
||||
|
||||
# Unfortunately, this 'off-label' use case creates an 'empty' nav and includes
|
||||
# the divTag content on every tab. There shouldn't be any reason to be relying on
|
||||
# this behavior since we now have pre/post arguments, so throw a warning, but still
|
||||
# support the use case since we don't make breaking changes
|
||||
warning(msg, call. = FALSE)
|
||||
|
||||
return(buildNavItem(divTag, tabsetId, index))
|
||||
}
|
||||
|
||||
buildNavItem <- function(divTag, tabsetId, index) {
|
||||
id <- paste("tab", tabsetId, index, sep = "-")
|
||||
# Get title attribute directory (not via tagGetAttribute()) so that contents
|
||||
# don't get passed to as.character().
|
||||
# https://github.com/rstudio/shiny/issues/3352
|
||||
title <- divTag$attribs[["title"]]
|
||||
value <- divTag$attribs[["data-value"]]
|
||||
icon <- getIcon(iconClass = divTag$attribs[["data-icon-class"]])
|
||||
active <- isTabSelected(divTag)
|
||||
divTag <- tagAppendAttributes(divTag, class = if (active) "active")
|
||||
divTag$attribs$id <- id
|
||||
divTag$attribs$title <- NULL
|
||||
list(
|
||||
divTag = divTag,
|
||||
liTag = tagFunction(function() {
|
||||
navItem <- if ("3" %in% getCurrentVersion()) bs3NavItem else bs4NavItem
|
||||
navItem(id, title, value, icon, active)
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
buildDropdown <- function(divTag, tabset) {
|
||||
title <- divTag$title
|
||||
value <- divTag$menuName
|
||||
icon <- getIcon(iconClass = divTag$iconClass)
|
||||
active <- containsSelectedTab(divTag$tabs)
|
||||
list(
|
||||
# list of tab content divs from the child tabset
|
||||
divTag = tabset$content$children,
|
||||
liTag = tagFunction(function() {
|
||||
if ("3" %in% getCurrentVersion()) {
|
||||
bs3NavItemDropdown(title, value, icon, active, tabset$navList)
|
||||
} else {
|
||||
# In BS4, dropdown nav anchors can't be wrapped in a <li> tag
|
||||
# and also need .nav-link replaced with .dropdown-item to be
|
||||
# styled sensibly
|
||||
items <- tabset$navList
|
||||
items$children <- lapply(items$children, function(x) {
|
||||
# x should be a tagFunction() due to the else block below
|
||||
x <- if (inherits(x, "shiny.tag.function")) x() else x
|
||||
# Replace <li class="nav-item"><a class="nav-link"></a></li>
|
||||
# with <a class="dropdown-item"></a>
|
||||
if (tagHasClass(x, "nav-item")) {
|
||||
x <- x$children[[1]]
|
||||
x$attribs$class <- "dropdown-item"
|
||||
}
|
||||
x
|
||||
})
|
||||
bs4NavItemDropdown(title, value, icon, active, items)
|
||||
}
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
bs3NavItemDropdown <- function(title, value, icon, active, items) {
|
||||
tags$li(
|
||||
class = "dropdown",
|
||||
class = if (active) "active", # BS3
|
||||
tags$a(
|
||||
href = "#",
|
||||
class = "dropdown-toggle",
|
||||
`data-toggle` = "dropdown",
|
||||
`data-value` = value,
|
||||
icon,
|
||||
title, tags$b(class = "caret")
|
||||
),
|
||||
items
|
||||
)
|
||||
}
|
||||
|
||||
bs3NavItem <- function(id, title, value, icon, active) {
|
||||
tags$li(
|
||||
class = if (active) "active",
|
||||
tags$a(
|
||||
href = paste0("#", id),
|
||||
`data-toggle` = "tab",
|
||||
`data-value` = value,
|
||||
icon,
|
||||
title
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
bs4NavItemDropdown <- function(title, value, icon, active, items) {
|
||||
tags$li(
|
||||
class = "dropdown",
|
||||
class = "nav-item",
|
||||
tags$a(
|
||||
href = "#",
|
||||
class = "dropdown-toggle",
|
||||
class = "nav-link",
|
||||
class = if (active) "active",
|
||||
`data-toggle` = "dropdown",
|
||||
`data-value` = value,
|
||||
icon,
|
||||
title,
|
||||
tags$b(class = "caret") # TODO: can be removed?
|
||||
),
|
||||
items
|
||||
)
|
||||
}
|
||||
|
||||
bs4NavItem <- function(id, title, value, icon, active) {
|
||||
tags$li(
|
||||
class = "nav-item",
|
||||
tags$a(
|
||||
class = "nav-link",
|
||||
class = if (active) "active",
|
||||
href = paste0("#", id),
|
||||
`data-toggle` = "tab",
|
||||
`data-value` = value,
|
||||
icon,
|
||||
title
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
# TODO: something like this should exist in htmltools
|
||||
tagHasClass <- function(x, class) {
|
||||
if (!inherits(x, "shiny.tag")) return(FALSE)
|
||||
classes <- unlist(x$attribs[names(x$attribs) %in% "class"], use.names = FALSE)
|
||||
if (!length(classes)) return(FALSE)
|
||||
classes <- unlist(strsplit(classes, split = "\\s+"), use.names = FALSE)
|
||||
isTRUE(class %in% classes)
|
||||
}
|
||||
|
||||
#' Create a text output element
|
||||
#'
|
||||
|
||||
@@ -233,6 +233,7 @@ registered_devmode_options <- Map$new()
|
||||
#' devmode_default = FALSE
|
||||
#' )
|
||||
#' ```
|
||||
#'
|
||||
#' @param name Name of option to look for in `options()`
|
||||
#' @param default Default value to return if `in_devmode()` returns
|
||||
#' `TRUE` and the specified option is not set in [`options()`].
|
||||
@@ -243,6 +244,7 @@ registered_devmode_options <- Map$new()
|
||||
#' `TRUE` and the specified option is not set in [`options()`]. For
|
||||
#' `get_devmode_option()`, if `devmode_default` is missing, the
|
||||
#' registered `devmode_default` value will be used.
|
||||
#' @export
|
||||
#' @examples
|
||||
#' # Ex: Within shiny, we register the option "shiny.minified"
|
||||
#' # to default to `FALSE` when in Dev Mode
|
||||
|
||||
@@ -49,6 +49,12 @@ processDeps <- function(tags, session) {
|
||||
)
|
||||
names(dependencies) <- NULL
|
||||
|
||||
# If ui is a tagFunction() (e.g., insertTab() et al),
|
||||
# then doRenderTags() won't work...
|
||||
if (inherits(ui, "shiny.tag.function")) {
|
||||
ui <- renderTags(ui)$html
|
||||
}
|
||||
|
||||
list(
|
||||
html = doRenderTags(ui),
|
||||
deps = dependencies
|
||||
|
||||
@@ -568,7 +568,7 @@ ReactiveValues <- R6Class(
|
||||
#' @seealso [isolate()] and [is.reactivevalues()].
|
||||
#' @export
|
||||
reactiveValues <- function(...) {
|
||||
args <- list(...)
|
||||
args <- list2(...)
|
||||
if ((length(args) > 0) && (is.null(names(args)) || any(names(args) == "")))
|
||||
rlang::abort("All arguments passed to reactiveValues() must be named.")
|
||||
|
||||
@@ -1915,7 +1915,7 @@ reactivePoll <- function(intervalMillis, session, checkFunc, valueFunc) {
|
||||
#' @export
|
||||
reactiveFileReader <- function(intervalMillis, session, filePath, readFunc, ...) {
|
||||
filePath <- coerceToFunc(filePath)
|
||||
extraArgs <- list(...)
|
||||
extraArgs <- list2(...)
|
||||
|
||||
reactivePoll(
|
||||
intervalMillis, session,
|
||||
|
||||
@@ -178,7 +178,7 @@ getShinyOption <- function(name, default = NULL) {
|
||||
#' @aliases shiny-options
|
||||
#' @export
|
||||
shinyOptions <- function(...) {
|
||||
newOpts <- list(...)
|
||||
newOpts <- list2(...)
|
||||
|
||||
if (length(newOpts) > 0) {
|
||||
# If we're within a session, modify at the session level.
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
#' quo enquo as_function get_expr get_env new_function enquos
|
||||
#' eval_tidy expr pairlist2 new_quosure enexpr as_quosure is_quosure inject
|
||||
#' enquos0 zap_srcref %||% is_na
|
||||
#' is_false
|
||||
#' is_false list2
|
||||
#' missing_arg is_missing maybe_missing
|
||||
#' @importFrom ellipsis
|
||||
#' check_dots_empty check_dots_unnamed
|
||||
|
||||
@@ -2175,7 +2175,7 @@ ShinySession <- R6Class(
|
||||
if (getOption("shiny.allowoutputreads", FALSE)) {
|
||||
.subset2(x, 'impl')$getOutput(name)
|
||||
} else {
|
||||
rlang::abort(paste0("Can't read output '", output, "'"))
|
||||
rlang::abort(paste0("Can't read output '", name, "'"))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
48
R/shinyui.R
48
R/shinyui.R
@@ -47,27 +47,8 @@ renderPage <- function(ui, showcase=0, testMode=FALSE) {
|
||||
)
|
||||
}
|
||||
|
||||
jquery <- function() {
|
||||
version <- getOption("shiny.jquery.version", 3)
|
||||
if (version == 3) {
|
||||
return(htmlDependency(
|
||||
"jquery", version_jquery,
|
||||
c(href = "shared"),
|
||||
script = "jquery.min.js"
|
||||
))
|
||||
}
|
||||
if (version == 1) {
|
||||
return(htmlDependency(
|
||||
"jquery", "1.12.4",
|
||||
c(href = "shared/legacy"),
|
||||
script = "jquery.min.js"
|
||||
))
|
||||
}
|
||||
stop("Unsupported version of jQuery: ", version)
|
||||
}
|
||||
|
||||
shiny_deps <- c(
|
||||
list(jquery()),
|
||||
list(jqueryDependency()),
|
||||
shinyDependencies()
|
||||
)
|
||||
|
||||
@@ -82,6 +63,33 @@ renderPage <- function(ui, showcase=0, testMode=FALSE) {
|
||||
enc2utf8(paste(collapse = "\n", html))
|
||||
}
|
||||
|
||||
jqueryDependency <- function() {
|
||||
version <- getOption("shiny.jquery.version", 3)
|
||||
if (version == 3) {
|
||||
return(htmlDependency(
|
||||
"jquery", version_jquery,
|
||||
src = c(
|
||||
href = "shared",
|
||||
file = "www/shared"
|
||||
),
|
||||
package = "shiny",
|
||||
script = "jquery.min.js"
|
||||
))
|
||||
}
|
||||
if (version == 1) {
|
||||
return(htmlDependency(
|
||||
"jquery", "1.12.4",
|
||||
src = c(
|
||||
href = "shared/legacy",
|
||||
file = "www/shared/legacy"
|
||||
),
|
||||
package = "shiny",
|
||||
script = "jquery.min.js"
|
||||
))
|
||||
}
|
||||
stop("Unsupported version of jQuery: ", version)
|
||||
}
|
||||
|
||||
shinyDependencies <- function() {
|
||||
list(
|
||||
bslib::bs_dependency_defer(shinyDependencyCSS),
|
||||
|
||||
@@ -538,9 +538,10 @@ installExprFunction <- function(expr, name, eval.env = parent.frame(2),
|
||||
#' @export
|
||||
quoToFunction <- function(q, label, ..stacktraceon = FALSE) {
|
||||
q <- as_quosure(q)
|
||||
# Use new_function() instead of as_function(), because as_function() adds an
|
||||
# extra parent environment. (This may not actually be a problem, though.)
|
||||
func <- new_function(NULL, get_expr(q), get_env(q))
|
||||
func <- as_function(q)
|
||||
# as_function returns a function that takes `...`. We want one that takes no
|
||||
# args.
|
||||
formals(func) <- list()
|
||||
wrapFunctionLabel(func, label, ..stacktraceon = ..stacktraceon)
|
||||
}
|
||||
|
||||
@@ -1176,7 +1177,7 @@ reactiveStop <- function(message = "", class = NULL) {
|
||||
#'
|
||||
#' }
|
||||
validate <- function(..., errorClass = character(0)) {
|
||||
results <- sapply(list(...), function(x) {
|
||||
results <- sapply(list2(...), function(x) {
|
||||
# Detect NULL or NA
|
||||
if (is.null(x))
|
||||
return(NA_character_)
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
|
||||
Selectize.define("selectize-plugin-a11y",function(t){var e,s,a=this;void 0===a.accessibility&&(a.accessibility={}),a.accessibility.helpers={randomId:function(t){for(var e="",i=t||10,s="abcdefghijklmnopqrstuvwxyz0123456789",a=s.length,o=0;o<i;o++)e+=s[Math.floor(a*Math.random())];return e}},a.accessibility.liveRegion={$region:"",speak:function(t){var e=$("<div></div>");e.text(t),this.$region.html(e)},domListener:function(){var t=new MutationObserver(function(t){t.forEach(function(t){t=$(t.target);if(t.hasClass("items"))if(t.hasClass("dropdown-active")){a.$control_input.attr("aria-expanded","true");var e=a.$dropdown_content[0].children;for(i=0;i<e.length;i++){var s=e[i].attributes;s.role||e[i].setAttribute("role","option"),s.id||e[i].setAttribute("id",a.accessibility.helpers.randomId())}}else a.$control_input.attr("aria-expanded","false"),a.$control_input.removeAttr("aria-activedescendant");else t.hasClass("active")&&t.attr("data-value")&&(a.$control_input.attr("aria-activedescendant",t.attr("id")),a.accessibility.liveRegion.speak(t.text(),500))})});t.observe(a.$dropdown[0],{attributes:!0,attributeFilter:["class"],subtree:!0,attributeOldValue:!0}),t.observe(a.$control[0],{attributes:!0,attributeFilter:["class"]}),t.observe(a.$control_input[0],{attributes:!0,attributeFilter:["value"]})},setAttributes:function(){this.$region.attr({"aria-live":"assertive",role:"log","aria-relevant":"additions","aria-atomic":"true"})},setStyles:function(){this.$region.css({position:"absolute",width:"1px",height:"1px","margin-top":"-1px",clip:"rect(1px, 1px, 1px, 1px)",overflow:"hidden"})},init:function(){this.$region=$("<div>"),this.setAttributes(),this.setStyles(),$("body").append(this.$region),this.domListener()}},this.setup=(e=a.setup,function(){e.apply(this,arguments);a.accessibility.helpers.randomId();var t=a.accessibility.helpers.randomId();a.$control.on("keydown",function(t){13===t.keyCode&&(a.settings.openOnFocus?(a.settings.openOnFocus=!1,a.focus(),setTimeout(function(){a.settings.openOnFocus=!0},0)):a.focus())}),a.$control_input.attr({role:"combobox","aria-expanded":"false",haspopup:"listbox","aria-owns":t,"aria-label":a.$wrapper.closest("[data-accessibility-selectize-label]").attr("data-accessibility-selectize-label")}),a.$dropdown_content.attr({role:"listbox",id:t}),a.accessibility.liveRegion.init()}),this.destroy=(s=a.destroy,function(){return a.accessibility.liveRegion.$region.remove(),s.apply(this,arguments)})});
|
||||
Selectize.define("selectize-plugin-a11y",function(c){var t=this,l=13;typeof t.accessibility=="undefined"&&(t.accessibility={}),t.accessibility.helpers={randomId:function(e){for(var a="",s=e||10,r="abcdefghijklmnopqrstuvwxyz0123456789",n=r.length,o=0;o<s;o++)a+=r[Math.floor(n*Math.random())];return a}},t.accessibility.liveRegion={$region:"",speak:function(e){var a=$("<div></div>");a.text(e),this.$region.html(a)},domListener:function(){var e=new MutationObserver(function(a){a.forEach(function(s){var r=$(s.target);if(r.hasClass("items"))if(r.hasClass("dropdown-active")){t.$control_input.attr("aria-expanded","true");var n=t.$dropdown_content[0].children;for(i=0;i<n.length;i++){var o=n[i].attributes;o.role||n[i].setAttribute("role","option"),o.id||n[i].setAttribute("id",t.accessibility.helpers.randomId())}}else t.$control_input.attr("aria-expanded","false"),t.$control_input.removeAttr("aria-activedescendant");else r.hasClass("active")&&r.attr("data-value")&&(t.$control_input.attr("aria-activedescendant",r.attr("id")),t.accessibility.liveRegion.speak(r.text(),500))})});e.observe(t.$dropdown[0],{attributes:!0,attributeFilter:["class"],subtree:!0,attributeOldValue:!0}),e.observe(t.$control[0],{attributes:!0,attributeFilter:["class"]}),e.observe(t.$control_input[0],{attributes:!0,attributeFilter:["value"]})},setAttributes:function(){this.$region.attr({"aria-live":"assertive",role:"log","aria-relevant":"additions","aria-atomic":"true"})},setStyles:function(){this.$region.css({position:"absolute",width:"1px",height:"1px","margin-top":"-1px",clip:"rect(1px, 1px, 1px, 1px)",overflow:"hidden"})},init:function(){this.$region=$("<div>"),this.setAttributes(),this.setStyles(),$("body").append(this.$region),this.domListener()}},this.setup=function(){var e=t.setup;return function(){e.apply(this,arguments);var a=t.accessibility.helpers.randomId(),s=t.accessibility.helpers.randomId();t.$control.on("keydown",function(r){r.keyCode===l&&(t.settings.openOnFocus?(t.settings.openOnFocus=!1,t.focus(),setTimeout(function(){t.settings.openOnFocus=!0},0)):t.focus())}),t.$control_input.attr({role:"combobox","aria-expanded":"false",haspopup:"listbox","aria-owns":s,"aria-label":t.$wrapper.closest("[data-accessibility-selectize-label]").attr("data-accessibility-selectize-label")}),t.$dropdown_content.attr({role:"listbox",id:s}),t.accessibility.liveRegion.init()}}(),this.destroy=function(){var e=t.destroy;return function(){return t.accessibility.liveRegion.$region.remove(),e.apply(this,arguments)}}()});
|
||||
|
||||
@@ -1198,6 +1198,26 @@
|
||||
};
|
||||
});
|
||||
|
||||
// node_modules/core-js/internals/regexp-exec-abstract.js
|
||||
var require_regexp_exec_abstract = __commonJS(function(exports2, module2) {
|
||||
var classof2 = require_classof_raw();
|
||||
var regexpExec2 = require_regexp_exec();
|
||||
module2.exports = function(R, S) {
|
||||
var exec = R.exec;
|
||||
if (typeof exec === "function") {
|
||||
var result = exec.call(R, S);
|
||||
if (typeof result !== "object") {
|
||||
throw TypeError("RegExp exec method returned something other than an Object or null");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
if (classof2(R) !== "RegExp") {
|
||||
throw TypeError("RegExp#exec called on incompatible receiver");
|
||||
}
|
||||
return regexpExec2.call(R, S);
|
||||
};
|
||||
});
|
||||
|
||||
// node_modules/core-js/internals/get-substitution.js
|
||||
var require_get_substitution = __commonJS(function(exports2, module2) {
|
||||
var toObject4 = require_to_object();
|
||||
@@ -1246,26 +1266,6 @@
|
||||
};
|
||||
});
|
||||
|
||||
// node_modules/core-js/internals/regexp-exec-abstract.js
|
||||
var require_regexp_exec_abstract = __commonJS(function(exports2, module2) {
|
||||
var classof2 = require_classof_raw();
|
||||
var regexpExec2 = require_regexp_exec();
|
||||
module2.exports = function(R, S) {
|
||||
var exec = R.exec;
|
||||
if (typeof exec === "function") {
|
||||
var result = exec.call(R, S);
|
||||
if (typeof result !== "object") {
|
||||
throw TypeError("RegExp exec method returned something other than an Object or null");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
if (classof2(R) !== "RegExp") {
|
||||
throw TypeError("RegExp#exec called on incompatible receiver");
|
||||
}
|
||||
return regexpExec2.call(R, S);
|
||||
};
|
||||
});
|
||||
|
||||
// node_modules/core-js/internals/is-regexp.js
|
||||
var require_is_regexp = __commonJS(function(exports2, module2) {
|
||||
var isObject3 = require_is_object();
|
||||
@@ -2996,7 +2996,9 @@
|
||||
function getTargetTabs($tabset, $tabContent, target) {
|
||||
var dataValue = "[data-value='" + $escape(target) + "']";
|
||||
var $aTag = $tabset.find("a" + dataValue);
|
||||
var $liTag = $aTag.parent();
|
||||
var $liTag = $aTag.parent("li");
|
||||
if ($liTag.length === 0)
|
||||
$liTag = $aTag;
|
||||
if ($liTag.length === 0) {
|
||||
throw "There is no tabPanel (or navbarMenu) with value (or menuName) equal to '" + target + "'";
|
||||
}
|
||||
@@ -3005,7 +3007,10 @@
|
||||
if ($aTag.attr("data-toggle") === "dropdown") {
|
||||
var $dropdownTabset = $aTag.find("+ ul.dropdown-menu");
|
||||
var dropdownId = $dropdownTabset.attr("data-tabsetid");
|
||||
var $dropdownLiTags = $dropdownTabset.find("a[data-toggle='tab']").parent("li");
|
||||
var $dropdownLiTags = $dropdownTabset.find("a[data-toggle='tab']");
|
||||
if ($dropdownLiTags.parent("li").length > 0) {
|
||||
$dropdownLiTags = $dropdownLiTags.parent("li");
|
||||
}
|
||||
$dropdownLiTags.each(function(i, el) {
|
||||
$liTags.push(import_jquery6.default(el));
|
||||
});
|
||||
@@ -3036,6 +3041,9 @@
|
||||
if (message.target !== null) {
|
||||
target = getTargetTabs($tabset, $tabContent, message.target);
|
||||
$targetLiTag = target.$liTag;
|
||||
if ($targetLiTag.hasClass("dropdown-item")) {
|
||||
$liTag = $aTag.removeClass("nav-link").addClass("dropdown-item");
|
||||
}
|
||||
}
|
||||
var dropdown = getDropdown();
|
||||
if (dropdown !== null) {
|
||||
@@ -3047,7 +3055,10 @@
|
||||
if ($aTag.attr("data-toggle") === "tab") {
|
||||
var index = getTabIndex($tabset, tabsetId);
|
||||
var tabId = "tab-" + tabsetId + "-" + index;
|
||||
$liTag.find("> a").attr("href", "#" + tabId);
|
||||
var anchor = $liTag.find("> a");
|
||||
if (anchor.length === 0)
|
||||
anchor = $liTag;
|
||||
anchor.attr("href", "#" + tabId);
|
||||
$divTag.attr("id", tabId);
|
||||
}
|
||||
if (message.position === "before") {
|
||||
@@ -3080,8 +3091,8 @@
|
||||
}
|
||||
function getTabIndex($tabset2, tabsetId2) {
|
||||
var existingTabIds = [0];
|
||||
$tabset2.find("> li").each(function() {
|
||||
var $tab = import_jquery6.default(this).find("> a[data-toggle='tab']");
|
||||
$tabset2.find("a[data-toggle='tab']").each(function() {
|
||||
var $tab = import_jquery6.default(this);
|
||||
if ($tab.length > 0) {
|
||||
var href = $tab.attr("href").replace(/.*(?=#[^\s]+$)/, "");
|
||||
var _index = href.replace("#tab-" + tabsetId2 + "-", "");
|
||||
@@ -3116,9 +3127,9 @@
|
||||
}
|
||||
});
|
||||
function ensureTabsetHasVisibleTab($tabset) {
|
||||
if ($tabset.find("li.active").not(".dropdown").length === 0) {
|
||||
var inputBinding = $tabset.data("shiny-input-binding");
|
||||
if (!inputBinding.getValue($tabset)) {
|
||||
var destTabValue = getFirstTab($tabset);
|
||||
var inputBinding = $tabset.data("shiny-input-binding");
|
||||
var evt = jQuery.Event("shiny:updateinput");
|
||||
evt.binding = inputBinding;
|
||||
$tabset.trigger(evt);
|
||||
@@ -4719,7 +4730,7 @@
|
||||
if (!restyle) {
|
||||
$head.append(links);
|
||||
} else {
|
||||
var refreshStyle = function refreshStyle2(href2, oldSheet) {
|
||||
var refreshStyle = function refreshStyle2(href2, oldSheet, onload) {
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open("GET", href2);
|
||||
xhr.onload = function() {
|
||||
@@ -4733,6 +4744,7 @@
|
||||
setTimeout(function() {
|
||||
return removeSheet(oldSheet);
|
||||
}, 500);
|
||||
onload();
|
||||
};
|
||||
xhr.send();
|
||||
};
|
||||
@@ -4757,21 +4769,29 @@
|
||||
var oldSheet = findSheet(link.attr("href"));
|
||||
var href2 = link.attr("href") + "?restyle=" + new Date().getTime();
|
||||
if (isIE()) {
|
||||
refreshStyle(href2, oldSheet);
|
||||
refreshStyle(href2, oldSheet, scheduleCssReporter);
|
||||
} else {
|
||||
link.attr("href", href2);
|
||||
link.attr("onload", function() {
|
||||
setTimeout(function() {
|
||||
return removeSheet(oldSheet);
|
||||
}, 500);
|
||||
var dummy_id = "dummy-" + Math.floor(Math.random() * 99999999);
|
||||
var css_string = "#" + dummy_id + " { color: #" + Math.floor(Math.random() * 16777215).toString(16) + "; transition: 0.2s all; visibility: hidden; position: fixed; top: 0; left: 0; }";
|
||||
var base64_css_string = "data:text/css;base64," + btoa(css_string);
|
||||
var dummy_link = document.createElement("link");
|
||||
dummy_link.rel = "stylesheet";
|
||||
dummy_link.type = "text/css";
|
||||
dummy_link.href = base64_css_string;
|
||||
var $dummy_el = import_jquery6.default("<div id=" + dummy_id + "></div>");
|
||||
import_jquery6.default(document.body).append($dummy_el);
|
||||
$dummy_el.one("transitionend", function() {
|
||||
sendImageSize();
|
||||
removeSheet(oldSheet);
|
||||
$dummy_el.remove();
|
||||
});
|
||||
$head.append(dummy_link);
|
||||
});
|
||||
$head.append(link);
|
||||
}
|
||||
});
|
||||
var bindDebouncer = new Debouncer(null, Shiny.bindAll, 100);
|
||||
setTimeout(function() {
|
||||
return bindDebouncer.normalCall();
|
||||
}, 100);
|
||||
}
|
||||
}
|
||||
if (dep.script && !restyle) {
|
||||
@@ -6084,7 +6104,7 @@
|
||||
return import_jquery6.default(scope).find("ul.nav.shiny-tab-input");
|
||||
},
|
||||
getValue: function getValue(el) {
|
||||
var anchor = import_jquery6.default(el).find("li:not(.dropdown).active").children("a");
|
||||
var anchor = isBS3() ? import_jquery6.default(el).find("li:not(.dropdown).active > a") : import_jquery6.default(el).find(".nav-link:not(.dropdown-toggle).active, .dropdown-menu > .dropdown-item.active");
|
||||
if (anchor.length === 1)
|
||||
return this._getTabName(anchor);
|
||||
return null;
|
||||
@@ -6093,7 +6113,7 @@
|
||||
var self2 = this;
|
||||
var success = false;
|
||||
if (value) {
|
||||
var anchors = import_jquery6.default(el).find("li:not(.dropdown)").children("a");
|
||||
var anchors = isBS3() ? import_jquery6.default(el).find("li:not(.dropdown) > a") : import_jquery6.default(el).find(".nav-link:not(.dropdown-toggle), .dropdown-menu > .dropdown-item");
|
||||
anchors.each(function() {
|
||||
if (self2._getTabName(import_jquery6.default(this)) === value) {
|
||||
import_jquery6.default(this).tab("show");
|
||||
@@ -6429,6 +6449,7 @@
|
||||
}
|
||||
});
|
||||
inputBindings.register(fileInputBinding, "shiny.fileInputBinding");
|
||||
var sendImageSize;
|
||||
function initShiny() {
|
||||
var shinyapp = Shiny.shinyapp = new ShinyApp();
|
||||
function bindOutputs() {
|
||||
@@ -6753,9 +6774,9 @@
|
||||
});
|
||||
}
|
||||
var sendImageSizeDebouncer = new Debouncer(null, doSendImageSize, 0);
|
||||
function sendImageSize() {
|
||||
sendImageSize = function sendImageSize() {
|
||||
sendImageSizeDebouncer.normalCall();
|
||||
}
|
||||
};
|
||||
inputBatchSender.lastChanceCallback.push(function() {
|
||||
if (sendImageSizeDebouncer.isPending())
|
||||
sendImageSizeDebouncer.immediateCall();
|
||||
@@ -7259,28 +7280,68 @@
|
||||
}, {unsafe: true});
|
||||
}
|
||||
|
||||
// node_modules/core-js/modules/es.string.replace.js
|
||||
// node_modules/core-js/modules/es.string.match.js
|
||||
"use strict";
|
||||
var fixRegExpWellKnownSymbolLogic = require_fix_regexp_well_known_symbol_logic();
|
||||
var anObject2 = require_an_object();
|
||||
var toLength4 = require_to_length();
|
||||
var toInteger2 = require_to_integer();
|
||||
var requireObjectCoercible = require_require_object_coercible();
|
||||
var advanceStringIndex = require_advance_string_index();
|
||||
var getSubstitution = require_get_substitution();
|
||||
var regExpExec = require_regexp_exec_abstract();
|
||||
fixRegExpWellKnownSymbolLogic("match", 1, function(MATCH, nativeMatch, maybeCallNative) {
|
||||
return [
|
||||
function match(regexp) {
|
||||
var O = requireObjectCoercible(this);
|
||||
var matcher = regexp == void 0 ? void 0 : regexp[MATCH];
|
||||
return matcher !== void 0 ? matcher.call(regexp, O) : new RegExp(regexp)[MATCH](String(O));
|
||||
},
|
||||
function(regexp) {
|
||||
var res = maybeCallNative(nativeMatch, regexp, this);
|
||||
if (res.done)
|
||||
return res.value;
|
||||
var rx = anObject2(regexp);
|
||||
var S = String(this);
|
||||
if (!rx.global)
|
||||
return regExpExec(rx, S);
|
||||
var fullUnicode = rx.unicode;
|
||||
rx.lastIndex = 0;
|
||||
var A = [];
|
||||
var n = 0;
|
||||
var result;
|
||||
while ((result = regExpExec(rx, S)) !== null) {
|
||||
var matchStr = String(result[0]);
|
||||
A[n] = matchStr;
|
||||
if (matchStr === "")
|
||||
rx.lastIndex = advanceStringIndex(S, toLength4(rx.lastIndex), fullUnicode);
|
||||
n++;
|
||||
}
|
||||
return n === 0 ? null : A;
|
||||
}
|
||||
];
|
||||
});
|
||||
|
||||
// node_modules/core-js/modules/es.string.replace.js
|
||||
"use strict";
|
||||
var fixRegExpWellKnownSymbolLogic2 = require_fix_regexp_well_known_symbol_logic();
|
||||
var anObject3 = require_an_object();
|
||||
var toLength5 = require_to_length();
|
||||
var toInteger2 = require_to_integer();
|
||||
var requireObjectCoercible2 = require_require_object_coercible();
|
||||
var advanceStringIndex2 = require_advance_string_index();
|
||||
var getSubstitution = require_get_substitution();
|
||||
var regExpExec2 = require_regexp_exec_abstract();
|
||||
var max3 = Math.max;
|
||||
var min2 = Math.min;
|
||||
var maybeToString = function(it) {
|
||||
return it === void 0 ? it : String(it);
|
||||
};
|
||||
fixRegExpWellKnownSymbolLogic("replace", 2, function(REPLACE, nativeReplace, maybeCallNative, reason) {
|
||||
fixRegExpWellKnownSymbolLogic2("replace", 2, function(REPLACE, nativeReplace, maybeCallNative, reason) {
|
||||
var REGEXP_REPLACE_SUBSTITUTES_UNDEFINED_CAPTURE = reason.REGEXP_REPLACE_SUBSTITUTES_UNDEFINED_CAPTURE;
|
||||
var REPLACE_KEEPS_$0 = reason.REPLACE_KEEPS_$0;
|
||||
var UNSAFE_SUBSTITUTE = REGEXP_REPLACE_SUBSTITUTES_UNDEFINED_CAPTURE ? "$" : "$0";
|
||||
return [
|
||||
function replace(searchValue, replaceValue) {
|
||||
var O = requireObjectCoercible(this);
|
||||
var O = requireObjectCoercible2(this);
|
||||
var replacer = searchValue == void 0 ? void 0 : searchValue[REPLACE];
|
||||
return replacer !== void 0 ? replacer.call(searchValue, O, replaceValue) : nativeReplace.call(String(O), searchValue, replaceValue);
|
||||
},
|
||||
@@ -7290,7 +7351,7 @@
|
||||
if (res.done)
|
||||
return res.value;
|
||||
}
|
||||
var rx = anObject2(regexp);
|
||||
var rx = anObject3(regexp);
|
||||
var S = String(this);
|
||||
var functionalReplace = typeof replaceValue === "function";
|
||||
if (!functionalReplace)
|
||||
@@ -7302,7 +7363,7 @@
|
||||
}
|
||||
var results = [];
|
||||
while (true) {
|
||||
var result = regExpExec(rx, S);
|
||||
var result = regExpExec2(rx, S);
|
||||
if (result === null)
|
||||
break;
|
||||
results.push(result);
|
||||
@@ -7310,7 +7371,7 @@
|
||||
break;
|
||||
var matchStr = String(result[0]);
|
||||
if (matchStr === "")
|
||||
rx.lastIndex = advanceStringIndex(S, toLength4(rx.lastIndex), fullUnicode);
|
||||
rx.lastIndex = advanceStringIndex2(S, toLength5(rx.lastIndex), fullUnicode);
|
||||
}
|
||||
var accumulatedResult = "";
|
||||
var nextSourcePosition = 0;
|
||||
@@ -7342,13 +7403,13 @@
|
||||
|
||||
// node_modules/core-js/modules/es.string.split.js
|
||||
"use strict";
|
||||
var fixRegExpWellKnownSymbolLogic2 = require_fix_regexp_well_known_symbol_logic();
|
||||
var fixRegExpWellKnownSymbolLogic3 = require_fix_regexp_well_known_symbol_logic();
|
||||
var isRegExp = require_is_regexp();
|
||||
var anObject3 = require_an_object();
|
||||
var requireObjectCoercible2 = require_require_object_coercible();
|
||||
var anObject4 = require_an_object();
|
||||
var requireObjectCoercible3 = require_require_object_coercible();
|
||||
var speciesConstructor = require_species_constructor();
|
||||
var advanceStringIndex2 = require_advance_string_index();
|
||||
var toLength5 = require_to_length();
|
||||
var advanceStringIndex3 = require_advance_string_index();
|
||||
var toLength6 = require_to_length();
|
||||
var callRegExpExec = require_regexp_exec_abstract();
|
||||
var regexpExec = require_regexp_exec();
|
||||
var fails5 = require_fails();
|
||||
@@ -7358,11 +7419,11 @@
|
||||
var SUPPORTS_Y = !fails5(function() {
|
||||
return !RegExp(MAX_UINT32, "y");
|
||||
});
|
||||
fixRegExpWellKnownSymbolLogic2("split", 2, function(SPLIT, nativeSplit, maybeCallNative) {
|
||||
fixRegExpWellKnownSymbolLogic3("split", 2, function(SPLIT, nativeSplit, maybeCallNative) {
|
||||
var internalSplit;
|
||||
if ("abbc".split(/(b)*/)[1] == "c" || "test".split(/(?:)/, -1).length != 4 || "ab".split(/(?:ab)*/).length != 2 || ".".split(/(.?)(.?)/).length != 4 || ".".split(/()()/).length > 1 || "".split(/.?/).length) {
|
||||
internalSplit = function(separator, limit) {
|
||||
var string = String(requireObjectCoercible2(this));
|
||||
var string = String(requireObjectCoercible3(this));
|
||||
var lim = limit === void 0 ? MAX_UINT32 : limit >>> 0;
|
||||
if (lim === 0)
|
||||
return [];
|
||||
@@ -7405,7 +7466,7 @@
|
||||
internalSplit = nativeSplit;
|
||||
return [
|
||||
function split(separator, limit) {
|
||||
var O = requireObjectCoercible2(this);
|
||||
var O = requireObjectCoercible3(this);
|
||||
var splitter = separator == void 0 ? void 0 : separator[SPLIT];
|
||||
return splitter !== void 0 ? splitter.call(separator, O, limit) : internalSplit.call(String(O), separator, limit);
|
||||
},
|
||||
@@ -7413,7 +7474,7 @@
|
||||
var res = maybeCallNative(internalSplit, regexp, this, limit, internalSplit !== nativeSplit);
|
||||
if (res.done)
|
||||
return res.value;
|
||||
var rx = anObject3(regexp);
|
||||
var rx = anObject4(regexp);
|
||||
var S = String(this);
|
||||
var C = speciesConstructor(rx, RegExp);
|
||||
var unicodeMatching = rx.unicode;
|
||||
@@ -7431,8 +7492,8 @@
|
||||
splitter.lastIndex = SUPPORTS_Y ? q : 0;
|
||||
var z = callRegExpExec(splitter, SUPPORTS_Y ? S : S.slice(q));
|
||||
var e;
|
||||
if (z === null || (e = min3(toLength5(splitter.lastIndex + (SUPPORTS_Y ? 0 : q)), S.length)) === p) {
|
||||
q = advanceStringIndex2(S, q, unicodeMatching);
|
||||
if (z === null || (e = min3(toLength6(splitter.lastIndex + (SUPPORTS_Y ? 0 : q)), S.length)) === p) {
|
||||
q = advanceStringIndex3(S, q, unicodeMatching);
|
||||
} else {
|
||||
A.push(S.slice(p, q));
|
||||
if (A.length === lim)
|
||||
@@ -7725,6 +7786,12 @@
|
||||
el.removeChild(div);
|
||||
return linkColor;
|
||||
}
|
||||
function isBS3() {
|
||||
if (!import_jquery5.default.fn.tab) {
|
||||
return false;
|
||||
}
|
||||
return import_jquery5.default.fn.tab.Constructor.VERSION.match(/^3\./);
|
||||
}
|
||||
|
||||
// src/shiny.ts
|
||||
var Shiny;
|
||||
@@ -7855,9 +7922,9 @@
|
||||
var $22 = require_export();
|
||||
var fails6 = require_fails();
|
||||
var ArrayBufferModule = require_array_buffer();
|
||||
var anObject4 = require_an_object();
|
||||
var anObject5 = require_an_object();
|
||||
var toAbsoluteIndex3 = require_to_absolute_index();
|
||||
var toLength6 = require_to_length();
|
||||
var toLength7 = require_to_length();
|
||||
var speciesConstructor2 = require_species_constructor();
|
||||
var ArrayBuffer3 = ArrayBufferModule.ArrayBuffer;
|
||||
var DataView2 = ArrayBufferModule.DataView;
|
||||
@@ -7868,12 +7935,12 @@
|
||||
$22({target: "ArrayBuffer", proto: true, unsafe: true, forced: INCORRECT_SLICE}, {
|
||||
slice: function slice2(start, end) {
|
||||
if (nativeArrayBufferSlice !== void 0 && end === void 0) {
|
||||
return nativeArrayBufferSlice.call(anObject4(this), start);
|
||||
return nativeArrayBufferSlice.call(anObject5(this), start);
|
||||
}
|
||||
var length = anObject4(this).byteLength;
|
||||
var length = anObject5(this).byteLength;
|
||||
var first = toAbsoluteIndex3(start, length);
|
||||
var fin = toAbsoluteIndex3(end === void 0 ? length : end, length);
|
||||
var result = new (speciesConstructor2(this, ArrayBuffer3))(toLength6(fin - first));
|
||||
var result = new (speciesConstructor2(this, ArrayBuffer3))(toLength7(fin - first));
|
||||
var viewSource = new DataView2(this);
|
||||
var viewTarget = new DataView2(result);
|
||||
var index = 0;
|
||||
@@ -7991,46 +8058,6 @@
|
||||
var j;
|
||||
var key;
|
||||
|
||||
// node_modules/core-js/modules/es.string.match.js
|
||||
"use strict";
|
||||
var fixRegExpWellKnownSymbolLogic3 = require_fix_regexp_well_known_symbol_logic();
|
||||
var anObject5 = require_an_object();
|
||||
var toLength7 = require_to_length();
|
||||
var requireObjectCoercible3 = require_require_object_coercible();
|
||||
var advanceStringIndex3 = require_advance_string_index();
|
||||
var regExpExec2 = require_regexp_exec_abstract();
|
||||
fixRegExpWellKnownSymbolLogic3("match", 1, function(MATCH, nativeMatch, maybeCallNative) {
|
||||
return [
|
||||
function match(regexp) {
|
||||
var O = requireObjectCoercible3(this);
|
||||
var matcher = regexp == void 0 ? void 0 : regexp[MATCH];
|
||||
return matcher !== void 0 ? matcher.call(regexp, O) : new RegExp(regexp)[MATCH](String(O));
|
||||
},
|
||||
function(regexp) {
|
||||
var res = maybeCallNative(nativeMatch, regexp, this);
|
||||
if (res.done)
|
||||
return res.value;
|
||||
var rx = anObject5(regexp);
|
||||
var S = String(this);
|
||||
if (!rx.global)
|
||||
return regExpExec2(rx, S);
|
||||
var fullUnicode = rx.unicode;
|
||||
rx.lastIndex = 0;
|
||||
var A = [];
|
||||
var n = 0;
|
||||
var result;
|
||||
while ((result = regExpExec2(rx, S)) !== null) {
|
||||
var matchStr = String(result[0]);
|
||||
A[n] = matchStr;
|
||||
if (matchStr === "")
|
||||
rx.lastIndex = advanceStringIndex3(S, toLength7(rx.lastIndex), fullUnicode);
|
||||
n++;
|
||||
}
|
||||
return n === 0 ? null : A;
|
||||
}
|
||||
];
|
||||
});
|
||||
|
||||
// node_modules/core-js/modules/es.string.search.js
|
||||
"use strict";
|
||||
var fixRegExpWellKnownSymbolLogic4 = require_fix_regexp_well_known_symbol_logic();
|
||||
|
||||
File diff suppressed because one or more lines are too long
2
inst/www/shared/shiny.min.js
vendored
2
inst/www/shared/shiny.min.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -28,8 +28,10 @@ navbarMenu(title, ..., menuName = title, icon = NULL)
|
||||
\arguments{
|
||||
\item{title}{The title to display in the navbar}
|
||||
|
||||
\item{...}{\code{\link[=tabPanel]{tabPanel()}} elements to include in the navbar.
|
||||
Plain strings will be converted to headers.}
|
||||
\item{...}{\code{\link[=tabPanel]{tabPanel()}} elements to include in the page. The
|
||||
\code{navbarMenu} function also accepts strings, which will be used as menu
|
||||
section headers. If the string is a set of dashes like \code{"----"} a
|
||||
horizontal separator will be displayed in the menu.}
|
||||
|
||||
\item{id}{If provided, you can use \verb{input$}\emph{\code{id}} in your
|
||||
server logic to determine which of the current tabs is active. The value
|
||||
@@ -63,7 +65,7 @@ elements into a menu when the width of the browser is less than 940 pixels
|
||||
|
||||
\item{collapsable}{Deprecated; use \code{collapsible} instead.}
|
||||
|
||||
\item{fluid}{\code{TRUE} to use fluid layout; \code{FALSE} to use fixed
|
||||
\item{fluid}{\code{TRUE} to use a fluid layout. \code{FALSE} to use a fixed
|
||||
layout.}
|
||||
|
||||
\item{responsive}{This option is deprecated; it is no longer optional with
|
||||
@@ -91,11 +93,17 @@ is needed if you want to insert/remove or show/hide an entire
|
||||
|
||||
\item{icon}{Optional icon to appear on a \code{navbarMenu} tab.}
|
||||
}
|
||||
\value{
|
||||
A UI defintion that can be passed to the \link{shinyUI} function.
|
||||
}
|
||||
\description{
|
||||
Create a page that contains a top level navigation bar that can be used to
|
||||
toggle a set of \code{\link[=tabPanel]{tabPanel()}} elements. \code{navbarMenu()} can be used to create
|
||||
an embedded menu within the navbar that in turns includes additional
|
||||
\code{tabPanels}.
|
||||
toggle a set of \code{\link[=tabPanel]{tabPanel()}} elements.
|
||||
}
|
||||
\details{
|
||||
The \code{navbarMenu} function can be used to create an embedded
|
||||
menu within the navbar that in turns includes additional tabPanels (see
|
||||
example below).
|
||||
}
|
||||
\examples{
|
||||
navbarPage("App Title",
|
||||
@@ -115,7 +123,9 @@ navbarPage("App Title",
|
||||
)
|
||||
}
|
||||
\seealso{
|
||||
\code{\link[=updateNavbarPage]{updateNavbarPage()}}, \code{\link[=insertTab]{insertTab()}}, \code{\link[=showTab]{showTab()}}
|
||||
\code{\link[=tabPanel]{tabPanel()}}, \code{\link[=tabsetPanel]{tabsetPanel()}},
|
||||
\code{\link[=updateNavbarPage]{updateNavbarPage()}}, \code{\link[=insertTab]{insertTab()}},
|
||||
\code{\link[=showTab]{showTab()}}
|
||||
|
||||
Other layout functions:
|
||||
\code{\link{fillPage}()},
|
||||
@@ -125,11 +135,5 @@ Other layout functions:
|
||||
\code{\link{sidebarLayout}()},
|
||||
\code{\link{splitLayout}()},
|
||||
\code{\link{verticalLayout}()}
|
||||
|
||||
Other tab layouts:
|
||||
\code{\link{navlistPanel}()},
|
||||
\code{\link{tabPanel}()},
|
||||
\code{\link{tabsetPanel}()}
|
||||
}
|
||||
\concept{layout functions}
|
||||
\concept{tab layouts}
|
||||
|
||||
@@ -8,23 +8,30 @@ navlistPanel(
|
||||
...,
|
||||
id = NULL,
|
||||
selected = NULL,
|
||||
header = NULL,
|
||||
footer = NULL,
|
||||
well = TRUE,
|
||||
fluid = TRUE,
|
||||
widths = c(4, 8)
|
||||
)
|
||||
}
|
||||
\arguments{
|
||||
\item{...}{\code{\link[=tabPanel]{tabPanel()}} elements to include in the navbar.
|
||||
Plain strings will be converted to headers.}
|
||||
\item{...}{\code{\link[=tabPanel]{tabPanel()}} elements to include in the navlist}
|
||||
|
||||
\item{id}{If provided, you can use \verb{input$}\emph{\code{id}} in your
|
||||
server logic to determine which of the current tabs is active. The value
|
||||
will correspond to the \code{value} argument that is passed to
|
||||
server logic to determine which of the current navlist items is active. The
|
||||
value will correspond to the \code{value} argument that is passed to
|
||||
\code{\link[=tabPanel]{tabPanel()}}.}
|
||||
|
||||
\item{selected}{The \code{value} (or, if none was supplied, the \code{title})
|
||||
of the tab that should be selected by default. If \code{NULL}, the first
|
||||
tab will be selected.}
|
||||
of the navigation item that should be selected by default. If \code{NULL},
|
||||
the first navigation will be selected.}
|
||||
|
||||
\item{header}{Tag or list of tags to display as a common header above all
|
||||
tabPanels.}
|
||||
|
||||
\item{footer}{Tag or list of tags to display as a common footer below all
|
||||
tabPanels}
|
||||
|
||||
\item{well}{\code{TRUE} to place a well (gray rounded rectangle) around the
|
||||
navigation list.}
|
||||
@@ -40,7 +47,11 @@ Create a navigation list panel that provides a list of links on the left
|
||||
which navigate to a set of tabPanels displayed to the right.
|
||||
}
|
||||
\details{
|
||||
|
||||
You can include headers within the \code{navlistPanel} by including
|
||||
plain text elements in the list. Versions of Shiny before 0.11 supported
|
||||
separators with "------", but as of 0.11, separators were no longer
|
||||
supported. This is because version 0.11 switched to Bootstrap 3, which
|
||||
doesn't support separators.
|
||||
}
|
||||
\examples{
|
||||
fluidPage(
|
||||
@@ -56,11 +67,6 @@ fluidPage(
|
||||
)
|
||||
}
|
||||
\seealso{
|
||||
\code{\link[=updateTabsetPanel]{updateTabsetPanel()}}, \code{\link[=insertTab]{insertTab()}}, \code{\link[=showTab]{showTab()}}
|
||||
|
||||
Other tab layouts:
|
||||
\code{\link{navbarPage}()},
|
||||
\code{\link{tabPanel}()},
|
||||
\code{\link{tabsetPanel}()}
|
||||
\code{\link[=tabPanel]{tabPanel()}}, \code{\link[=updateNavlistPanel]{updateNavlistPanel()}},
|
||||
\code{\link[=insertTab]{insertTab()}}, \code{\link[=showTab]{showTab()}}
|
||||
}
|
||||
\concept{tab layouts}
|
||||
|
||||
@@ -21,12 +21,20 @@ that this tab is selected. If omitted and \code{tabsetPanel} has an
|
||||
\item{icon}{Optional icon to appear on the tab. This attribute is only
|
||||
valid when using a \code{tabPanel} within a \code{\link[=navbarPage]{navbarPage()}}.}
|
||||
}
|
||||
\description{
|
||||
\code{tabPanel()} creates a tab panel that can be included within a
|
||||
\code{\link[=tabsetPanel]{tabsetPanel()}}, \code{\link[=navListPanel]{navListPanel()}}, or \code{\link[=navbarPage]{navbarPage()}}. \code{tabPanelBody()}
|
||||
drops the \code{title}, making it most suitable for use within
|
||||
\code{tabsetPanel(type = "hidden")}.
|
||||
\value{
|
||||
A tab that can be passed to \code{\link[=tabsetPanel]{tabsetPanel()}}
|
||||
}
|
||||
\description{
|
||||
Create a tab panel
|
||||
}
|
||||
\section{Functions}{
|
||||
\itemize{
|
||||
\item \code{tabPanel}: Create a tab panel that can be included within a \code{\link[=tabsetPanel]{tabsetPanel()}} or a \code{\link[=navbarPage]{navbarPage()}}.
|
||||
|
||||
\item \code{tabPanelBody}: Create a tab panel that drops the title argument.
|
||||
This function should be used within \code{tabsetPanel(type = "hidden")}. See \code{\link[=tabsetPanel]{tabsetPanel()}} for example usage.
|
||||
}}
|
||||
|
||||
\examples{
|
||||
# Show a tabset that includes a plot, summary, and
|
||||
# table view of the generated distribution
|
||||
@@ -39,9 +47,5 @@ mainPanel(
|
||||
)
|
||||
}
|
||||
\seealso{
|
||||
Other tab layouts:
|
||||
\code{\link{navbarPage}()},
|
||||
\code{\link{navlistPanel}()},
|
||||
\code{\link{tabsetPanel}()}
|
||||
\code{\link[=tabsetPanel]{tabsetPanel()}}
|
||||
}
|
||||
\concept{tab layouts}
|
||||
|
||||
@@ -9,6 +9,8 @@ tabsetPanel(
|
||||
id = NULL,
|
||||
selected = NULL,
|
||||
type = c("tabs", "pills", "hidden"),
|
||||
header = NULL,
|
||||
footer = NULL,
|
||||
position = deprecated()
|
||||
)
|
||||
}
|
||||
@@ -32,9 +34,18 @@ conjunction with \code{\link[=tabPanelBody]{tabPanelBody()}} and \code{\link[=up
|
||||
active tab via other input controls. (See example below)}
|
||||
}}
|
||||
|
||||
\item{header}{Tag or list of tags to display as a common header above all
|
||||
tabPanels.}
|
||||
|
||||
\item{footer}{Tag or list of tags to display as a common footer below all
|
||||
tabPanels}
|
||||
|
||||
\item{position}{This argument is deprecated; it has been discontinued in
|
||||
Bootstrap 3.}
|
||||
}
|
||||
\value{
|
||||
A tabset that can be passed to \code{\link[=mainPanel]{mainPanel()}}
|
||||
}
|
||||
\description{
|
||||
Create a tabset that contains \code{\link[=tabPanel]{tabPanel()}} elements. Tabsets are
|
||||
useful for dividing output into multiple independently viewable sections.
|
||||
@@ -80,11 +91,6 @@ if (interactive()) {
|
||||
}
|
||||
}
|
||||
\seealso{
|
||||
\code{\link[=updateTabsetPanel]{updateTabsetPanel()}}, \code{\link[=insertTab]{insertTab()}}, \code{\link[=showTab]{showTab()}}
|
||||
|
||||
Other tab layouts:
|
||||
\code{\link{navbarPage}()},
|
||||
\code{\link{navlistPanel}()},
|
||||
\code{\link{tabPanel}()}
|
||||
\code{\link[=tabPanel]{tabPanel()}}, \code{\link[=updateTabsetPanel]{updateTabsetPanel()}},
|
||||
\code{\link[=insertTab]{insertTab()}}, \code{\link[=showTab]{showTab()}}
|
||||
}
|
||||
\concept{tab layouts}
|
||||
|
||||
63
srcts/esbuild.external_libs.mjs
Normal file
63
srcts/esbuild.external_libs.mjs
Normal file
@@ -0,0 +1,63 @@
|
||||
import {readdirSync, unlinkSync, writeFileSync} from "fs";
|
||||
import esbuild from "esbuild";
|
||||
import globalsPlugin from "esbuild-plugin-globals";
|
||||
|
||||
// import process from "process";
|
||||
// let watch = process.argv.length >= 3 && process.argv[2] == "--watch";
|
||||
|
||||
let instdir = "../inst/";
|
||||
let outdir = instdir + "/www/shared/";
|
||||
|
||||
let opts = {
|
||||
bundle: false,
|
||||
watch: false,
|
||||
target: "es5",
|
||||
sourcemap: false,
|
||||
};
|
||||
|
||||
console.log("Building datepicker");
|
||||
const locale_files = readdirSync(instdir + "www/shared/datepicker/js/locales/")
|
||||
|
||||
let require_files = locale_files.map(function(filename) {
|
||||
return `require("./locales/${ filename }");`;
|
||||
}).join("\n");
|
||||
|
||||
let tmpfile = instdir + "www/shared/datepicker/js/temp.js";
|
||||
writeFileSync(tmpfile,
|
||||
`require("./bootstrap-datepicker.js");
|
||||
${require_files}`)
|
||||
await esbuild.build({
|
||||
...opts,
|
||||
plugins:[
|
||||
globalsPlugin({
|
||||
jquery: "window.jQuery",
|
||||
})
|
||||
],
|
||||
bundle: true,
|
||||
entryPoints: [tmpfile],
|
||||
outfile: instdir + "www/shared/datepicker/js/bootstrap-datepicker.min.js",
|
||||
external: ['jquery'],
|
||||
minify: true,
|
||||
});
|
||||
// Clean up
|
||||
unlinkSync(tmpfile);
|
||||
|
||||
console.log("Building ionrangeslider");
|
||||
await esbuild.build({
|
||||
...opts,
|
||||
entryPoints: [
|
||||
instdir + "www/shared/ionrangeslider/js/ion.rangeSlider.js"
|
||||
],
|
||||
outfile: instdir + "www/shared/ionrangeslider/js/ion.rangeSlider.min.js",
|
||||
minify: true,
|
||||
});
|
||||
|
||||
console.log("Building selectize");
|
||||
await esbuild.build({
|
||||
...opts,
|
||||
entryPoints: [
|
||||
instdir + "www/shared/selectize/accessibility/js/selectize-plugin-a11y.js"
|
||||
],
|
||||
outfile: instdir + "www/shared/selectize/accessibility/js/selectize-plugin-a11y.min.js",
|
||||
minify: true,
|
||||
});
|
||||
@@ -42,10 +42,11 @@
|
||||
},
|
||||
"scripts": {
|
||||
"watch": "yarn run build_shiny --watch",
|
||||
"build": "yarn run build_shiny",
|
||||
"build": "yarn run build_shiny && yarn run bundle_external_libs",
|
||||
"setup_build_shiny": "yarn run lint && yarn run typescript-check",
|
||||
"build_shiny": "yarn run setup_build_shiny && yarn run bundle_shiny",
|
||||
"bundle_shiny": "node esbuild.config.mjs",
|
||||
"bundle_external_libs": "node esbuild.external_libs.mjs",
|
||||
"bundle_shiny_parcel2": "parcel build -d ../inst/www/shared --no-minify -o shiny.js src/index.ts",
|
||||
"watch_parcel2": "yarn run setup_build_shiny && parcel run -d ../inst/www/shared -o shiny.js srcjs/index.ts",
|
||||
"replace_shiny_version2": "replace --silent '\"[^\"]+\"; // @VERSION@' \"\\\"`node -e 'console.log(require(\"readcontrol\").readSync(\"../DESCRIPTION\").version)'`\\\"; // @VERSION@\" src/shiny.ts",
|
||||
|
||||
@@ -34,6 +34,7 @@ import {
|
||||
updateLabel,
|
||||
getComputedLinkColor,
|
||||
makeBlob,
|
||||
isBS3,
|
||||
} from "./utils";
|
||||
|
||||
import { isQt, isIE, IEVersion } from "./utils/browser";
|
||||
@@ -1241,8 +1242,10 @@ function main(): void {
|
||||
function getTargetTabs($tabset, $tabContent, target) {
|
||||
const dataValue = "[data-value='" + $escape(target) + "']";
|
||||
const $aTag = $tabset.find("a" + dataValue);
|
||||
const $liTag = $aTag.parent();
|
||||
let $liTag = $aTag.parent("li");
|
||||
// BS3 dropdown anchors are wrapped in <li>, but they can't be in BS4
|
||||
|
||||
if ($liTag.length === 0) $liTag = $aTag;
|
||||
if ($liTag.length === 0) {
|
||||
throw (
|
||||
"There is no tabPanel (or navbarMenu) with value" +
|
||||
@@ -1259,10 +1262,12 @@ function main(): void {
|
||||
const $dropdownTabset = $aTag.find("+ ul.dropdown-menu");
|
||||
const dropdownId = $dropdownTabset.attr("data-tabsetid");
|
||||
|
||||
const $dropdownLiTags = $dropdownTabset
|
||||
.find("a[data-toggle='tab']")
|
||||
.parent("li");
|
||||
let $dropdownLiTags = $dropdownTabset.find("a[data-toggle='tab']");
|
||||
// BS3 dropdown anchors are wrapped in <li>, but they can't be in BS4
|
||||
|
||||
if ($dropdownLiTags.parent("li").length > 0) {
|
||||
$dropdownLiTags = $dropdownLiTags.parent("li");
|
||||
}
|
||||
$dropdownLiTags.each(function (i, el) {
|
||||
$liTags.push($(el));
|
||||
});
|
||||
@@ -1286,7 +1291,7 @@ function main(): void {
|
||||
let tabsetId = $parentTabset.attr("data-tabsetid");
|
||||
|
||||
const $divTag = $(message.divTag.html);
|
||||
const $liTag = $(message.liTag.html);
|
||||
let $liTag = $(message.liTag.html);
|
||||
const $aTag = $liTag.find("> a");
|
||||
|
||||
// Unless the item is being prepended/appended, the target tab
|
||||
@@ -1297,6 +1302,12 @@ function main(): void {
|
||||
if (message.target !== null) {
|
||||
target = getTargetTabs($tabset, $tabContent, message.target);
|
||||
$targetLiTag = target.$liTag;
|
||||
// If the target is a (BS4) .dropdown-item, then we can't insert
|
||||
// <li class='nav-item'><a class='nav-link'>...</a></li>,
|
||||
// instead, we need <a class='dropdown-item'>...</a>
|
||||
if ($targetLiTag.hasClass("dropdown-item")) {
|
||||
$liTag = $aTag.removeClass("nav-link").addClass("dropdown-item");
|
||||
}
|
||||
}
|
||||
|
||||
// If the item is to be placed inside a navbarMenu (dropdown),
|
||||
@@ -1321,7 +1332,11 @@ function main(): void {
|
||||
const index = getTabIndex($tabset, tabsetId);
|
||||
const tabId = "tab-" + tabsetId + "-" + index;
|
||||
|
||||
$liTag.find("> a").attr("href", "#" + tabId);
|
||||
let anchor = $liTag.find("> a");
|
||||
// BS3 dropdown anchors are wrapped in <li>, but they can't be in BS4
|
||||
|
||||
if (anchor.length === 0) anchor = $liTag;
|
||||
anchor.attr("href", "#" + tabId);
|
||||
$divTag.attr("id", tabId);
|
||||
}
|
||||
|
||||
@@ -1411,8 +1426,8 @@ function main(): void {
|
||||
// loop through all existing tabs, find the one with highest id
|
||||
// (since this is based on a numeric counter), and increment
|
||||
|
||||
$tabset.find("> li").each(function () {
|
||||
const $tab = $(this).find("> a[data-toggle='tab']");
|
||||
$tabset.find("a[data-toggle='tab']").each(function () {
|
||||
const $tab = $(this);
|
||||
|
||||
if ($tab.length > 0) {
|
||||
// remove leading url if it exists. (copy of bootstrap url stripper)
|
||||
@@ -1465,13 +1480,16 @@ function main(): void {
|
||||
|
||||
// If the given tabset has no active tabs, select the first one
|
||||
function ensureTabsetHasVisibleTab($tabset) {
|
||||
if ($tabset.find("li.active").not(".dropdown").length === 0) {
|
||||
const inputBinding = $tabset.data("shiny-input-binding");
|
||||
|
||||
// Use the getValue() method to avoid duplicating the CSS selector
|
||||
// for querying the DOM for the currently active tab
|
||||
if (!inputBinding.getValue($tabset)) {
|
||||
// Note: destTabValue may be null. We still want to proceed
|
||||
// through the below logic and setValue so that the input
|
||||
// value for the tabset gets updated (i.e. input$tabsetId
|
||||
// should be null if there are no tabs).
|
||||
const destTabValue = getFirstTab($tabset);
|
||||
const inputBinding = $tabset.data("shiny-input-binding");
|
||||
const evt = jQuery.Event("shiny:updateinput");
|
||||
|
||||
evt.binding = inputBinding;
|
||||
@@ -4022,7 +4040,7 @@ function main(): void {
|
||||
$head.append(links);
|
||||
} else {
|
||||
// This inline <style> based approach works for IE11
|
||||
let refreshStyle = function (href, oldSheet) {
|
||||
let refreshStyle = function (href, oldSheet, onload) {
|
||||
const xhr = new XMLHttpRequest();
|
||||
|
||||
xhr.open("GET", href);
|
||||
@@ -4035,6 +4053,7 @@ function main(): void {
|
||||
$head.append(newStyle);
|
||||
setTimeout(() => oldStyle.remove(), 500);
|
||||
setTimeout(() => removeSheet(oldSheet), 500);
|
||||
onload();
|
||||
};
|
||||
xhr.send();
|
||||
};
|
||||
@@ -4071,26 +4090,43 @@ function main(): void {
|
||||
// <link> -based approach
|
||||
|
||||
if (isIE()) {
|
||||
refreshStyle(href, oldSheet);
|
||||
refreshStyle(href, oldSheet, scheduleCssReporter);
|
||||
} else {
|
||||
link.attr("href", href);
|
||||
// Once the new <link> is loaded, schedule the old <link> to be removed
|
||||
// on the next tick which is needed to avoid FOUC
|
||||
link.attr("onload", () => {
|
||||
setTimeout(() => removeSheet(oldSheet), 500);
|
||||
const dummy_id = "dummy-" + Math.floor(Math.random() * 99999999);
|
||||
const css_string =
|
||||
"#" + dummy_id +
|
||||
" { color: #" +
|
||||
Math.floor(Math.random() * 16777215).toString(16) + "; " +
|
||||
"transition: 0.2s all; " +
|
||||
"visibility: hidden; " +
|
||||
"position: fixed; top: 0; left: 0; }";
|
||||
const base64_css_string =
|
||||
"data:text/css;base64," + btoa(css_string);
|
||||
|
||||
let dummy_link = document.createElement("link");
|
||||
|
||||
dummy_link.rel = "stylesheet";
|
||||
dummy_link.type = "text/css";
|
||||
dummy_link.href = base64_css_string;
|
||||
|
||||
let $dummy_el = $("<div id=" + dummy_id + "></div>")
|
||||
$(document.body).append($dummy_el);
|
||||
$dummy_el
|
||||
.one("transitionend", () => {
|
||||
sendImageSize();
|
||||
removeSheet(oldSheet);
|
||||
$dummy_el.remove();
|
||||
});
|
||||
|
||||
$head.append(dummy_link);
|
||||
});
|
||||
$head.append(link);
|
||||
}
|
||||
});
|
||||
|
||||
// Once the new styles are applied, CSS values that are accessible server-side
|
||||
// (e.g., getCurrentOutputInfo(), output visibility, etc) may become outdated.
|
||||
// At the time of writing, that means we need to do sendImageSize() &
|
||||
// sendOutputHiddenState() again, which can be done by re-binding.
|
||||
/* global Shiny */
|
||||
const bindDebouncer = new Debouncer(null, Shiny.bindAll, 100);
|
||||
|
||||
setTimeout(() => bindDebouncer.normalCall(), 100);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5881,7 +5917,12 @@ function main(): void {
|
||||
return $(scope).find("ul.nav.shiny-tab-input");
|
||||
},
|
||||
getValue: function (el) {
|
||||
const anchor = $(el).find("li:not(.dropdown).active").children("a");
|
||||
// prettier-ignore
|
||||
let anchor = isBS3()
|
||||
? $(el).find("li:not(.dropdown).active > a")
|
||||
: $(el).find(
|
||||
".nav-link:not(.dropdown-toggle).active, .dropdown-menu > .dropdown-item.active"
|
||||
);
|
||||
|
||||
if (anchor.length === 1) return this._getTabName(anchor);
|
||||
|
||||
@@ -5892,7 +5933,12 @@ function main(): void {
|
||||
let success = false;
|
||||
|
||||
if (value) {
|
||||
const anchors = $(el).find("li:not(.dropdown)").children("a");
|
||||
// prettier-ignore
|
||||
let anchors = isBS3()
|
||||
? $(el).find("li:not(.dropdown) > a")
|
||||
: $(el).find(
|
||||
".nav-link:not(.dropdown-toggle), .dropdown-menu > .dropdown-item"
|
||||
);
|
||||
|
||||
anchors.each(function () {
|
||||
if (self._getTabName($(this)) === value) {
|
||||
@@ -6361,6 +6407,10 @@ function main(): void {
|
||||
});
|
||||
inputBindings.register(fileInputBinding, "shiny.fileInputBinding");
|
||||
|
||||
// This function gets defined in initShiny() and 'hoisted' so it can be reused
|
||||
// (to send CSS info) inside of Shiny.renderDependencies()
|
||||
let sendImageSize;
|
||||
|
||||
// "init_shiny.js"
|
||||
function initShiny() {
|
||||
const shinyapp = (Shiny.shinyapp = new ShinyApp());
|
||||
@@ -6843,9 +6893,9 @@ function main(): void {
|
||||
}
|
||||
const sendImageSizeDebouncer = new Debouncer(null, doSendImageSize, 0);
|
||||
|
||||
function sendImageSize() {
|
||||
sendImageSize = function () {
|
||||
sendImageSizeDebouncer.normalCall();
|
||||
}
|
||||
};
|
||||
// Make sure sendImageSize actually gets called before the inputBatchSender
|
||||
// sends data to the server.
|
||||
inputBatchSender.lastChanceCallback.push(function () {
|
||||
|
||||
@@ -361,6 +361,17 @@ function getComputedLinkColor(el: HTMLElement): string {
|
||||
return linkColor;
|
||||
}
|
||||
|
||||
function isBS3(): boolean {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
if (!$.fn.tab) {
|
||||
return false;
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
return $.fn.tab.Constructor.VERSION.match(/^3\./);
|
||||
}
|
||||
|
||||
export {
|
||||
escapeHTML,
|
||||
randomId,
|
||||
@@ -384,4 +395,5 @@ export {
|
||||
updateLabel,
|
||||
getComputedLinkColor,
|
||||
makeBlob,
|
||||
isBS3,
|
||||
};
|
||||
|
||||
339
tests/testthat/_snaps/tabPanel.md
Normal file
339
tests/testthat/_snaps/tabPanel.md
Normal file
@@ -0,0 +1,339 @@
|
||||
# tabsetPanel() markup is correct
|
||||
|
||||
Code
|
||||
default
|
||||
Output
|
||||
<div class="tabbable">
|
||||
<ul class="nav nav-tabs" data-tabsetid="4785">
|
||||
<li class="active">
|
||||
<a href="#tab-4785-1" data-toggle="tab" data-value="A">A</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#tab-4785-2" data-toggle="tab" data-value="B">
|
||||
<i class=" fab fa-github fa-fw" role="presentation" aria-label=" icon"></i>
|
||||
B
|
||||
</a>
|
||||
</li>
|
||||
<li class="dropdown">
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown" data-value="Menu">
|
||||
Menu
|
||||
<b class="caret"></b>
|
||||
</a>
|
||||
<ul class="dropdown-menu" data-tabsetid="1502">
|
||||
<li>
|
||||
<a href="#tab-1502-1" data-toggle="tab" data-value="C">C</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="tab-content" data-tabsetid="4785">
|
||||
<div class="tab-pane active" data-value="A" id="tab-4785-1">a</div>
|
||||
<div class="tab-pane" data-value="B" data-icon-class="fab fa-github" id="tab-4785-2">b</div>
|
||||
<div class="tab-pane" data-value="C" id="tab-1502-1">c</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
Code
|
||||
pills
|
||||
Output
|
||||
<div class="tabbable">
|
||||
<ul class="nav nav-pills" data-tabsetid="4785">
|
||||
<li>
|
||||
<a href="#tab-4785-1" data-toggle="tab" data-value="A">A</a>
|
||||
</li>
|
||||
<li class="active">
|
||||
<a href="#tab-4785-2" data-toggle="tab" data-value="B">
|
||||
<i class=" fab fa-github fa-fw" role="presentation" aria-label=" icon"></i>
|
||||
B
|
||||
</a>
|
||||
</li>
|
||||
<li class="dropdown">
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown" data-value="Menu">
|
||||
Menu
|
||||
<b class="caret"></b>
|
||||
</a>
|
||||
<ul class="dropdown-menu" data-tabsetid="1502">
|
||||
<li>
|
||||
<a href="#tab-1502-1" data-toggle="tab" data-value="C">C</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="content-header"></div>
|
||||
<div class="tab-content" data-tabsetid="4785">
|
||||
<div class="tab-pane" data-value="A" id="tab-4785-1">a</div>
|
||||
<div class="tab-pane active" data-icon-class="fab fa-github" data-value="B" id="tab-4785-2">b</div>
|
||||
<div class="tab-pane" data-value="C" id="tab-1502-1">c</div>
|
||||
</div>
|
||||
<div class="content-footer"></div>
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
Code
|
||||
bslib_tags(x)
|
||||
Output
|
||||
<div class="tabbable">
|
||||
<ul class="nav nav-tabs" data-tabsetid="4785">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" data-toggle="tab" data-value="A" href="#tab-4785-1">A</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="#tab-4785-2" data-toggle="tab" data-value="B">
|
||||
<i class=" fab fa-github fa-fw" role="presentation" aria-label=" icon"></i>
|
||||
B
|
||||
</a>
|
||||
</li>
|
||||
<li class="dropdown nav-item">
|
||||
<a class="dropdown-toggle nav-link" data-toggle="dropdown" data-value="Menu" href="#">
|
||||
Menu
|
||||
<b class="caret"></b>
|
||||
</a>
|
||||
<ul class="dropdown-menu" data-tabsetid="1502">
|
||||
<a class="dropdown-item" href="#tab-1502-1" data-toggle="tab" data-value="C">C</a>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="tab-content" data-tabsetid="4785">
|
||||
<div class="tab-pane active" data-value="A" id="tab-4785-1">a</div>
|
||||
<div class="tab-pane" data-value="B" data-icon-class="fab fa-github" id="tab-4785-2">b</div>
|
||||
<div class="tab-pane" data-value="C" id="tab-1502-1">c</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
Code
|
||||
bslib_tags(x)
|
||||
Output
|
||||
<div class="tabbable">
|
||||
<ul class="nav nav-pills" data-tabsetid="4785">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="#tab-4785-1" data-toggle="tab" data-value="A">A</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" data-toggle="tab" data-value="B" href="#tab-4785-2">
|
||||
<i class=" fab fa-github fa-fw" role="presentation" aria-label=" icon"></i>
|
||||
B
|
||||
</a>
|
||||
</li>
|
||||
<li class="dropdown nav-item">
|
||||
<a class="dropdown-toggle nav-link" data-toggle="dropdown" data-value="Menu" href="#">
|
||||
Menu
|
||||
<b class="caret"></b>
|
||||
</a>
|
||||
<ul class="dropdown-menu" data-tabsetid="1502">
|
||||
<a class="dropdown-item" href="#tab-1502-1" data-toggle="tab" data-value="C">C</a>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="content-header"></div>
|
||||
<div class="tab-content" data-tabsetid="4785">
|
||||
<div class="tab-pane" data-value="A" id="tab-4785-1">a</div>
|
||||
<div class="tab-pane active" data-icon-class="fab fa-github" data-value="B" id="tab-4785-2">b</div>
|
||||
<div class="tab-pane" data-value="C" id="tab-1502-1">c</div>
|
||||
</div>
|
||||
<div class="content-footer"></div>
|
||||
</div>
|
||||
|
||||
# navbarPage() markup is correct
|
||||
|
||||
Code
|
||||
nav_page
|
||||
Output
|
||||
<nav class="navbar navbar-default navbar-static-top" role="navigation">
|
||||
<div class="container-fluid">
|
||||
<div class="navbar-header">
|
||||
<span class="navbar-brand">Title</span>
|
||||
</div>
|
||||
<ul class="nav navbar-nav" data-tabsetid="4785">
|
||||
<li class="active">
|
||||
<a href="#tab-4785-1" data-toggle="tab" data-value="A">A</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#tab-4785-2" data-toggle="tab" data-value="B">
|
||||
<i class=" fab fa-github fa-fw" role="presentation" aria-label=" icon"></i>
|
||||
B
|
||||
</a>
|
||||
</li>
|
||||
<li class="dropdown">
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown" data-value="Menu">
|
||||
Menu
|
||||
<b class="caret"></b>
|
||||
</a>
|
||||
<ul class="dropdown-menu" data-tabsetid="1502">
|
||||
<li>
|
||||
<a href="#tab-1502-1" data-toggle="tab" data-value="C">C</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
<div class="container-fluid">
|
||||
<div class="tab-content" data-tabsetid="4785">
|
||||
<div class="tab-pane active" data-value="A" id="tab-4785-1">a</div>
|
||||
<div class="tab-pane" data-value="B" data-icon-class="fab fa-github" id="tab-4785-2">b</div>
|
||||
<div class="tab-pane" data-value="C" id="tab-1502-1">c</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
Code
|
||||
bslib_tags(x)
|
||||
Output
|
||||
<nav class="navbar navbar-default navbar-static-top" role="navigation">
|
||||
<div class="container-fluid">
|
||||
<div class="navbar-header">
|
||||
<span class="navbar-brand">Title</span>
|
||||
</div>
|
||||
<ul class="nav navbar-nav" data-tabsetid="4785">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" data-toggle="tab" data-value="A" href="#tab-4785-1">A</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="#tab-4785-2" data-toggle="tab" data-value="B">
|
||||
<i class=" fab fa-github fa-fw" role="presentation" aria-label=" icon"></i>
|
||||
B
|
||||
</a>
|
||||
</li>
|
||||
<li class="dropdown nav-item">
|
||||
<a class="dropdown-toggle nav-link" data-toggle="dropdown" data-value="Menu" href="#">
|
||||
Menu
|
||||
<b class="caret"></b>
|
||||
</a>
|
||||
<ul class="dropdown-menu" data-tabsetid="1502">
|
||||
<a class="dropdown-item" href="#tab-1502-1" data-toggle="tab" data-value="C">C</a>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
<div class="container-fluid">
|
||||
<div class="tab-content" data-tabsetid="4785">
|
||||
<div class="tab-pane active" data-value="A" id="tab-4785-1">a</div>
|
||||
<div class="tab-pane" data-value="B" data-icon-class="fab fa-github" id="tab-4785-2">b</div>
|
||||
<div class="tab-pane" data-value="C" id="tab-1502-1">c</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
# String input is handled properly
|
||||
|
||||
Code
|
||||
nav_list
|
||||
Output
|
||||
<div class="row">
|
||||
<div class="col-sm-4 well">
|
||||
<ul class="nav nav-pills nav-stacked" data-tabsetid="4785">
|
||||
<li class="navbar-brand">A header</li>
|
||||
<li class="active">
|
||||
<a href="#tab-4785-2" data-toggle="tab" data-value="A">A</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#tab-4785-3" data-toggle="tab" data-value="B">
|
||||
<i class=" fab fa-github fa-fw" role="presentation" aria-label=" icon"></i>
|
||||
B
|
||||
</a>
|
||||
</li>
|
||||
<li class="dropdown">
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown" data-value="Menu">
|
||||
Menu
|
||||
<b class="caret"></b>
|
||||
</a>
|
||||
<ul class="dropdown-menu" data-tabsetid="1502">
|
||||
<li>
|
||||
<a href="#tab-1502-1" data-toggle="tab" data-value="C">C</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col-sm-8">
|
||||
<div class="tab-content" data-tabsetid="4785">
|
||||
<div class="tab-pane active" data-value="A" id="tab-4785-2">a</div>
|
||||
<div class="tab-pane" data-value="B" data-icon-class="fab fa-github" id="tab-4785-3">b</div>
|
||||
<div class="tab-pane" data-value="C" id="tab-1502-1">c</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
Code
|
||||
bslib_tags(x)
|
||||
Output
|
||||
<div class="row">
|
||||
<div class="col-sm-4 well">
|
||||
<ul class="nav nav-pills nav-stacked" data-tabsetid="4785">
|
||||
<li class="navbar-brand">A header</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" data-toggle="tab" data-value="A" href="#tab-4785-2">A</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="#tab-4785-3" data-toggle="tab" data-value="B">
|
||||
<i class=" fab fa-github fa-fw" role="presentation" aria-label=" icon"></i>
|
||||
B
|
||||
</a>
|
||||
</li>
|
||||
<li class="dropdown nav-item">
|
||||
<a class="dropdown-toggle nav-link" data-toggle="dropdown" data-value="Menu" href="#">
|
||||
Menu
|
||||
<b class="caret"></b>
|
||||
</a>
|
||||
<ul class="dropdown-menu" data-tabsetid="1502">
|
||||
<a class="dropdown-item" href="#tab-1502-1" data-toggle="tab" data-value="C">C</a>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col-sm-8">
|
||||
<div class="tab-content" data-tabsetid="4785">
|
||||
<div class="tab-pane active" data-value="A" id="tab-4785-2">a</div>
|
||||
<div class="tab-pane" data-value="B" data-icon-class="fab fa-github" id="tab-4785-3">b</div>
|
||||
<div class="tab-pane" data-value="C" id="tab-1502-1">c</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
# Shiny.tag input produces a warning
|
||||
|
||||
Code
|
||||
tab_tags
|
||||
Output
|
||||
<div class="tabbable">
|
||||
<ul class="nav nav-tabs" data-tabsetid="4785">
|
||||
<li class="active">
|
||||
<a href="#tab-4785-1" data-toggle="tab"></a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#tab-4785-2" data-toggle="tab" data-value="A">A</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#tab-4785-3" data-toggle="tab" data-value="B">
|
||||
<i class=" fab fa-github fa-fw" role="presentation" aria-label=" icon"></i>
|
||||
B
|
||||
</a>
|
||||
</li>
|
||||
<li class="dropdown">
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown" data-value="Menu">
|
||||
Menu
|
||||
<b class="caret"></b>
|
||||
</a>
|
||||
<ul class="dropdown-menu" data-tabsetid="1502">
|
||||
<li>
|
||||
<a href="#tab-1502-1" data-toggle="tab" data-value="C">C</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="tab-content" data-tabsetid="4785">
|
||||
<div class="active" id="tab-4785-1">A div</div>
|
||||
<div class="tab-pane" data-value="A" id="tab-4785-2">a</div>
|
||||
<div class="tab-pane" data-value="B" data-icon-class="fab fa-github" id="tab-4785-3">b</div>
|
||||
<div class="tab-pane" data-value="C" id="tab-1502-1">c</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,4 +1,87 @@
|
||||
# tabsetPanel() et al. use p_randomInt() to generate ids (which uses withPrivateSeed()),
|
||||
# so we need to fix Shiny's private seed in order to make their HTML output deterministic
|
||||
navlist_panel <- function(...) {
|
||||
|
||||
withPrivateSeed(set.seed(100))
|
||||
navlistPanel(...)
|
||||
}
|
||||
navbar_page <- function(...) {
|
||||
withPrivateSeed(set.seed(100))
|
||||
navbarPage(...)
|
||||
}
|
||||
tabset_panel <- function(...) {
|
||||
withPrivateSeed(set.seed(100))
|
||||
tabsetPanel(...)
|
||||
}
|
||||
|
||||
expect_snapshot2 <- function(...) {
|
||||
if (getRversion() < "3.6.0") {
|
||||
skip("Skipping snapshots on R < 3.6 because of different RNG method")
|
||||
}
|
||||
expect_snapshot(...)
|
||||
}
|
||||
|
||||
expect_snapshot_bslib <- function(x, ...) {
|
||||
expect_snapshot2(bslib_tags(x), ...)
|
||||
}
|
||||
|
||||
# Simulates the UI tags that would be produced by
|
||||
# shinyApp(bootstrapPage(theme), function(...) {})
|
||||
bslib_tags <- function(ui, theme = bslib::bs_theme()) {
|
||||
old_theme <- getCurrentTheme()
|
||||
on.exit(setCurrentTheme(old_theme), add = TRUE)
|
||||
setCurrentTheme(theme)
|
||||
htmltools::renderTags(ui)$html
|
||||
}
|
||||
|
||||
panels <- list(
|
||||
tabPanel("A", "a"),
|
||||
tabPanel("B", "b", icon = icon("github")),
|
||||
navbarMenu("Menu", tabPanel("C", "c"))
|
||||
)
|
||||
|
||||
test_that("tabsetPanel() markup is correct", {
|
||||
|
||||
default <- tabset_panel(!!!panels)
|
||||
pills <- tabset_panel(
|
||||
!!!panels, type = "pills", selected = "B",
|
||||
header = div(class = "content-header"),
|
||||
footer = div(class = "content-footer")
|
||||
)
|
||||
# BS3
|
||||
expect_snapshot2(default)
|
||||
expect_snapshot2(pills)
|
||||
# BS4
|
||||
expect_snapshot_bslib(default)
|
||||
expect_snapshot_bslib(pills)
|
||||
})
|
||||
|
||||
test_that("navbarPage() markup is correct", {
|
||||
nav_page <- navbar_page("Title", !!!panels)
|
||||
expect_snapshot2(nav_page)
|
||||
expect_snapshot_bslib(nav_page)
|
||||
})
|
||||
|
||||
# navlistPanel() can handle strings, but the others can't
|
||||
test_that("String input is handled properly", {
|
||||
nav_list <- navlist_panel(!!!c(list("A header"), panels))
|
||||
expect_snapshot2(nav_list)
|
||||
expect_snapshot_bslib(nav_list)
|
||||
expect_error(
|
||||
tabsetPanel(!!!c(list("A header"), panels)),
|
||||
"tabPanel"
|
||||
)
|
||||
})
|
||||
|
||||
test_that("Shiny.tag input produces a warning", {
|
||||
panels3 <- c(list(div("A div")), panels)
|
||||
tab_tags <- expect_warning(tabset_panel(!!!panels3))
|
||||
# Carson March 12th, 2021: Yes, he 'empty nav' output here isn't
|
||||
# sensible (which is why we now throw a warning), but it's probably
|
||||
# too late to change the behavior (it could break user code to do
|
||||
# anything different)
|
||||
expect_snapshot2(tab_tags)
|
||||
})
|
||||
|
||||
test_that("tabPanelBody validates it's input", {
|
||||
expect_silent(tabPanelBody("a", "content1", "content2", icon = icon("table")))
|
||||
@@ -13,3 +96,19 @@ test_that("tabPanelBody validates it's input", {
|
||||
expect_error(tabPanelBody(""), "single, non-empty string")
|
||||
expect_error(tabPanelBody(letters[1:2]), "single, non-empty string")
|
||||
})
|
||||
|
||||
|
||||
# https://github.com/rstudio/shiny/issues/3352
|
||||
test_that("tabItem titles can contain tag objects", {
|
||||
title <- tagList(tags$i("Hello"), "world")
|
||||
x <- tabsetPanel(tabPanel(title, "tab content"))
|
||||
x <- renderTags(x)
|
||||
|
||||
# Result should contain (with different whitespace):
|
||||
# "<a ....> <i>Hello</i> world"
|
||||
# As opposed to:
|
||||
# "<a ....><i>Hello</i> world
|
||||
expect_true(
|
||||
grepl("<a [^>]+>\\s*<i>Hello</i>\\s+world", x$html)
|
||||
)
|
||||
})
|
||||
|
||||
@@ -239,3 +239,15 @@ test_that("dateYMD works", {
|
||||
c("2019/11/05", "")
|
||||
)
|
||||
})
|
||||
|
||||
test_that("quoToFunction handles nested quosures", {
|
||||
quo_inner <- local({
|
||||
x <- 1
|
||||
rlang::quo(x)
|
||||
})
|
||||
|
||||
quo_outer <- rlang::quo(!!quo_inner + 1)
|
||||
|
||||
func <- quoToFunction(quo_outer, "foo")
|
||||
expect_identical(func(), 2)
|
||||
})
|
||||
|
||||
@@ -66,4 +66,4 @@ sass(
|
||||
|
||||
|
||||
# Finally, run yarn build so the JS patches propogate to the minified files
|
||||
withr::with_dir(rprojroot::find_package_root_file("tools"), system("yarn build"))
|
||||
withr::with_dir(rprojroot::find_package_root_file("tools"), system("yarn bundle_external_libs"))
|
||||
|
||||
Reference in New Issue
Block a user