mirror of
https://github.com/rstudio/shiny.git
synced 2026-01-12 08:27:56 -05:00
Compare commits
25 Commits
wch-css-lo
...
navtreePan
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
006cd8ed5e | ||
|
|
88cdd607b0 | ||
|
|
f9403c5e66 | ||
|
|
d04c7ae6ed | ||
|
|
7811f06e70 | ||
|
|
94a9b8a5f1 | ||
|
|
8ce0068f1d | ||
|
|
c8d76cf48f | ||
|
|
6159259699 | ||
|
|
9790b8f716 | ||
|
|
f9d82f17af | ||
|
|
3950f0e7fe | ||
|
|
886d369e7c | ||
|
|
ec97fe731d | ||
|
|
f8476973e5 | ||
|
|
2bde956fc9 | ||
|
|
2e33634726 | ||
|
|
f9ee5fcca2 | ||
|
|
124a89d7b4 | ||
|
|
1d608d2e25 | ||
|
|
f63d50d1b5 | ||
|
|
f8e09c15d1 | ||
|
|
959e4e41da | ||
|
|
36e679a9af | ||
|
|
c6693ead30 |
@@ -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", comment = c(ORCID = "0000-0002-1576-2126")),
|
||||
person("Winston", "Chang", role = c("aut", "cre"), email = "winston@rstudio.com"),
|
||||
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", comment = c(ORCID = "0000-0002-4958-2844")),
|
||||
person("Barret", "Schloerke", role = "aut", email = "barret@rstudio.com", comment = c(ORCID = "0000-0001-9986-114X")),
|
||||
person("Carson", "Sievert", role = "aut", email = "carson@rstudio.com"),
|
||||
person("Barret", "Schloerke", role = "aut", email = "barret@rstudio.com"),
|
||||
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"),
|
||||
@@ -170,6 +170,7 @@ Collate:
|
||||
'mock-session.R'
|
||||
'modal.R'
|
||||
'modules.R'
|
||||
'navtreePanel.R'
|
||||
'notifications.R'
|
||||
'priorityqueue.R'
|
||||
'progress.R'
|
||||
|
||||
@@ -175,6 +175,7 @@ export(moduleServer)
|
||||
export(navbarMenu)
|
||||
export(navbarPage)
|
||||
export(navlistPanel)
|
||||
export(navtreePanel)
|
||||
export(nearPoints)
|
||||
export(need)
|
||||
export(ns.sep)
|
||||
@@ -222,7 +223,6 @@ export(reactlogReset)
|
||||
export(reactlogShow)
|
||||
export(registerInputHandler)
|
||||
export(registerThemeDependency)
|
||||
export(register_devmode_option)
|
||||
export(removeInputHandler)
|
||||
export(removeModal)
|
||||
export(removeNotification)
|
||||
|
||||
6
NEWS.md
6
NEWS.md
@@ -17,7 +17,7 @@ shiny 1.6.0.9000
|
||||
* 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)
|
||||
@@ -28,10 +28,6 @@ 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)
|
||||
|
||||
@@ -43,7 +43,6 @@ 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.")
|
||||
@@ -1014,12 +1013,9 @@ buildTabItem <- function(index, tabsetId, foundSelected, tabs = NULL,
|
||||
|
||||
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"]])
|
||||
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
|
||||
|
||||
@@ -233,7 +233,6 @@ 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()`].
|
||||
@@ -244,7 +243,6 @@ 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
|
||||
|
||||
159
R/navtreePanel.R
Normal file
159
R/navtreePanel.R
Normal file
@@ -0,0 +1,159 @@
|
||||
#' @export
|
||||
navtreePanel <- function(..., id = NULL,
|
||||
selected = NULL,
|
||||
fluid = TRUE,
|
||||
# Also allow for string to determine padding in a flex layout?
|
||||
widths = c(3, 9)) {
|
||||
# TODO: how to incorporate this into a sidebar layout?
|
||||
|
||||
if (!is.null(id))
|
||||
selected <- restoreInput(id = id, default = selected)
|
||||
|
||||
tabset <- buildTreePanel(..., ulClass = "nav nav-navtree", id = id, selected = selected)
|
||||
|
||||
row <- if (fluid) fluidRow else fixedRow
|
||||
|
||||
navList <- attachDependencies(
|
||||
tabset$navList,
|
||||
bslib::bs_dependency_defer(navtreeCssDependency)
|
||||
)
|
||||
|
||||
row(
|
||||
column(widths[[1]], navList),
|
||||
column(widths[[2]], tabset$content)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
# Algorithm inspired by buildTabset() but we need different HTML/CSS,
|
||||
# and menus are rendered as collapse toggles instead of Bootstrap dropdowns
|
||||
buildTreePanel <- function(..., ulClass, id = NULL, selected = NULL, foundSelected = FALSE, depth = 0) {
|
||||
|
||||
tabs <- list2(...)
|
||||
res <- findAndMarkSelectedTab(tabs, selected, foundSelected)
|
||||
tabs <- res$tabs
|
||||
foundSelected <- res$foundSelected
|
||||
|
||||
# add input class if we have an id
|
||||
if (!is.null(id)) ulClass <- paste(ulClass, "shiny-tab-input")
|
||||
|
||||
if (anyNamed(tabs)) {
|
||||
nms <- names(tabs)
|
||||
nms <- nms[nzchar(nms)]
|
||||
stop("Tabs should all be unnamed arguments, but some are named: ",
|
||||
paste(nms, collapse = ", "))
|
||||
}
|
||||
|
||||
tabsetId <- p_randomInt(1000, 10000)
|
||||
tabs <- lapply(
|
||||
seq_len(length(tabs)), buildTreeItem,
|
||||
tabsetId = tabsetId,
|
||||
foundSelected = foundSelected,
|
||||
tabs = tabs, depth = depth
|
||||
)
|
||||
|
||||
list(
|
||||
navList = tags$ul(
|
||||
class = ulClass, id = id,
|
||||
`data-tabsetid` = tabsetId,
|
||||
!!!lapply(tabs, "[[", "liTag")
|
||||
),
|
||||
content = div(
|
||||
class = "tab-content",
|
||||
`data-tabsetid` = tabsetId,
|
||||
!!!lapply(tabs, "[[", "divTag")
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
buildTreeItem <- function(index, tabsetId, foundSelected, tabs = NULL, divTag = NULL, depth = 0) {
|
||||
divTag <- divTag %||% tabs[[index]]
|
||||
|
||||
subMenuPadding <- if (depth > 0) css(padding_left = paste0(depth * 1.25, "rem"))
|
||||
|
||||
if (isNavbarMenu(divTag)) {
|
||||
icon <- getIcon(iconClass = divTag$iconClass)
|
||||
if (!is.null(icon)) {
|
||||
warning("Configurable icons are not yet supported in navtreePanel().")
|
||||
}
|
||||
tabset <- buildTreePanel(
|
||||
!!!divTag$tabs, ulClass = "nav nav-navtree",
|
||||
foundSelected = foundSelected, depth = depth + 1
|
||||
)
|
||||
# Sort of like .dropdown in the tabsetPanel() case,
|
||||
# but utilizes collapsing (which is recursive) instead of dropdown
|
||||
active <- containsSelectedTab(divTag$tabs)
|
||||
menuId <- paste0("collapse-", p_randomInt(1000, 10000))
|
||||
liTag <- tags$li(
|
||||
tags$a(
|
||||
class = if (!active) "collapsed",
|
||||
"data-toggle" = "collapse",
|
||||
"data-value" = divTag$menuName,
|
||||
"data-target" = paste0("#", menuId),
|
||||
role = "button",
|
||||
style = subMenuPadding,
|
||||
getIcon(iconClass = divTag$iconClass),
|
||||
divTag$title
|
||||
),
|
||||
div(
|
||||
class = "collapse",
|
||||
class = if (active) "show in",
|
||||
id = menuId,
|
||||
tabset$navList
|
||||
)
|
||||
)
|
||||
return(list(liTag = liTag, divTag = tabset$content$children))
|
||||
}
|
||||
|
||||
if (isTabPanel(divTag)) {
|
||||
# Borrow from the usual nav logic so we get the right
|
||||
# li.active vs li > a.active (BS4) markup
|
||||
navItem <- buildNavItem(divTag, tabsetId, index)
|
||||
liTag <- navItem$liTag
|
||||
return(
|
||||
list(
|
||||
divTag = navItem$divTag,
|
||||
liTag = tagFunction(function() {
|
||||
# Incoming liTag should be a tagFunction()
|
||||
liTag <- if (inherits(liTag, "shiny.tag.function")) liTag() else liTag
|
||||
|
||||
# Add padding for sub menu anchors
|
||||
liTag$children[[1]] <- tagAppendAttributes(
|
||||
liTag$children[[1]], style = subMenuPadding
|
||||
)
|
||||
|
||||
liTag
|
||||
})
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
abort("navtreePanel() items must be tabPanel()s and/or tabPanelMenu()s")
|
||||
}
|
||||
|
||||
|
||||
navtreeCssDependency <- function(theme) {
|
||||
name <- "navtreePanel"
|
||||
version <- packageVersion("shiny")
|
||||
if (!is_bs_theme(theme)) {
|
||||
# TODO: Should we allow navtreePanel() to be statically rendered?
|
||||
# Can/should we move away from href="shared/*"?
|
||||
htmlDependency(
|
||||
name = name, version = version,
|
||||
src = c(href = "shared/navtree", file = "www/shared/navtree"),
|
||||
package = "shiny",
|
||||
stylesheet = "navtree.css",
|
||||
script = "navtree.js"
|
||||
)
|
||||
} else {
|
||||
navtree <- system.file(package = "shiny", "www/shared/navtree")
|
||||
bslib::bs_dependency(
|
||||
sass::sass_file(file.path(navtree, "navtree.scss")),
|
||||
theme = theme,
|
||||
name = name,
|
||||
version = version,
|
||||
cache_key_extra = version,
|
||||
.dep_args = list(script = file.path(navtree, "navtree.js"))
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -2175,7 +2175,7 @@ ShinySession <- R6Class(
|
||||
if (getOption("shiny.allowoutputreads", FALSE)) {
|
||||
.subset2(x, 'impl')$getOutput(name)
|
||||
} else {
|
||||
rlang::abort(paste0("Can't read output '", name, "'"))
|
||||
rlang::abort(paste0("Can't read output '", output, "'"))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
48
R/shinyui.R
48
R/shinyui.R
@@ -47,8 +47,27 @@ 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(jqueryDependency()),
|
||||
list(jquery()),
|
||||
shinyDependencies()
|
||||
)
|
||||
|
||||
@@ -63,33 +82,6 @@ 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,10 +538,9 @@ installExprFunction <- function(expr, name, eval.env = parent.frame(2),
|
||||
#' @export
|
||||
quoToFunction <- function(q, label, ..stacktraceon = FALSE) {
|
||||
q <- as_quosure(q)
|
||||
func <- as_function(q)
|
||||
# as_function returns a function that takes `...`. We want one that takes no
|
||||
# args.
|
||||
formals(func) <- list()
|
||||
# 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))
|
||||
wrapFunctionLabel(func, label, ..stacktraceon = ..stacktraceon)
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
50
inst/www/shared/navtree/navtree.css
Normal file
50
inst/www/shared/navtree/navtree.css
Normal file
@@ -0,0 +1,50 @@
|
||||
.nav-navtree {
|
||||
display: block !important;
|
||||
}
|
||||
|
||||
.nav-navtree li.active, .nav-navtree li > a.active, .nav-navtree li a:focus {
|
||||
font-weight: 600;
|
||||
background-color: rgba(0, 123, 255, 0.3) !important;
|
||||
color: rgba(33, 37, 41, 0.85);
|
||||
}
|
||||
|
||||
.nav-navtree li:not(.active) > a:not(.active):hover {
|
||||
background-color: rgba(33, 37, 41, 0.1);
|
||||
}
|
||||
|
||||
.nav-navtree li a {
|
||||
display: inline-flex !important;
|
||||
align-items: center !important;
|
||||
width: 100% !important;
|
||||
padding: .1875rem .5rem;
|
||||
border: 1px solid #dee2e6 !important;
|
||||
color: rgba(33, 37, 41, 0.65) !important;
|
||||
text-decoration: none !important;
|
||||
}
|
||||
|
||||
.nav-navtree li a i {
|
||||
padding-right: 1.25rem !important;
|
||||
}
|
||||
|
||||
.nav-navtree li a[data-toggle="collapse"]::before {
|
||||
width: 1.25em;
|
||||
line-height: 0;
|
||||
content: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='rgba%2833, 37, 41, 0.5%29' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M5 14l6-6-6-6'/%3e%3c/svg%3e");
|
||||
transition: transform 0.35s ease;
|
||||
transform-origin: .5em 50%;
|
||||
padding-right: 1rem;
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.nav-navtree li a[data-toggle="collapse"]::before {
|
||||
transition: none;
|
||||
}
|
||||
}
|
||||
|
||||
.nav-navtree li a[data-toggle="collapse"]:not(.collapsed) {
|
||||
color: rgba(33, 37, 41, 0.85);
|
||||
}
|
||||
|
||||
.nav-navtree li a[data-toggle="collapse"]:not(.collapsed)::before {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
11
inst/www/shared/navtree/navtree.js
Normal file
11
inst/www/shared/navtree/navtree.js
Normal file
@@ -0,0 +1,11 @@
|
||||
// nav-navtree is built on a combination of Bootstrap's tab &
|
||||
// collapse components, but the tab component isn't smart enough to
|
||||
// know about the deactive when are activated. Note that this logic also
|
||||
// exists in input_binding_tabinput.js and is repeated here in case this
|
||||
// component wants to be statically rendered
|
||||
$(document).on("shown.bs.tab", ".nav-navtree", function(e) {
|
||||
var tgt = $(e.target);
|
||||
var nav = tgt.parents(".nav-navtree");
|
||||
nav.find("li").not(tgt).removeClass("active"); // BS3
|
||||
nav.find("li > a").not(tgt).removeClass("active"); // BS4
|
||||
});
|
||||
66
inst/www/shared/navtree/navtree.scss
Normal file
66
inst/www/shared/navtree/navtree.scss
Normal file
@@ -0,0 +1,66 @@
|
||||
// Inspired by BS5's sidebar nav
|
||||
// https://github.com/twbs/bootstrap/blob/548be2e/site/assets/scss/_sidebar.scss#L7
|
||||
// As well as
|
||||
// https://chniter.github.io/bstreeview/
|
||||
|
||||
$body-color: $text-color !default; // BS3compat
|
||||
$link-decoration: none !default;
|
||||
$link-hover-decoration: underline !default;
|
||||
|
||||
$sidebar-collapse-icon-color: rgba($body-color, 0.5) !default;
|
||||
$sidebar-collapse-icon: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 16 16'><path fill='none' stroke='#{$sidebar-collapse-icon-color}' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M5 14l6-6-6-6'/></svg>") !default;
|
||||
|
||||
// TODO: make this more configurable from the R side?
|
||||
$navtree-anchor-color: rgba($body-color, .65) !default;
|
||||
$navtree-active-bg: rgba($component-active-bg, .3) !default;
|
||||
$navtree-active-color: rgba($body-color, .85) !default;
|
||||
|
||||
.nav-navtree {
|
||||
// Override collapse behaviors
|
||||
display: block !important;
|
||||
|
||||
li {
|
||||
// Handle both li.active (BS3) and li > a.active (BS4)
|
||||
&.active, > a.active, a:focus {
|
||||
font-weight: 600;
|
||||
background-color: $navtree-active-bg !important;
|
||||
color: $navtree-active-color;
|
||||
}
|
||||
&:not(.active) > a:not(.active):hover {
|
||||
background-color: rgba($body-color, .1);
|
||||
}
|
||||
|
||||
a {
|
||||
display: inline-flex !important; // Needed for centering/transforming the collapse icon
|
||||
align-items: center !important;
|
||||
width: 100% !important;
|
||||
padding: .1875rem .5rem;
|
||||
border: $border-width solid $border-color !important;
|
||||
color: $navtree-anchor-color !important;
|
||||
text-decoration: none !important;
|
||||
i {
|
||||
padding-right: 1.25rem !important;
|
||||
}
|
||||
// Add chevron if there's a submenu
|
||||
&[data-toggle="collapse"] {
|
||||
&::before {
|
||||
width: 1.25em;
|
||||
line-height: 0; // Align in the middle
|
||||
content: escape-svg($sidebar-collapse-icon);
|
||||
@include transition(transform .35s ease);
|
||||
transform-origin: .5em 50%;
|
||||
padding-right: 1rem;
|
||||
}
|
||||
|
||||
&:not(.collapsed) {
|
||||
color: $navtree-active-color;
|
||||
&::before {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
} // a
|
||||
|
||||
} // li
|
||||
|
||||
} // nav
|
||||
@@ -1 +1 @@
|
||||
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)}}()});
|
||||
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)})});
|
||||
@@ -3086,6 +3086,22 @@
|
||||
$tabContent[0].appendChild(el);
|
||||
Shiny.renderContent(el, el.innerHTML || el.textContent);
|
||||
});
|
||||
if ($tabset.hasClass("nav-navtree") && $liTag.hasClass("dropdown")) {
|
||||
var collapseId = "collapse-" + tabsetId + "-" + getTabIndex($tabset, tabsetId);
|
||||
$tabset.find(".dropdown").each(function(i, el) {
|
||||
var $el = import_jquery6.default(el).removeClass("dropdown nav-item");
|
||||
$el.find(".dropdown-toggle").removeClass("dropdown-toggle nav-link").addClass(message.select ? "" : "collapsed").attr("data-toggle", "collapse").attr("data-target", "#" + collapseId);
|
||||
var collapse = import_jquery6.default("<div>").addClass("collapse" + (message.select ? " show" : "")).attr("id", collapseId);
|
||||
var menu = $el.find(".dropdown-menu").removeClass("dropdown-menu").addClass("nav nav-navtree").wrap(collapse);
|
||||
var depth = $el.parents(".nav-navtree").length - 1;
|
||||
if (depth > 0) {
|
||||
$el.find("a").css("padding-left", depth + "rem");
|
||||
}
|
||||
if (menu.find("li").length === 0) {
|
||||
menu.find("a").removeClass("dropdown-item").addClass("nav-link").wrap("<li class='nav-item'></li>");
|
||||
}
|
||||
});
|
||||
}
|
||||
if (message.select) {
|
||||
$liTag.find("a").tab("show");
|
||||
}
|
||||
@@ -4730,7 +4746,7 @@
|
||||
if (!restyle) {
|
||||
$head.append(links);
|
||||
} else {
|
||||
var refreshStyle = function refreshStyle2(href2, oldSheet, onload) {
|
||||
var refreshStyle = function refreshStyle2(href2, oldSheet) {
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open("GET", href2);
|
||||
xhr.onload = function() {
|
||||
@@ -4744,7 +4760,6 @@
|
||||
setTimeout(function() {
|
||||
return removeSheet(oldSheet);
|
||||
}, 500);
|
||||
onload();
|
||||
};
|
||||
xhr.send();
|
||||
};
|
||||
@@ -4769,29 +4784,21 @@
|
||||
var oldSheet = findSheet(link.attr("href"));
|
||||
var href2 = link.attr("href") + "?restyle=" + new Date().getTime();
|
||||
if (isIE()) {
|
||||
refreshStyle(href2, oldSheet, scheduleCssReporter);
|
||||
refreshStyle(href2, oldSheet);
|
||||
} else {
|
||||
link.attr("href", href2);
|
||||
link.attr("onload", function() {
|
||||
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);
|
||||
setTimeout(function() {
|
||||
return removeSheet(oldSheet);
|
||||
}, 500);
|
||||
});
|
||||
$head.append(link);
|
||||
}
|
||||
});
|
||||
var bindDebouncer = new Debouncer(null, Shiny.bindAll, 100);
|
||||
setTimeout(function() {
|
||||
return bindDebouncer.normalCall();
|
||||
}, 100);
|
||||
}
|
||||
}
|
||||
if (dep.script && !restyle) {
|
||||
@@ -6117,6 +6124,7 @@
|
||||
anchors.each(function() {
|
||||
if (self2._getTabName(import_jquery6.default(this)) === value) {
|
||||
import_jquery6.default(this).tab("show");
|
||||
import_jquery6.default(this).parents(".collapse").collapse("show");
|
||||
success = true;
|
||||
return false;
|
||||
}
|
||||
@@ -6138,7 +6146,9 @@
|
||||
import_jquery6.default(el).trigger("change");
|
||||
},
|
||||
subscribe: function subscribe(el, callback) {
|
||||
var deactivateOtherTabs = this._deactivateOtherTabs;
|
||||
import_jquery6.default(el).on("change shown.bootstrapTabInputBinding shown.bs.tab.bootstrapTabInputBinding", function(event) {
|
||||
deactivateOtherTabs(event);
|
||||
callback();
|
||||
});
|
||||
},
|
||||
@@ -6147,6 +6157,12 @@
|
||||
},
|
||||
_getTabName: function _getTabName(anchor) {
|
||||
return anchor.attr("data-value") || anchor.text();
|
||||
},
|
||||
_deactivateOtherTabs: function _deactivateOtherTabs(event) {
|
||||
var tgt = import_jquery6.default(event.target);
|
||||
var nav = tgt.parents(".nav-navtree");
|
||||
nav.find("li").not(tgt).removeClass("active");
|
||||
nav.find("li > a").not(tgt).removeClass("active");
|
||||
}
|
||||
});
|
||||
inputBindings.register(bootstrapTabInputBinding, "shiny.bootstrapTabInput");
|
||||
@@ -6449,7 +6465,6 @@
|
||||
}
|
||||
});
|
||||
inputBindings.register(fileInputBinding, "shiny.fileInputBinding");
|
||||
var sendImageSize;
|
||||
function initShiny() {
|
||||
var shinyapp = Shiny.shinyapp = new ShinyApp();
|
||||
function bindOutputs() {
|
||||
@@ -6774,9 +6789,9 @@
|
||||
});
|
||||
}
|
||||
var sendImageSizeDebouncer = new Debouncer(null, doSendImageSize, 0);
|
||||
sendImageSize = function sendImageSize() {
|
||||
function sendImageSize() {
|
||||
sendImageSizeDebouncer.normalCall();
|
||||
};
|
||||
}
|
||||
inputBatchSender.lastChanceCallback.push(function() {
|
||||
if (sendImageSizeDebouncer.isPending())
|
||||
sendImageSizeDebouncer.immediateCall();
|
||||
@@ -7787,10 +7802,10 @@
|
||||
return linkColor;
|
||||
}
|
||||
function isBS3() {
|
||||
if (!import_jquery5.default.fn.tab) {
|
||||
if (!import_jquery5.default.fn.tooltip) {
|
||||
return false;
|
||||
}
|
||||
return import_jquery5.default.fn.tab.Constructor.VERSION.match(/^3\./);
|
||||
return import_jquery5.default.fn.tooltip.Constructor.VERSION.match(/^3\./);
|
||||
}
|
||||
|
||||
// src/shiny.ts
|
||||
|
||||
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
@@ -1,63 +0,0 @@
|
||||
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,11 +42,10 @@
|
||||
},
|
||||
"scripts": {
|
||||
"watch": "yarn run build_shiny --watch",
|
||||
"build": "yarn run build_shiny && yarn run bundle_external_libs",
|
||||
"build": "yarn run build_shiny",
|
||||
"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",
|
||||
|
||||
@@ -1407,6 +1407,50 @@ function main(): void {
|
||||
Shiny.renderContent(el, el.innerHTML || el.textContent);
|
||||
});
|
||||
|
||||
// If we're inserting a navbarMenu() into a navtreePanel() target, we need
|
||||
// to transform buildTabset() output (i.e., a .dropdown component) to
|
||||
// buildTreePanel() output (i.e., a .collapse component), because
|
||||
// insertTab() et al. doesn't know about the relevant tabset container
|
||||
if ($tabset.hasClass("nav-navtree") && $liTag.hasClass("dropdown")) {
|
||||
let collapseId =
|
||||
"collapse-" + tabsetId + "-" + getTabIndex($tabset, tabsetId);
|
||||
|
||||
$tabset.find(".dropdown").each(function (i, el) {
|
||||
let $el = $(el).removeClass("dropdown nav-item");
|
||||
|
||||
$el
|
||||
.find(".dropdown-toggle")
|
||||
.removeClass("dropdown-toggle nav-link")
|
||||
.addClass(message.select ? "" : "collapsed")
|
||||
.attr("data-toggle", "collapse")
|
||||
.attr("data-target", "#" + collapseId);
|
||||
|
||||
let collapse = $("<div>")
|
||||
.addClass("collapse" + (message.select ? " show" : ""))
|
||||
.attr("id", collapseId);
|
||||
|
||||
let menu = $el
|
||||
.find(".dropdown-menu")
|
||||
.removeClass("dropdown-menu")
|
||||
.addClass("nav nav-navtree")
|
||||
.wrap(collapse);
|
||||
|
||||
let depth = $el.parents(".nav-navtree").length - 1;
|
||||
|
||||
if (depth > 0) {
|
||||
$el.find("a").css("padding-left", depth + "rem");
|
||||
}
|
||||
|
||||
if (menu.find("li").length === 0) {
|
||||
menu
|
||||
.find("a")
|
||||
.removeClass("dropdown-item")
|
||||
.addClass("nav-link")
|
||||
.wrap("<li class='nav-item'></li>");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (message.select) {
|
||||
$liTag.find("a").tab("show");
|
||||
}
|
||||
@@ -4040,7 +4084,7 @@ function main(): void {
|
||||
$head.append(links);
|
||||
} else {
|
||||
// This inline <style> based approach works for IE11
|
||||
let refreshStyle = function (href, oldSheet, onload) {
|
||||
let refreshStyle = function (href, oldSheet) {
|
||||
const xhr = new XMLHttpRequest();
|
||||
|
||||
xhr.open("GET", href);
|
||||
@@ -4053,7 +4097,6 @@ function main(): void {
|
||||
$head.append(newStyle);
|
||||
setTimeout(() => oldStyle.remove(), 500);
|
||||
setTimeout(() => removeSheet(oldSheet), 500);
|
||||
onload();
|
||||
};
|
||||
xhr.send();
|
||||
};
|
||||
@@ -4090,43 +4133,26 @@ function main(): void {
|
||||
// <link> -based approach
|
||||
|
||||
if (isIE()) {
|
||||
refreshStyle(href, oldSheet, scheduleCssReporter);
|
||||
refreshStyle(href, oldSheet);
|
||||
} 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", () => {
|
||||
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);
|
||||
setTimeout(() => removeSheet(oldSheet), 500);
|
||||
});
|
||||
$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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5943,6 +5969,8 @@ function main(): void {
|
||||
anchors.each(function () {
|
||||
if (self._getTabName($(this)) === value) {
|
||||
$(this).tab("show");
|
||||
// navtreePanel()'s navbarMenu() uses collapsing -- expand the menu when activated!
|
||||
$(this).parents(".collapse").collapse("show");
|
||||
success = true;
|
||||
return false; // Break out of each()
|
||||
}
|
||||
@@ -5963,9 +5991,12 @@ function main(): void {
|
||||
$(el).trigger("change");
|
||||
},
|
||||
subscribe: function (el, callback) {
|
||||
let deactivateOtherTabs = this._deactivateOtherTabs;
|
||||
|
||||
$(el).on(
|
||||
"change shown.bootstrapTabInputBinding shown.bs.tab.bootstrapTabInputBinding",
|
||||
function (event) {
|
||||
deactivateOtherTabs(event);
|
||||
callback();
|
||||
}
|
||||
);
|
||||
@@ -5976,6 +6007,17 @@ function main(): void {
|
||||
_getTabName: function (anchor) {
|
||||
return anchor.attr("data-value") || anchor.text();
|
||||
},
|
||||
// nav-navtree is built on a combination of Bootstrap's tab &
|
||||
// collapse components, but the tab component isn't smart enough to
|
||||
// know about the deactive when are activated. Note that this logic is
|
||||
// very similar to shinydashboard's deactivateOtherTabs() (in tab.js)
|
||||
_deactivateOtherTabs: function (event) {
|
||||
let tgt = $(event.target);
|
||||
let nav = tgt.parents(".nav-navtree");
|
||||
|
||||
nav.find("li").not(tgt).removeClass("active"); // BS3
|
||||
nav.find("li > a").not(tgt).removeClass("active"); // BS4
|
||||
},
|
||||
});
|
||||
inputBindings.register(bootstrapTabInputBinding, "shiny.bootstrapTabInput");
|
||||
|
||||
@@ -6407,10 +6449,6 @@ 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());
|
||||
@@ -6893,9 +6931,9 @@ function main(): void {
|
||||
}
|
||||
const sendImageSizeDebouncer = new Debouncer(null, doSendImageSize, 0);
|
||||
|
||||
sendImageSize = function () {
|
||||
function sendImageSize() {
|
||||
sendImageSizeDebouncer.normalCall();
|
||||
};
|
||||
}
|
||||
// Make sure sendImageSize actually gets called before the inputBatchSender
|
||||
// sends data to the server.
|
||||
inputBatchSender.lastChanceCallback.push(function () {
|
||||
|
||||
@@ -364,12 +364,12 @@ function getComputedLinkColor(el: HTMLElement): string {
|
||||
function isBS3(): boolean {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
if (!$.fn.tab) {
|
||||
if (!$.fn.tooltip) {
|
||||
return false;
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
return $.fn.tab.Constructor.VERSION.match(/^3\./);
|
||||
return $.fn.tooltip.Constructor.VERSION.match(/^3\./);
|
||||
}
|
||||
|
||||
export {
|
||||
|
||||
@@ -96,19 +96,3 @@ 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,15 +239,3 @@ 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 bundle_external_libs"))
|
||||
withr::with_dir(rprojroot::find_package_root_file("tools"), system("yarn build"))
|
||||
|
||||
15
tools/updateNavTreeList.R
Normal file
15
tools/updateNavTreeList.R
Normal file
@@ -0,0 +1,15 @@
|
||||
library(sass)
|
||||
home <- rprojroot::find_package_root_file("inst/www/shared/navtree")
|
||||
# TODO: write a unit test
|
||||
withr::with_dir(
|
||||
home, {
|
||||
sass_partial(
|
||||
sass_file("navtree.scss"),
|
||||
bslib::bs_theme(),
|
||||
output = "navtree.css",
|
||||
cache = FALSE,
|
||||
write_attachments = FALSE
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user