mirror of
https://github.com/rstudio/shiny.git
synced 2026-01-11 16:08:19 -05:00
Compare commits
2 Commits
navtreePan
...
switchInpu
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0be84710fd | ||
|
|
967078ebbb |
@@ -156,6 +156,7 @@ Collate:
|
||||
'input-select.R'
|
||||
'input-slider.R'
|
||||
'input-submit.R'
|
||||
'input-switch.R'
|
||||
'input-text.R'
|
||||
'input-textarea.R'
|
||||
'input-utils.R'
|
||||
@@ -170,7 +171,6 @@ Collate:
|
||||
'mock-session.R'
|
||||
'modal.R'
|
||||
'modules.R'
|
||||
'navtreePanel.R'
|
||||
'notifications.R'
|
||||
'priorityqueue.R'
|
||||
'progress.R'
|
||||
|
||||
@@ -175,7 +175,6 @@ export(moduleServer)
|
||||
export(navbarMenu)
|
||||
export(navbarPage)
|
||||
export(navlistPanel)
|
||||
export(navtreePanel)
|
||||
export(nearPoints)
|
||||
export(need)
|
||||
export(ns.sep)
|
||||
@@ -281,6 +280,7 @@ export(stopApp)
|
||||
export(strong)
|
||||
export(submitButton)
|
||||
export(suppressDependencies)
|
||||
export(switchInput)
|
||||
export(tabPanel)
|
||||
export(tabPanelBody)
|
||||
export(tableOutput)
|
||||
@@ -315,6 +315,7 @@ export(updateRadioButtons)
|
||||
export(updateSelectInput)
|
||||
export(updateSelectizeInput)
|
||||
export(updateSliderInput)
|
||||
export(updateSwitchInput)
|
||||
export(updateTabsetPanel)
|
||||
export(updateTextAreaInput)
|
||||
export(updateTextInput)
|
||||
|
||||
@@ -91,10 +91,6 @@ getLang <- function(ui) {
|
||||
#' @export
|
||||
bootstrapLib <- function(theme = NULL) {
|
||||
tagFunction(function() {
|
||||
if (isRunning()) {
|
||||
setCurrentTheme(theme)
|
||||
}
|
||||
|
||||
# If we're not compiling Bootstrap Sass (from bslib), return the
|
||||
# static Bootstrap build.
|
||||
if (!is_bs_theme(theme)) {
|
||||
@@ -116,6 +112,7 @@ bootstrapLib <- function(theme = NULL) {
|
||||
# Note also that since this is shinyOptions() (and not options()), the
|
||||
# option is automatically reset when the app (or session) exits
|
||||
if (isRunning()) {
|
||||
setCurrentTheme(theme)
|
||||
registerThemeDependency(bs_theme_deps)
|
||||
|
||||
} else {
|
||||
|
||||
65
R/input-switch.R
Normal file
65
R/input-switch.R
Normal file
@@ -0,0 +1,65 @@
|
||||
#' Switch Input Control
|
||||
#'
|
||||
#' Create a switch for toggling a logical value.
|
||||
#'
|
||||
#' @inheritParams checkboxInput
|
||||
#' @return A switch control that can be added to a UI definition.
|
||||
#'
|
||||
#' @family input elements
|
||||
#' @seealso [checkboxInput()], [updateSwitchInput()]
|
||||
#'
|
||||
#' @examples
|
||||
#' ## Only run examples in interactive R sessions
|
||||
#' if (interactive()) {
|
||||
#' ui <- fluidPage(
|
||||
#' switchInput("somevalue", "Some value", FALSE),
|
||||
#' verbatimTextOutput("value")
|
||||
#' )
|
||||
#' server <- function(input, output) {
|
||||
#' output$value <- renderText({ input$somevalue })
|
||||
#' }
|
||||
#' shinyApp(ui, server)
|
||||
#' }
|
||||
#'
|
||||
#' @section Server value:
|
||||
#' `TRUE` if checked, `FALSE` otherwise.
|
||||
#'
|
||||
#' @export
|
||||
switchInput <- function(inputId, label, value = FALSE, width = NULL) {
|
||||
|
||||
value <- restoreInput(id = inputId, default = value)
|
||||
|
||||
inputTag <- tags$input(
|
||||
id = inputId, type = "checkbox",
|
||||
checked = if (isTRUE(value)) "checked"
|
||||
)
|
||||
|
||||
# TODO: checkboxInput() should do this too (for accessibility)?
|
||||
labelTag <- shinyInputLabel(inputId, label)
|
||||
|
||||
tagFunction(function() {
|
||||
if (getCurrentVersion() < 4) {
|
||||
stop(
|
||||
"switchInput() 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
|
||||
)
|
||||
}
|
||||
|
||||
isBS4 <- getCurrentVersion() == 4
|
||||
div(
|
||||
class = "shiny-input-container",
|
||||
style = css(width = validateCssUnit(width)),
|
||||
div(
|
||||
class = if (isBS4) "custom-control custom-switch" else "form-check form-switch",
|
||||
tagAppendAttributes(
|
||||
inputTag, class = if (isBS4) "custom-control-input" else "form-check-input"
|
||||
),
|
||||
tagAppendAttributes(
|
||||
labelTag, class = if (isBS4) "custom-control-label" else "form-check-label"
|
||||
)
|
||||
)
|
||||
)
|
||||
})
|
||||
}
|
||||
159
R/navtreePanel.R
159
R/navtreePanel.R
@@ -1,159 +0,0 @@
|
||||
#' @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"))
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -115,6 +115,12 @@ updateCheckboxInput <- function(session = getDefaultReactiveDomain(), inputId, l
|
||||
session$sendInputMessage(inputId, message)
|
||||
}
|
||||
|
||||
#' @rdname updateCheckboxInput
|
||||
#' @export
|
||||
updateSwitchInput <- function(session = getDefaultReactiveDomain(), inputId, label = NULL, value = NULL) {
|
||||
updateCheckboxInput(session = session, inputId = inputId, label = label, value = value)
|
||||
}
|
||||
|
||||
|
||||
#' Change the label or icon of an action button on the client
|
||||
#'
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
.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);
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
// 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
|
||||
});
|
||||
@@ -1,66 +0,0 @@
|
||||
// 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
|
||||
@@ -3086,22 +3086,6 @@
|
||||
$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");
|
||||
}
|
||||
@@ -3143,9 +3127,9 @@
|
||||
}
|
||||
});
|
||||
function ensureTabsetHasVisibleTab($tabset) {
|
||||
var inputBinding = $tabset.data("shiny-input-binding");
|
||||
if (!inputBinding.getValue($tabset)) {
|
||||
if ($tabset.find("li.active").not(".dropdown").length === 0) {
|
||||
var destTabValue = getFirstTab($tabset);
|
||||
var inputBinding = $tabset.data("shiny-input-binding");
|
||||
var evt = jQuery.Event("shiny:updateinput");
|
||||
evt.binding = inputBinding;
|
||||
$tabset.trigger(evt);
|
||||
@@ -5179,7 +5163,7 @@
|
||||
},
|
||||
getState: function getState(el) {
|
||||
return {
|
||||
label: import_jquery6.default(el).parent().find("span").text(),
|
||||
label: import_jquery6.default(el).parent().find("span, label").text(),
|
||||
value: el.checked
|
||||
};
|
||||
},
|
||||
@@ -5187,7 +5171,7 @@
|
||||
if (data.hasOwnProperty("value"))
|
||||
el.checked = data.value;
|
||||
if (data.hasOwnProperty("label"))
|
||||
import_jquery6.default(el).parent().find("span").text(data.label);
|
||||
import_jquery6.default(el).parent().find("span, label").text(data.label);
|
||||
import_jquery6.default(el).trigger("change");
|
||||
}
|
||||
});
|
||||
@@ -6124,7 +6108,6 @@
|
||||
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;
|
||||
}
|
||||
@@ -6146,9 +6129,7 @@
|
||||
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();
|
||||
});
|
||||
},
|
||||
@@ -6157,12 +6138,6 @@
|
||||
},
|
||||
_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");
|
||||
|
||||
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
@@ -83,6 +83,7 @@ Other input elements:
|
||||
\code{\link{selectInput}()},
|
||||
\code{\link{sliderInput}()},
|
||||
\code{\link{submitButton}()},
|
||||
\code{\link{switchInput}()},
|
||||
\code{\link{textAreaInput}()},
|
||||
\code{\link{textInput}()},
|
||||
\code{\link{varSelectInput}()}
|
||||
|
||||
@@ -112,6 +112,7 @@ Other input elements:
|
||||
\code{\link{selectInput}()},
|
||||
\code{\link{sliderInput}()},
|
||||
\code{\link{submitButton}()},
|
||||
\code{\link{switchInput}()},
|
||||
\code{\link{textAreaInput}()},
|
||||
\code{\link{textInput}()},
|
||||
\code{\link{varSelectInput}()}
|
||||
|
||||
@@ -57,6 +57,7 @@ Other input elements:
|
||||
\code{\link{selectInput}()},
|
||||
\code{\link{sliderInput}()},
|
||||
\code{\link{submitButton}()},
|
||||
\code{\link{switchInput}()},
|
||||
\code{\link{textAreaInput}()},
|
||||
\code{\link{textInput}()},
|
||||
\code{\link{varSelectInput}()}
|
||||
|
||||
@@ -143,6 +143,7 @@ Other input elements:
|
||||
\code{\link{selectInput}()},
|
||||
\code{\link{sliderInput}()},
|
||||
\code{\link{submitButton}()},
|
||||
\code{\link{switchInput}()},
|
||||
\code{\link{textAreaInput}()},
|
||||
\code{\link{textInput}()},
|
||||
\code{\link{varSelectInput}()}
|
||||
|
||||
@@ -147,6 +147,7 @@ Other input elements:
|
||||
\code{\link{selectInput}()},
|
||||
\code{\link{sliderInput}()},
|
||||
\code{\link{submitButton}()},
|
||||
\code{\link{switchInput}()},
|
||||
\code{\link{textAreaInput}()},
|
||||
\code{\link{textInput}()},
|
||||
\code{\link{varSelectInput}()}
|
||||
|
||||
@@ -113,6 +113,7 @@ Other input elements:
|
||||
\code{\link{selectInput}()},
|
||||
\code{\link{sliderInput}()},
|
||||
\code{\link{submitButton}()},
|
||||
\code{\link{switchInput}()},
|
||||
\code{\link{textAreaInput}()},
|
||||
\code{\link{textInput}()},
|
||||
\code{\link{varSelectInput}()}
|
||||
|
||||
@@ -71,6 +71,7 @@ Other input elements:
|
||||
\code{\link{selectInput}()},
|
||||
\code{\link{sliderInput}()},
|
||||
\code{\link{submitButton}()},
|
||||
\code{\link{switchInput}()},
|
||||
\code{\link{textAreaInput}()},
|
||||
\code{\link{textInput}()},
|
||||
\code{\link{varSelectInput}()}
|
||||
|
||||
@@ -65,6 +65,7 @@ Other input elements:
|
||||
\code{\link{selectInput}()},
|
||||
\code{\link{sliderInput}()},
|
||||
\code{\link{submitButton}()},
|
||||
\code{\link{switchInput}()},
|
||||
\code{\link{textAreaInput}()},
|
||||
\code{\link{textInput}()},
|
||||
\code{\link{varSelectInput}()}
|
||||
|
||||
@@ -128,6 +128,7 @@ Other input elements:
|
||||
\code{\link{selectInput}()},
|
||||
\code{\link{sliderInput}()},
|
||||
\code{\link{submitButton}()},
|
||||
\code{\link{switchInput}()},
|
||||
\code{\link{textAreaInput}()},
|
||||
\code{\link{textInput}()},
|
||||
\code{\link{varSelectInput}()}
|
||||
|
||||
@@ -148,6 +148,7 @@ Other input elements:
|
||||
\code{\link{radioButtons}()},
|
||||
\code{\link{sliderInput}()},
|
||||
\code{\link{submitButton}()},
|
||||
\code{\link{switchInput}()},
|
||||
\code{\link{textAreaInput}()},
|
||||
\code{\link{textInput}()},
|
||||
\code{\link{varSelectInput}()}
|
||||
|
||||
@@ -151,6 +151,7 @@ Other input elements:
|
||||
\code{\link{radioButtons}()},
|
||||
\code{\link{selectInput}()},
|
||||
\code{\link{submitButton}()},
|
||||
\code{\link{switchInput}()},
|
||||
\code{\link{textAreaInput}()},
|
||||
\code{\link{textInput}()},
|
||||
\code{\link{varSelectInput}()}
|
||||
|
||||
@@ -76,6 +76,7 @@ Other input elements:
|
||||
\code{\link{radioButtons}()},
|
||||
\code{\link{selectInput}()},
|
||||
\code{\link{sliderInput}()},
|
||||
\code{\link{switchInput}()},
|
||||
\code{\link{textAreaInput}()},
|
||||
\code{\link{textInput}()},
|
||||
\code{\link{varSelectInput}()}
|
||||
|
||||
64
man/switchInput.Rd
Normal file
64
man/switchInput.Rd
Normal file
@@ -0,0 +1,64 @@
|
||||
% Generated by roxygen2: do not edit by hand
|
||||
% Please edit documentation in R/input-switch.R
|
||||
\name{switchInput}
|
||||
\alias{switchInput}
|
||||
\title{Switch Input Control}
|
||||
\usage{
|
||||
switchInput(inputId, label, value = FALSE, width = NULL)
|
||||
}
|
||||
\arguments{
|
||||
\item{inputId}{The \code{input} slot that will be used to access the value.}
|
||||
|
||||
\item{label}{Display label for the control, or \code{NULL} for no label.}
|
||||
|
||||
\item{value}{Initial value (\code{TRUE} or \code{FALSE}).}
|
||||
|
||||
\item{width}{The width of the input, e.g. \code{'400px'}, or \code{'100\%'};
|
||||
see \code{\link[=validateCssUnit]{validateCssUnit()}}.}
|
||||
}
|
||||
\value{
|
||||
A switch control that can be added to a UI definition.
|
||||
}
|
||||
\description{
|
||||
Create a switch for toggling a logical value.
|
||||
}
|
||||
\section{Server value}{
|
||||
|
||||
\code{TRUE} if checked, \code{FALSE} otherwise.
|
||||
}
|
||||
|
||||
\examples{
|
||||
## Only run examples in interactive R sessions
|
||||
if (interactive()) {
|
||||
ui <- fluidPage(
|
||||
switchInput("somevalue", "Some value", FALSE),
|
||||
verbatimTextOutput("value")
|
||||
)
|
||||
server <- function(input, output) {
|
||||
output$value <- renderText({ input$somevalue })
|
||||
}
|
||||
shinyApp(ui, server)
|
||||
}
|
||||
|
||||
}
|
||||
\seealso{
|
||||
\code{\link[=checkboxInput]{checkboxInput()}}, \code{\link[=updateSwitchInput]{updateSwitchInput()}}
|
||||
|
||||
Other input elements:
|
||||
\code{\link{actionButton}()},
|
||||
\code{\link{checkboxGroupInput}()},
|
||||
\code{\link{checkboxInput}()},
|
||||
\code{\link{dateInput}()},
|
||||
\code{\link{dateRangeInput}()},
|
||||
\code{\link{fileInput}()},
|
||||
\code{\link{numericInput}()},
|
||||
\code{\link{passwordInput}()},
|
||||
\code{\link{radioButtons}()},
|
||||
\code{\link{selectInput}()},
|
||||
\code{\link{sliderInput}()},
|
||||
\code{\link{submitButton}()},
|
||||
\code{\link{textAreaInput}()},
|
||||
\code{\link{textInput}()},
|
||||
\code{\link{varSelectInput}()}
|
||||
}
|
||||
\concept{input elements}
|
||||
@@ -91,6 +91,7 @@ Other input elements:
|
||||
\code{\link{selectInput}()},
|
||||
\code{\link{sliderInput}()},
|
||||
\code{\link{submitButton}()},
|
||||
\code{\link{switchInput}()},
|
||||
\code{\link{textInput}()},
|
||||
\code{\link{varSelectInput}()}
|
||||
}
|
||||
|
||||
@@ -63,6 +63,7 @@ Other input elements:
|
||||
\code{\link{selectInput}()},
|
||||
\code{\link{sliderInput}()},
|
||||
\code{\link{submitButton}()},
|
||||
\code{\link{switchInput}()},
|
||||
\code{\link{textAreaInput}()},
|
||||
\code{\link{varSelectInput}()}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
% Please edit documentation in R/update-input.R
|
||||
\name{updateCheckboxInput}
|
||||
\alias{updateCheckboxInput}
|
||||
\alias{updateSwitchInput}
|
||||
\title{Change the value of a checkbox input on the client}
|
||||
\usage{
|
||||
updateCheckboxInput(
|
||||
@@ -10,6 +11,13 @@ updateCheckboxInput(
|
||||
label = NULL,
|
||||
value = NULL
|
||||
)
|
||||
|
||||
updateSwitchInput(
|
||||
session = getDefaultReactiveDomain(),
|
||||
inputId,
|
||||
label = NULL,
|
||||
value = NULL
|
||||
)
|
||||
}
|
||||
\arguments{
|
||||
\item{session}{The \code{session} object passed to function given to
|
||||
|
||||
@@ -140,6 +140,7 @@ Other input elements:
|
||||
\code{\link{selectInput}()},
|
||||
\code{\link{sliderInput}()},
|
||||
\code{\link{submitButton}()},
|
||||
\code{\link{switchInput}()},
|
||||
\code{\link{textAreaInput}()},
|
||||
\code{\link{textInput}()}
|
||||
}
|
||||
|
||||
@@ -1407,50 +1407,6 @@ 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");
|
||||
}
|
||||
@@ -1524,16 +1480,13 @@ function main(): void {
|
||||
|
||||
// If the given tabset has no active tabs, select the first one
|
||||
function ensureTabsetHasVisibleTab($tabset) {
|
||||
const inputBinding = $tabset.data("shiny-input-binding");
|
||||
|
||||
// Use the getValue() method to avoid duplicating the CSS selector
|
||||
// for querying the DOM for the currently active tab
|
||||
if (!inputBinding.getValue($tabset)) {
|
||||
if ($tabset.find("li.active").not(".dropdown").length === 0) {
|
||||
// Note: destTabValue may be null. We still want to proceed
|
||||
// through the below logic and setValue so that the input
|
||||
// value for the tabset gets updated (i.e. input$tabsetId
|
||||
// should be null if there are no tabs).
|
||||
const destTabValue = getFirstTab($tabset);
|
||||
const inputBinding = $tabset.data("shiny-input-binding");
|
||||
const evt = jQuery.Event("shiny:updateinput");
|
||||
|
||||
evt.binding = inputBinding;
|
||||
@@ -4674,7 +4627,7 @@ function main(): void {
|
||||
},
|
||||
getState: function (el) {
|
||||
return {
|
||||
label: $(el).parent().find("span").text(),
|
||||
label: $(el).parent().find("span, label").text(),
|
||||
value: el.checked,
|
||||
};
|
||||
},
|
||||
@@ -4684,7 +4637,7 @@ function main(): void {
|
||||
// checkboxInput()'s label works different from other
|
||||
// input labels...the label container should always exist
|
||||
if (data.hasOwnProperty("label"))
|
||||
$(el).parent().find("span").text(data.label);
|
||||
$(el).parent().find("span, label").text(data.label);
|
||||
|
||||
$(el).trigger("change");
|
||||
},
|
||||
@@ -5969,8 +5922,6 @@ 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()
|
||||
}
|
||||
@@ -5991,12 +5942,9 @@ 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();
|
||||
}
|
||||
);
|
||||
@@ -6007,17 +5955,6 @@ 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");
|
||||
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
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