Compare commits

...

4 Commits

Author SHA1 Message Date
Carson
0be84710fd Add BS5 support as well 2021-03-23 17:54:14 -05:00
Carson
967078ebbb Quick implementation of switchInput() 2021-03-23 17:29:32 -05:00
Hadley Wickham
e29d92c5ff Allow trailing commas in more places (#3328)
* Allow trailing commas in more places

I grepped for list(...) and replaced with rlang::list2(...). This also enables !!! which is generally not important for Shiny because it automatically splices lists/tagLists, but I doubt it will affect any existing code.

* update news; no need to rlang::

* missed one

* Update NEWS.md

Co-authored-by: Hadley Wickham <h.wickham@gmail.com>

Co-authored-by: Carson Sievert <cpsievert1@gmail.com>
2021-03-23 14:24:21 -05:00
Carson
0a331e3366 update news 2021-03-22 16:00:39 -05:00
32 changed files with 191 additions and 19 deletions

View File

@@ -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'

View File

@@ -280,6 +280,7 @@ export(stopApp)
export(strong)
export(submitButton)
export(suppressDependencies)
export(switchInput)
export(tabPanel)
export(tabPanelBody)
export(tableOutput)
@@ -314,6 +315,7 @@ export(updateRadioButtons)
export(updateSelectInput)
export(updateSelectizeInput)
export(updateSliderInput)
export(updateSwitchInput)
export(updateTabsetPanel)
export(updateTextAreaInput)
export(updateTextInput)

13
NEWS.md
View File

@@ -7,7 +7,18 @@ shiny 1.6.0.9000
* The `format` and `locale` arguments to `sliderInput()` have been removed. They have been deprecated since 0.10.2.2 (released on 2014-12-08).
### Minor new features and improvements
### New features and improvements
* All uses of `list(...)` have been replaced with `rlang::list2(...)`. This means that you can use trailing `,` without error and use rlang's `!!!` operator to "splice" a list of argument values into `...`. We think this'll be particularly useful for passing a list of `tabPanel()` to their consumers (i.e., `tabsetPanel()`, `navbarPage()`, etc). For example, `tabs <- list(tabPanel("A", "a"), tabPanel("B", "b")); navbarPage(!!!tabs)`. (#3315 and #3328)
* Numerous improvements tabset panels (i.e., `tabPanel()`, `navbarMenu()`, `tabsetPanel()`, `navbarPage()`, etc) (#3315):
* Closed #3322: `tabsetPanel()` and `navlistPanel()` gain `header`/`footer` arguments (inspired by `navbarPage()`'s already existing `header`/`footer`), making it easier to include content that should appear on every tab.
* Closed #3313 and #1823: More informative error when non-`tabPanel()`/`shiny.tag` objects are supplied to `...`.
* Closed #3321: New informative warning when `shiny.tag` object(s) are supplied to `...`. In this case we will continue to create an "empty" nav item and include the content on every tab, but the warning will mention the (new) `header`/`footer` args, which is likely what the user wants.
* Closed #3320: The HTML markup that `tabPanel()` et. al generate (for Bootstrap nav) is now Bootstrap 4+ compliant when used with `theme = bslib::bs_theme()`.
* Closed #1928: `NULL` values are now dropped instead of producing an empty nav item.
### Other improvements
* Shiny's core JavaScript code was converted to TypeScript. For the latest development information, please see the [README.md in `./srcts`](https://github.com/rstudio/shiny/tree/master/srcts). (#3296)

View File

@@ -396,7 +396,7 @@ mainPanel <- function(..., width = 8) {
#' }
#' @export
verticalLayout <- function(..., fluid = TRUE) {
lapply(list(...), function(row) {
lapply(list2(...), function(row) {
col <- column(12, row)
if (fluid)
fluidRow(col)
@@ -433,7 +433,7 @@ verticalLayout <- function(..., fluid = TRUE) {
#' @export
flowLayout <- function(..., cellArgs = list()) {
children <- list(...)
children <- list2(...)
childIdx <- !nzchar(names(children) %||% character(length(children)))
attribs <- children[!childIdx]
children <- children[childIdx]
@@ -516,7 +516,7 @@ inputPanel <- function(...) {
#' @export
splitLayout <- function(..., cellWidths = NULL, cellArgs = list()) {
children <- list(...)
children <- list2(...)
childIdx <- !nzchar(names(children) %||% character(length(children)))
attribs <- children[!childIdx]
children <- children[childIdx]
@@ -614,7 +614,7 @@ fillCol <- function(..., flex = 1, width = "100%", height = "100%") {
}
flexfill <- function(..., direction, flex, width = width, height = height) {
children <- list(...)
children <- list2(...)
attrs <- list()
if (!is.null(names(children))) {

View File

@@ -49,7 +49,7 @@ bootstrapPage <- function(..., title = NULL, responsive = deprecated(), theme =
tags$head(tags$link(rel="stylesheet", type="text/css", href=theme))
},
# remainder of tags passed to the function
list(...)
list2(...)
)
# If theme is a bslib::bs_theme() object, bootstrapLib() needs to come first

65
R/input-switch.R Normal file
View 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"
)
)
)
})
}

View File

@@ -568,7 +568,7 @@ ReactiveValues <- R6Class(
#' @seealso [isolate()] and [is.reactivevalues()].
#' @export
reactiveValues <- function(...) {
args <- list(...)
args <- list2(...)
if ((length(args) > 0) && (is.null(names(args)) || any(names(args) == "")))
rlang::abort("All arguments passed to reactiveValues() must be named.")
@@ -1915,7 +1915,7 @@ reactivePoll <- function(intervalMillis, session, checkFunc, valueFunc) {
#' @export
reactiveFileReader <- function(intervalMillis, session, filePath, readFunc, ...) {
filePath <- coerceToFunc(filePath)
extraArgs <- list(...)
extraArgs <- list2(...)
reactivePoll(
intervalMillis, session,

View File

@@ -178,7 +178,7 @@ getShinyOption <- function(name, default = NULL) {
#' @aliases shiny-options
#' @export
shinyOptions <- function(...) {
newOpts <- list(...)
newOpts <- list2(...)
if (length(newOpts) > 0) {
# If we're within a session, modify at the session level.

View File

@@ -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
#'

View File

@@ -1176,7 +1176,7 @@ reactiveStop <- function(message = "", class = NULL) {
#'
#' }
validate <- function(..., errorClass = character(0)) {
results <- sapply(list(...), function(x) {
results <- sapply(list2(...), function(x) {
# Detect NULL or NA
if (is.null(x))
return(NA_character_)

View File

@@ -5163,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
};
},
@@ -5171,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");
}
});

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -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}()}

View File

@@ -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}()}

View File

@@ -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}()}

View File

@@ -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}()}

View File

@@ -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}()}

View File

@@ -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}()}

View File

@@ -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}()}

View File

@@ -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}()}

View File

@@ -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}()}

View File

@@ -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}()}

View File

@@ -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}()}

View File

@@ -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
View 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}

View File

@@ -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}()}
}

View File

@@ -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}()}
}

View File

@@ -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

View File

@@ -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}()}
}

View File

@@ -4627,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,
};
},
@@ -4637,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");
},