mirror of
https://github.com/rstudio/shiny.git
synced 2026-01-11 07:58:11 -05:00
Compare commits
3 Commits
tabsetPane
...
tab-docs
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3450c36ff3 | ||
|
|
b77fbe2b09 | ||
|
|
7d7b312078 |
@@ -408,7 +408,6 @@ 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)
|
||||
|
||||
425
R/bootstrap.R
425
R/bootstrap.R
@@ -163,15 +163,6 @@ 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)
|
||||
}
|
||||
@@ -220,7 +211,7 @@ registerThemeDependency <- function(func) {
|
||||
|
||||
bootstrapDependency <- function(theme) {
|
||||
htmlDependency(
|
||||
"bootstrap", bootstrapVersion,
|
||||
"bootstrap", "3.4.1",
|
||||
c(
|
||||
href = "shared/bootstrap",
|
||||
file = system.file("www/shared/bootstrap", package = "shiny")
|
||||
@@ -239,8 +230,6 @@ bootstrapDependency <- function(theme) {
|
||||
)
|
||||
}
|
||||
|
||||
bootstrapVersion <- "3.4.1"
|
||||
|
||||
|
||||
#' @rdname bootstrapPage
|
||||
#' @export
|
||||
@@ -353,20 +342,13 @@ 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.
|
||||
#' toggle a set of [tabPanel()] elements. `navbarMenu()` can be used to create
|
||||
#' an embedded menu within the navbar that in turns includes additional
|
||||
#' `tabPanels`.
|
||||
#'
|
||||
#' @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
|
||||
@@ -384,26 +366,13 @@ 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.
|
||||
#'
|
||||
#' @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()]
|
||||
#'
|
||||
#' @seealso [updateNavbarPage()], [insertTab()], [showTab()]
|
||||
#' @family layout functions
|
||||
#' @family tab layouts
|
||||
#'
|
||||
#' @examples
|
||||
#' navbarPage("App Title",
|
||||
@@ -447,11 +416,10 @@ 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 <- paste0(navbarClass, " navbar-", position)
|
||||
navbarClass <- paste(navbarClass, " navbar-", position, sep = "")
|
||||
if (inverse)
|
||||
navbarClass <- paste(navbarClass, "navbar-inverse")
|
||||
|
||||
@@ -459,14 +427,21 @@ navbarPage <- function(title,
|
||||
selected <- restoreInput(id = id, default = selected)
|
||||
|
||||
# build the tabset
|
||||
tabset <- buildTabset(..., ulClass = "nav navbar-nav", id = id, selected = selected)
|
||||
tabs <- list(...)
|
||||
tabset <- buildTabset(tabs, "nav navbar-nav", NULL, id, selected)
|
||||
|
||||
containerClass <- paste0("container", if (fluid) "-fluid")
|
||||
# function to return plain or fluid class name
|
||||
className <- function(name) {
|
||||
if (fluid)
|
||||
paste(name, "-fluid", sep="")
|
||||
else
|
||||
name
|
||||
}
|
||||
|
||||
# built the container div dynamically to support optional collapsibility
|
||||
if (collapsible) {
|
||||
navId <- paste0("navbar-collapse-", p_randomInt(1000, 10000))
|
||||
containerDiv <- div(class=containerClass,
|
||||
navId <- paste("navbar-collapse-", p_randomInt(1000, 10000), sep="")
|
||||
containerDiv <- div(class=className("container"),
|
||||
div(class="navbar-header",
|
||||
tags$button(type="button", class="navbar-toggle collapsed",
|
||||
`data-toggle`="collapse", `data-target`=paste0("#", navId),
|
||||
@@ -480,7 +455,7 @@ navbarPage <- function(title,
|
||||
div(class="navbar-collapse collapse", id=navId, tabset$navList)
|
||||
)
|
||||
} else {
|
||||
containerDiv <- div(class=containerClass,
|
||||
containerDiv <- div(class=className("container"),
|
||||
div(class="navbar-header",
|
||||
span(class="navbar-brand", pageTitle)
|
||||
),
|
||||
@@ -489,7 +464,7 @@ navbarPage <- function(title,
|
||||
}
|
||||
|
||||
# build the main tab content div
|
||||
contentDiv <- div(class=containerClass)
|
||||
contentDiv <- div(class=className("container"))
|
||||
if (!is.null(header))
|
||||
contentDiv <- tagAppendChild(contentDiv, div(class="row", header))
|
||||
contentDiv <- tagAppendChild(contentDiv, tabset$content)
|
||||
@@ -516,15 +491,11 @@ navbarPage <- function(title,
|
||||
navbarMenu <- function(title, ..., menuName = title, icon = NULL) {
|
||||
structure(list(title = title,
|
||||
menuName = menuName,
|
||||
tabs = list2(...),
|
||||
tabs = list(...),
|
||||
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
|
||||
@@ -632,6 +603,10 @@ 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
|
||||
@@ -640,9 +615,8 @@ 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()]
|
||||
#'
|
||||
#' @seealso [tabsetPanel()]
|
||||
#' @family tab layouts
|
||||
#'
|
||||
#' @examples
|
||||
#' # Show a tabset that includes a plot, summary, and
|
||||
@@ -655,7 +629,6 @@ 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",
|
||||
@@ -665,16 +638,8 @@ 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
|
||||
#' @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.
|
||||
#' @rdname tabPanel
|
||||
tabPanelBody <- function(value, ..., icon = NULL) {
|
||||
if (
|
||||
!is.character(value) ||
|
||||
@@ -707,14 +672,10 @@ tabPanelBody <- function(value, ..., icon = NULL) {
|
||||
#' conjunction with [tabPanelBody()] and [updateTabsetPanel()] to control the
|
||||
#' active tab via other input controls. (See example below)}
|
||||
#' }
|
||||
#' @param card whether to wrap the navigation controls and content into an 'output card'. This functionality currently requires a [bslib::bs_theme()] in the page layout with `version = 4` or higher.
|
||||
#' @param position This argument is deprecated; it has been discontinued in
|
||||
#' Bootstrap 3.
|
||||
#' @inheritParams navbarPage
|
||||
#' @return A tabset that can be passed to [mainPanel()]
|
||||
#'
|
||||
#' @seealso [tabPanel()], [updateTabsetPanel()],
|
||||
#' [insertTab()], [showTab()]
|
||||
#' @seealso [updateTabsetPanel()], [insertTab()], [showTab()]
|
||||
#' @family tab layouts
|
||||
#'
|
||||
#' @examples
|
||||
#' # Show a tabset that includes a plot, summary, and
|
||||
@@ -760,9 +721,6 @@ tabsetPanel <- function(...,
|
||||
id = NULL,
|
||||
selected = NULL,
|
||||
type = c("tabs", "pills", "hidden"),
|
||||
header = NULL,
|
||||
footer = NULL,
|
||||
card = FALSE,
|
||||
position = deprecated()) {
|
||||
if (lifecycle::is_present(position)) {
|
||||
shinyDeprecated(
|
||||
@@ -774,37 +732,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)
|
||||
|
||||
nav <- tabset$navList
|
||||
if (card) {
|
||||
nav <- tags$div(
|
||||
class = "card-header",
|
||||
tagFunction(function() {
|
||||
if (getCurrentVersion() >= 4) {
|
||||
return(NULL)
|
||||
}
|
||||
stop(
|
||||
"`tabsetPanel(card = TRUE)` requires Bootstrap 4 or higher. ",
|
||||
"Please supply `bslib::bs_theme()` to the UI's page layout function ",
|
||||
"(e.g., `fluidPage(theme = bslib::bs_theme())`).",
|
||||
call. = FALSE
|
||||
)
|
||||
}),
|
||||
tagAppendAttributes(
|
||||
nav, class = paste0("card-header-", type)
|
||||
)
|
||||
)
|
||||
}
|
||||
tabset <- buildTabset(tabs, paste0("nav nav-", type), NULL, id, selected)
|
||||
|
||||
tabs <- tags$div(class = "tabbable", class = if (card) "card", nav)
|
||||
content <- dropNulls(list(header, tabset$content, footer))
|
||||
if (card) {
|
||||
tagAppendChild(tabs, tags$div(class = "card-body", !!!content))
|
||||
} else {
|
||||
tagAppendChildren(tabs, content)
|
||||
}
|
||||
# create the content
|
||||
first <- tabset$navList
|
||||
second <- tabset$content
|
||||
|
||||
# create the tab div
|
||||
tags$div(class = "tabbable", first, second)
|
||||
}
|
||||
|
||||
#' Create a navigation list panel
|
||||
@@ -812,31 +751,20 @@ 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.
|
||||
#'
|
||||
#' @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.
|
||||
#' @inheritParams tabsetPanel
|
||||
#' @param ... [tabPanel()] elements to include in the navbar.
|
||||
#' Plain strings will be converted to headers.
|
||||
#' @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 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.
|
||||
#' @details
|
||||
#'
|
||||
#' @seealso [tabPanel()], [updateNavlistPanel()],
|
||||
#' [insertTab()], [showTab()]
|
||||
#' @seealso [updateTabsetPanel()], [insertTab()], [showTab()]
|
||||
#' @family tab layouts
|
||||
#'
|
||||
#' @examples
|
||||
#' fluidPage(
|
||||
@@ -854,30 +782,37 @@ 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)
|
||||
|
||||
tabset <- buildTabset(
|
||||
..., ulClass = "nav nav-pills nav-stacked",
|
||||
textFilter = function(text) tags$li(class = "navbar-brand", text),
|
||||
id = id, selected = 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)
|
||||
)
|
||||
|
||||
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))
|
||||
)
|
||||
)
|
||||
# return the row
|
||||
if (fluid)
|
||||
fluidRow(columns)
|
||||
else
|
||||
fixedRow(columns)
|
||||
}
|
||||
|
||||
# Helpers to build tabsetPanels (& Co.) and their elements
|
||||
@@ -895,14 +830,14 @@ containsSelectedTab <- function(tabs) {
|
||||
}
|
||||
|
||||
findAndMarkSelectedTab <- function(tabs, selected, foundSelected) {
|
||||
tabs <- lapply(tabs, function(x) {
|
||||
if (foundSelected || is.character(x)) {
|
||||
tabs <- lapply(tabs, function(div) {
|
||||
if (foundSelected || is.character(div)) {
|
||||
# Strings are not selectable items
|
||||
|
||||
} else if (isNavbarMenu(x)) {
|
||||
} else if (inherits(div, "shiny.navbarmenu")) {
|
||||
# Recur for navbarMenus
|
||||
res <- findAndMarkSelectedTab(x$tabs, selected, foundSelected)
|
||||
x$tabs <- res$tabs
|
||||
res <- findAndMarkSelectedTab(div$tabs, selected, foundSelected)
|
||||
div$tabs <- res$tabs
|
||||
foundSelected <<- res$foundSelected
|
||||
|
||||
} else {
|
||||
@@ -911,16 +846,16 @@ findAndMarkSelectedTab <- function(tabs, selected, foundSelected) {
|
||||
# mark first available item as selected
|
||||
if (is.null(selected)) {
|
||||
foundSelected <<- TRUE
|
||||
x <- markTabAsSelected(x)
|
||||
div <- markTabAsSelected(div)
|
||||
} else {
|
||||
tabValue <- x$attribs$`data-value` %||% x$attribs$title
|
||||
tabValue <- div$attribs$`data-value` %||% div$attribs$title
|
||||
if (identical(selected, tabValue)) {
|
||||
foundSelected <<- TRUE
|
||||
x <- markTabAsSelected(x)
|
||||
div <- markTabAsSelected(div)
|
||||
}
|
||||
}
|
||||
}
|
||||
return(x)
|
||||
return(div)
|
||||
})
|
||||
return(list(tabs = tabs, foundSelected = foundSelected))
|
||||
}
|
||||
@@ -946,10 +881,9 @@ navbarMenuTextFilter <- function(text) {
|
||||
|
||||
# This function is called internally by navbarPage, tabsetPanel
|
||||
# and navlistPanel
|
||||
buildTabset <- function(..., ulClass, textFilter = NULL, id = NULL,
|
||||
buildTabset <- function(tabs, ulClass, textFilter = NULL, id = NULL,
|
||||
selected = NULL, foundSelected = FALSE) {
|
||||
|
||||
tabs <- dropNulls(list2(...))
|
||||
res <- findAndMarkSelectedTab(tabs, selected, foundSelected)
|
||||
tabs <- res$tabs
|
||||
foundSelected <- res$foundSelected
|
||||
@@ -970,10 +904,10 @@ buildTabset <- function(..., ulClass, textFilter = NULL, id = NULL,
|
||||
tabs = tabs, textFilter = textFilter)
|
||||
|
||||
tabNavList <- tags$ul(class = ulClass, id = id,
|
||||
`data-tabsetid` = tabsetId, !!!lapply(tabs, "[[", "liTag"))
|
||||
`data-tabsetid` = tabsetId, lapply(tabs, "[[", 1))
|
||||
|
||||
tabContent <- tags$div(class = "tab-content",
|
||||
`data-tabsetid` = tabsetId, !!!lapply(tabs, "[[", "divTag"))
|
||||
`data-tabsetid` = tabsetId, lapply(tabs, "[[", 2))
|
||||
|
||||
list(navList = tabNavList, content = tabContent)
|
||||
}
|
||||
@@ -985,173 +919,56 @@ buildTabset <- function(..., ulClass, textFilter = NULL, id = NULL,
|
||||
buildTabItem <- function(index, tabsetId, foundSelected, tabs = NULL,
|
||||
divTag = NULL, textFilter = NULL) {
|
||||
|
||||
divTag <- divTag %||% tabs[[index]]
|
||||
divTag <- if (!is.null(divTag)) divTag else tabs[[index]]
|
||||
|
||||
# Handles navlistPanel() headers and dropdown dividers
|
||||
if (is.character(divTag) && !is.null(textFilter)) {
|
||||
return(list(liTag = textFilter(divTag), divTag = NULL))
|
||||
}
|
||||
# text item: pass it to the textFilter if it exists
|
||||
liTag <- textFilter(divTag)
|
||||
divTag <- NULL
|
||||
|
||||
if (isNavbarMenu(divTag)) {
|
||||
# tabPanelMenu item: build the child tabset
|
||||
tabset <- buildTabset(
|
||||
!!!divTag$tabs, ulClass = "dropdown-menu",
|
||||
textFilter = navbarMenuTextFilter,
|
||||
foundSelected = foundSelected
|
||||
} 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
|
||||
)
|
||||
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 = "-")
|
||||
title <- tagGetAttribute(divTag, "title")
|
||||
value <- tagGetAttribute(divTag, "data-value")
|
||||
icon <- getIcon(iconClass = tagGetAttribute(divTag, "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)
|
||||
}
|
||||
})
|
||||
)
|
||||
}
|
||||
divTag <- tabset$content$children
|
||||
|
||||
|
||||
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
|
||||
} 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 = liTag, divTag = divTag))
|
||||
}
|
||||
|
||||
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
|
||||
#'
|
||||
|
||||
@@ -49,12 +49,6 @@ 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
|
||||
|
||||
@@ -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 list2
|
||||
#' is_false
|
||||
#' missing_arg is_missing maybe_missing
|
||||
#' @importFrom ellipsis
|
||||
#' check_dots_empty check_dots_unnamed
|
||||
|
||||
@@ -1198,26 +1198,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/get-substitution.js
|
||||
var require_get_substitution = __commonJS(function(exports2, module2) {
|
||||
var toObject4 = require_to_object();
|
||||
@@ -1266,6 +1246,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/is-regexp.js
|
||||
var require_is_regexp = __commonJS(function(exports2, module2) {
|
||||
var isObject3 = require_is_object();
|
||||
@@ -2996,9 +2996,7 @@
|
||||
function getTargetTabs($tabset, $tabContent, target) {
|
||||
var dataValue = "[data-value='" + $escape(target) + "']";
|
||||
var $aTag = $tabset.find("a" + dataValue);
|
||||
var $liTag = $aTag.parent("li");
|
||||
if ($liTag.length === 0)
|
||||
$liTag = $aTag;
|
||||
var $liTag = $aTag.parent();
|
||||
if ($liTag.length === 0) {
|
||||
throw "There is no tabPanel (or navbarMenu) with value (or menuName) equal to '" + target + "'";
|
||||
}
|
||||
@@ -3007,10 +3005,7 @@
|
||||
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']");
|
||||
if ($dropdownLiTags.parent("li").length > 0) {
|
||||
$dropdownLiTags = $dropdownLiTags.parent("li");
|
||||
}
|
||||
var $dropdownLiTags = $dropdownTabset.find("a[data-toggle='tab']").parent("li");
|
||||
$dropdownLiTags.each(function(i, el) {
|
||||
$liTags.push(import_jquery6.default(el));
|
||||
});
|
||||
@@ -3041,9 +3036,6 @@
|
||||
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) {
|
||||
@@ -3055,10 +3047,7 @@
|
||||
if ($aTag.attr("data-toggle") === "tab") {
|
||||
var index = getTabIndex($tabset, tabsetId);
|
||||
var tabId = "tab-" + tabsetId + "-" + index;
|
||||
var anchor = $liTag.find("> a");
|
||||
if (anchor.length === 0)
|
||||
anchor = $liTag;
|
||||
anchor.attr("href", "#" + tabId);
|
||||
$liTag.find("> a").attr("href", "#" + tabId);
|
||||
$divTag.attr("id", tabId);
|
||||
}
|
||||
if (message.position === "before") {
|
||||
@@ -3091,8 +3080,8 @@
|
||||
}
|
||||
function getTabIndex($tabset2, tabsetId2) {
|
||||
var existingTabIds = [0];
|
||||
$tabset2.find("a[data-toggle='tab']").each(function() {
|
||||
var $tab = import_jquery6.default(this);
|
||||
$tabset2.find("> li").each(function() {
|
||||
var $tab = import_jquery6.default(this).find("> a[data-toggle='tab']");
|
||||
if ($tab.length > 0) {
|
||||
var href = $tab.attr("href").replace(/.*(?=#[^\s]+$)/, "");
|
||||
var _index = href.replace("#tab-" + tabsetId2 + "-", "");
|
||||
@@ -6095,7 +6084,7 @@
|
||||
return import_jquery6.default(scope).find("ul.nav.shiny-tab-input");
|
||||
},
|
||||
getValue: function getValue(el) {
|
||||
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");
|
||||
var anchor = import_jquery6.default(el).find("li:not(.dropdown).active").children("a");
|
||||
if (anchor.length === 1)
|
||||
return this._getTabName(anchor);
|
||||
return null;
|
||||
@@ -6104,7 +6093,7 @@
|
||||
var self2 = this;
|
||||
var success = false;
|
||||
if (value) {
|
||||
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");
|
||||
var anchors = import_jquery6.default(el).find("li:not(.dropdown)").children("a");
|
||||
anchors.each(function() {
|
||||
if (self2._getTabName(import_jquery6.default(this)) === value) {
|
||||
import_jquery6.default(this).tab("show");
|
||||
@@ -7270,68 +7259,28 @@
|
||||
}, {unsafe: true});
|
||||
}
|
||||
|
||||
// node_modules/core-js/modules/es.string.match.js
|
||||
// node_modules/core-js/modules/es.string.replace.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 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 regExpExec = require_regexp_exec_abstract();
|
||||
var max3 = Math.max;
|
||||
var min2 = Math.min;
|
||||
var maybeToString = function(it) {
|
||||
return it === void 0 ? it : String(it);
|
||||
};
|
||||
fixRegExpWellKnownSymbolLogic2("replace", 2, function(REPLACE, nativeReplace, maybeCallNative, reason) {
|
||||
fixRegExpWellKnownSymbolLogic("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 = requireObjectCoercible2(this);
|
||||
var O = requireObjectCoercible(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);
|
||||
},
|
||||
@@ -7341,7 +7290,7 @@
|
||||
if (res.done)
|
||||
return res.value;
|
||||
}
|
||||
var rx = anObject3(regexp);
|
||||
var rx = anObject2(regexp);
|
||||
var S = String(this);
|
||||
var functionalReplace = typeof replaceValue === "function";
|
||||
if (!functionalReplace)
|
||||
@@ -7353,7 +7302,7 @@
|
||||
}
|
||||
var results = [];
|
||||
while (true) {
|
||||
var result = regExpExec2(rx, S);
|
||||
var result = regExpExec(rx, S);
|
||||
if (result === null)
|
||||
break;
|
||||
results.push(result);
|
||||
@@ -7361,7 +7310,7 @@
|
||||
break;
|
||||
var matchStr = String(result[0]);
|
||||
if (matchStr === "")
|
||||
rx.lastIndex = advanceStringIndex2(S, toLength5(rx.lastIndex), fullUnicode);
|
||||
rx.lastIndex = advanceStringIndex(S, toLength4(rx.lastIndex), fullUnicode);
|
||||
}
|
||||
var accumulatedResult = "";
|
||||
var nextSourcePosition = 0;
|
||||
@@ -7393,13 +7342,13 @@
|
||||
|
||||
// node_modules/core-js/modules/es.string.split.js
|
||||
"use strict";
|
||||
var fixRegExpWellKnownSymbolLogic3 = require_fix_regexp_well_known_symbol_logic();
|
||||
var fixRegExpWellKnownSymbolLogic2 = require_fix_regexp_well_known_symbol_logic();
|
||||
var isRegExp = require_is_regexp();
|
||||
var anObject4 = require_an_object();
|
||||
var requireObjectCoercible3 = require_require_object_coercible();
|
||||
var anObject3 = require_an_object();
|
||||
var requireObjectCoercible2 = require_require_object_coercible();
|
||||
var speciesConstructor = require_species_constructor();
|
||||
var advanceStringIndex3 = require_advance_string_index();
|
||||
var toLength6 = require_to_length();
|
||||
var advanceStringIndex2 = require_advance_string_index();
|
||||
var toLength5 = require_to_length();
|
||||
var callRegExpExec = require_regexp_exec_abstract();
|
||||
var regexpExec = require_regexp_exec();
|
||||
var fails5 = require_fails();
|
||||
@@ -7409,11 +7358,11 @@
|
||||
var SUPPORTS_Y = !fails5(function() {
|
||||
return !RegExp(MAX_UINT32, "y");
|
||||
});
|
||||
fixRegExpWellKnownSymbolLogic3("split", 2, function(SPLIT, nativeSplit, maybeCallNative) {
|
||||
fixRegExpWellKnownSymbolLogic2("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(requireObjectCoercible3(this));
|
||||
var string = String(requireObjectCoercible2(this));
|
||||
var lim = limit === void 0 ? MAX_UINT32 : limit >>> 0;
|
||||
if (lim === 0)
|
||||
return [];
|
||||
@@ -7456,7 +7405,7 @@
|
||||
internalSplit = nativeSplit;
|
||||
return [
|
||||
function split(separator, limit) {
|
||||
var O = requireObjectCoercible3(this);
|
||||
var O = requireObjectCoercible2(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);
|
||||
},
|
||||
@@ -7464,7 +7413,7 @@
|
||||
var res = maybeCallNative(internalSplit, regexp, this, limit, internalSplit !== nativeSplit);
|
||||
if (res.done)
|
||||
return res.value;
|
||||
var rx = anObject4(regexp);
|
||||
var rx = anObject3(regexp);
|
||||
var S = String(this);
|
||||
var C = speciesConstructor(rx, RegExp);
|
||||
var unicodeMatching = rx.unicode;
|
||||
@@ -7482,8 +7431,8 @@
|
||||
splitter.lastIndex = SUPPORTS_Y ? q : 0;
|
||||
var z = callRegExpExec(splitter, SUPPORTS_Y ? S : S.slice(q));
|
||||
var e;
|
||||
if (z === null || (e = min3(toLength6(splitter.lastIndex + (SUPPORTS_Y ? 0 : q)), S.length)) === p) {
|
||||
q = advanceStringIndex3(S, q, unicodeMatching);
|
||||
if (z === null || (e = min3(toLength5(splitter.lastIndex + (SUPPORTS_Y ? 0 : q)), S.length)) === p) {
|
||||
q = advanceStringIndex2(S, q, unicodeMatching);
|
||||
} else {
|
||||
A.push(S.slice(p, q));
|
||||
if (A.length === lim)
|
||||
@@ -7776,12 +7725,6 @@
|
||||
el.removeChild(div);
|
||||
return linkColor;
|
||||
}
|
||||
function isBS3() {
|
||||
if (!import_jquery5.default.fn.tooltip) {
|
||||
return false;
|
||||
}
|
||||
return import_jquery5.default.fn.tooltip.Constructor.VERSION.match(/^3\./);
|
||||
}
|
||||
|
||||
// src/shiny.ts
|
||||
var Shiny;
|
||||
@@ -7912,9 +7855,9 @@
|
||||
var $22 = require_export();
|
||||
var fails6 = require_fails();
|
||||
var ArrayBufferModule = require_array_buffer();
|
||||
var anObject5 = require_an_object();
|
||||
var anObject4 = require_an_object();
|
||||
var toAbsoluteIndex3 = require_to_absolute_index();
|
||||
var toLength7 = require_to_length();
|
||||
var toLength6 = require_to_length();
|
||||
var speciesConstructor2 = require_species_constructor();
|
||||
var ArrayBuffer3 = ArrayBufferModule.ArrayBuffer;
|
||||
var DataView2 = ArrayBufferModule.DataView;
|
||||
@@ -7925,12 +7868,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(anObject5(this), start);
|
||||
return nativeArrayBufferSlice.call(anObject4(this), start);
|
||||
}
|
||||
var length = anObject5(this).byteLength;
|
||||
var length = anObject4(this).byteLength;
|
||||
var first = toAbsoluteIndex3(start, length);
|
||||
var fin = toAbsoluteIndex3(end === void 0 ? length : end, length);
|
||||
var result = new (speciesConstructor2(this, ArrayBuffer3))(toLength7(fin - first));
|
||||
var result = new (speciesConstructor2(this, ArrayBuffer3))(toLength6(fin - first));
|
||||
var viewSource = new DataView2(this);
|
||||
var viewTarget = new DataView2(result);
|
||||
var index = 0;
|
||||
@@ -8048,6 +7991,46 @@
|
||||
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,10 +28,8 @@ 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 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{...}{\code{\link[=tabPanel]{tabPanel()}} elements to include in the navbar.
|
||||
Plain strings will be converted to headers.}
|
||||
|
||||
\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
|
||||
@@ -65,7 +63,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 a fluid layout. \code{FALSE} to use a fixed
|
||||
\item{fluid}{\code{TRUE} to use fluid layout; \code{FALSE} to use fixed
|
||||
layout.}
|
||||
|
||||
\item{responsive}{This option is deprecated; it is no longer optional with
|
||||
@@ -93,17 +91,11 @@ 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.
|
||||
}
|
||||
\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).
|
||||
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}.
|
||||
}
|
||||
\examples{
|
||||
navbarPage("App Title",
|
||||
@@ -123,9 +115,7 @@ navbarPage("App Title",
|
||||
)
|
||||
}
|
||||
\seealso{
|
||||
\code{\link[=tabPanel]{tabPanel()}}, \code{\link[=tabsetPanel]{tabsetPanel()}},
|
||||
\code{\link[=updateNavbarPage]{updateNavbarPage()}}, \code{\link[=insertTab]{insertTab()}},
|
||||
\code{\link[=showTab]{showTab()}}
|
||||
\code{\link[=updateNavbarPage]{updateNavbarPage()}}, \code{\link[=insertTab]{insertTab()}}, \code{\link[=showTab]{showTab()}}
|
||||
|
||||
Other layout functions:
|
||||
\code{\link{fillPage}()},
|
||||
@@ -135,5 +125,11 @@ 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,30 +8,23 @@ 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 navlist}
|
||||
\item{...}{\code{\link[=tabPanel]{tabPanel()}} elements to include in the navbar.
|
||||
Plain strings will be converted to headers.}
|
||||
|
||||
\item{id}{If provided, you can use \verb{input$}\emph{\code{id}} in your
|
||||
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
|
||||
server logic to determine which of the current tabs 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 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}
|
||||
of the tab that should be selected by default. If \code{NULL}, the first
|
||||
tab will be selected.}
|
||||
|
||||
\item{well}{\code{TRUE} to place a well (gray rounded rectangle) around the
|
||||
navigation list.}
|
||||
@@ -47,11 +40,7 @@ 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(
|
||||
@@ -67,6 +56,11 @@ fluidPage(
|
||||
)
|
||||
}
|
||||
\seealso{
|
||||
\code{\link[=tabPanel]{tabPanel()}}, \code{\link[=updateNavlistPanel]{updateNavlistPanel()}},
|
||||
\code{\link[=insertTab]{insertTab()}}, \code{\link[=showTab]{showTab()}}
|
||||
\code{\link[=updateTabsetPanel]{updateTabsetPanel()}}, \code{\link[=insertTab]{insertTab()}}, \code{\link[=showTab]{showTab()}}
|
||||
|
||||
Other tab layouts:
|
||||
\code{\link{navbarPage}()},
|
||||
\code{\link{tabPanel}()},
|
||||
\code{\link{tabsetPanel}()}
|
||||
}
|
||||
\concept{tab layouts}
|
||||
|
||||
@@ -21,20 +21,12 @@ 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()}}.}
|
||||
}
|
||||
\value{
|
||||
A tab that can be passed to \code{\link[=tabsetPanel]{tabsetPanel()}}
|
||||
}
|
||||
\description{
|
||||
Create a tab panel
|
||||
\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")}.
|
||||
}
|
||||
\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
|
||||
@@ -47,5 +39,9 @@ mainPanel(
|
||||
)
|
||||
}
|
||||
\seealso{
|
||||
\code{\link[=tabsetPanel]{tabsetPanel()}}
|
||||
Other tab layouts:
|
||||
\code{\link{navbarPage}()},
|
||||
\code{\link{navlistPanel}()},
|
||||
\code{\link{tabsetPanel}()}
|
||||
}
|
||||
\concept{tab layouts}
|
||||
|
||||
@@ -9,9 +9,6 @@ tabsetPanel(
|
||||
id = NULL,
|
||||
selected = NULL,
|
||||
type = c("tabs", "pills", "hidden"),
|
||||
header = NULL,
|
||||
footer = NULL,
|
||||
card = FALSE,
|
||||
position = deprecated()
|
||||
)
|
||||
}
|
||||
@@ -35,20 +32,9 @@ 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{card}{whether to wrap the navigation controls and content into an 'output card'. This functionality currently requires a \code{\link[bslib:bs_theme]{bslib::bs_theme()}} in the page layout with \code{version = 4} or higher.}
|
||||
|
||||
\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.
|
||||
@@ -94,6 +80,11 @@ if (interactive()) {
|
||||
}
|
||||
}
|
||||
\seealso{
|
||||
\code{\link[=tabPanel]{tabPanel()}}, \code{\link[=updateTabsetPanel]{updateTabsetPanel()}},
|
||||
\code{\link[=insertTab]{insertTab()}}, \code{\link[=showTab]{showTab()}}
|
||||
\code{\link[=updateTabsetPanel]{updateTabsetPanel()}}, \code{\link[=insertTab]{insertTab()}}, \code{\link[=showTab]{showTab()}}
|
||||
|
||||
Other tab layouts:
|
||||
\code{\link{navbarPage}()},
|
||||
\code{\link{navlistPanel}()},
|
||||
\code{\link{tabPanel}()}
|
||||
}
|
||||
\concept{tab layouts}
|
||||
|
||||
@@ -34,7 +34,6 @@ import {
|
||||
updateLabel,
|
||||
getComputedLinkColor,
|
||||
makeBlob,
|
||||
isBS3,
|
||||
} from "./utils";
|
||||
|
||||
import { isQt, isIE, IEVersion } from "./utils/browser";
|
||||
@@ -1242,10 +1241,8 @@ function main(): void {
|
||||
function getTargetTabs($tabset, $tabContent, target) {
|
||||
const dataValue = "[data-value='" + $escape(target) + "']";
|
||||
const $aTag = $tabset.find("a" + dataValue);
|
||||
let $liTag = $aTag.parent("li");
|
||||
// BS3 dropdown anchors are wrapped in <li>, but they can't be in BS4
|
||||
const $liTag = $aTag.parent();
|
||||
|
||||
if ($liTag.length === 0) $liTag = $aTag;
|
||||
if ($liTag.length === 0) {
|
||||
throw (
|
||||
"There is no tabPanel (or navbarMenu) with value" +
|
||||
@@ -1262,12 +1259,10 @@ function main(): void {
|
||||
const $dropdownTabset = $aTag.find("+ ul.dropdown-menu");
|
||||
const dropdownId = $dropdownTabset.attr("data-tabsetid");
|
||||
|
||||
let $dropdownLiTags = $dropdownTabset.find("a[data-toggle='tab']");
|
||||
// BS3 dropdown anchors are wrapped in <li>, but they can't be in BS4
|
||||
const $dropdownLiTags = $dropdownTabset
|
||||
.find("a[data-toggle='tab']")
|
||||
.parent("li");
|
||||
|
||||
if ($dropdownLiTags.parent("li").length > 0) {
|
||||
$dropdownLiTags = $dropdownLiTags.parent("li");
|
||||
}
|
||||
$dropdownLiTags.each(function (i, el) {
|
||||
$liTags.push($(el));
|
||||
});
|
||||
@@ -1291,7 +1286,7 @@ function main(): void {
|
||||
let tabsetId = $parentTabset.attr("data-tabsetid");
|
||||
|
||||
const $divTag = $(message.divTag.html);
|
||||
let $liTag = $(message.liTag.html);
|
||||
const $liTag = $(message.liTag.html);
|
||||
const $aTag = $liTag.find("> a");
|
||||
|
||||
// Unless the item is being prepended/appended, the target tab
|
||||
@@ -1302,12 +1297,6 @@ 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),
|
||||
@@ -1332,11 +1321,7 @@ function main(): void {
|
||||
const index = getTabIndex($tabset, tabsetId);
|
||||
const tabId = "tab-" + tabsetId + "-" + index;
|
||||
|
||||
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);
|
||||
$liTag.find("> a").attr("href", "#" + tabId);
|
||||
$divTag.attr("id", tabId);
|
||||
}
|
||||
|
||||
@@ -1426,8 +1411,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("a[data-toggle='tab']").each(function () {
|
||||
const $tab = $(this);
|
||||
$tabset.find("> li").each(function () {
|
||||
const $tab = $(this).find("> a[data-toggle='tab']");
|
||||
|
||||
if ($tab.length > 0) {
|
||||
// remove leading url if it exists. (copy of bootstrap url stripper)
|
||||
@@ -5896,12 +5881,7 @@ function main(): void {
|
||||
return $(scope).find("ul.nav.shiny-tab-input");
|
||||
},
|
||||
getValue: function (el) {
|
||||
// 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"
|
||||
);
|
||||
const anchor = $(el).find("li:not(.dropdown).active").children("a");
|
||||
|
||||
if (anchor.length === 1) return this._getTabName(anchor);
|
||||
|
||||
@@ -5912,12 +5892,7 @@ function main(): void {
|
||||
let success = false;
|
||||
|
||||
if (value) {
|
||||
// prettier-ignore
|
||||
let anchors = isBS3()
|
||||
? $(el).find("li:not(.dropdown) > a")
|
||||
: $(el).find(
|
||||
".nav-link:not(.dropdown-toggle), .dropdown-menu > .dropdown-item"
|
||||
);
|
||||
const anchors = $(el).find("li:not(.dropdown)").children("a");
|
||||
|
||||
anchors.each(function () {
|
||||
if (self._getTabName($(this)) === value) {
|
||||
|
||||
@@ -361,17 +361,6 @@ function getComputedLinkColor(el: HTMLElement): string {
|
||||
return linkColor;
|
||||
}
|
||||
|
||||
function isBS3(): boolean {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
if (!$.fn.tooltip) {
|
||||
return false;
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
return $.fn.tooltip.Constructor.VERSION.match(/^3\./);
|
||||
}
|
||||
|
||||
export {
|
||||
escapeHTML,
|
||||
randomId,
|
||||
@@ -395,5 +384,4 @@ export {
|
||||
updateLabel,
|
||||
getComputedLinkColor,
|
||||
makeBlob,
|
||||
isBS3,
|
||||
};
|
||||
|
||||
@@ -1,376 +0,0 @@
|
||||
# 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>
|
||||
|
||||
---
|
||||
|
||||
Code
|
||||
bslib_tags(x)
|
||||
Output
|
||||
<div class="tabbable card">
|
||||
<div class="card-header">
|
||||
<ul class="nav nav-tabs card-header-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>
|
||||
<div class="card-body">
|
||||
<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>
|
||||
</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,89 +1,4 @@
|
||||
# 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)
|
||||
card <- tabset_panel(!!!panels, card = TRUE)
|
||||
expect_snapshot_bslib(card)
|
||||
})
|
||||
|
||||
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")))
|
||||
|
||||
Reference in New Issue
Block a user