#' @include utils.R NULL #' Create a Bootstrap page #' #' Create a Shiny UI page that loads the CSS and JavaScript for #' \href{http://getbootstrap.com/3.3.1/}{Bootstrap}, and has no content in the #' page body (other than what you provide). #' #' This function is primarily intended for users who are proficient in #' HTML/CSS, and know how to lay out pages in Bootstrap. Most applications #' should use \code{\link{fluidPage}} along with layout functions like #' \code{\link{fluidRow}} and \code{\link{sidebarLayout}}. #' #' @param ... The contents of the document body. #' @param title The browser window title (defaults to the host URL of the page) #' @param responsive This option is deprecated; it is no longer optional with #' Bootstrap 3. #' @param theme Alternative Bootstrap stylesheet (normally a css file within the #' www directory, e.g. \code{www/bootstrap.css}) #' #' @return A UI defintion that can be passed to the \link{shinyUI} function. #' #' @note The \code{basicPage} function is deprecated, you should use the #' \code{\link{fluidPage}} function instead. #' #' @seealso \code{\link{fluidPage}}, \code{\link{fixedPage}} #' #' @export bootstrapPage <- function(..., title = NULL, responsive = NULL, theme = NULL) { if (!is.null(responsive)) { shinyDeprecated("The 'responsive' argument is no longer used with Bootstrap 3.") } # required head tags for boostrap importBootstrap <- function() { list( htmlDependency("bootstrap", "3.3.1", c( href = "shared/bootstrap", file = system.file("www/shared/bootstrap", package = "shiny") ), script = c( "js/bootstrap.min.js", # These shims are necessary for IE 8 compatibility "shim/html5shiv.min.js", "shim/respond.min.js" ), stylesheet = if (is.null(theme)) "css/bootstrap.min.css", meta = list(viewport = "width=device-width, initial-scale=1") ) ) } attachDependencies( tagList( if (!is.null(title)) tags$head(tags$title(title)), if (!is.null(theme)) { tags$head(tags$link(rel="stylesheet", type="text/css", href = theme)) }, # remainder of tags passed to the function list(...) ), importBootstrap() ) } #' @rdname bootstrapPage #' @export basicPage <- function(...) { bootstrapPage(div(class="container-fluid", list(...))) } #' Create a page with a sidebar #' #' Create a Shiny UI that contains a header with the application title, a #' sidebar for input controls, and a main area for output. #' #' @param headerPanel The \link{headerPanel} with the application title #' @param sidebarPanel The \link{sidebarPanel} containing input controls #' @param mainPanel The \link{mainPanel} containing outputs #' @return A UI defintion that can be passed to the \link{shinyUI} function #' #' @note This function is deprecated. You should use \code{\link{fluidPage}} #' along with \code{\link{sidebarLayout}} to implement a page with a sidebar. #' #' @examples #' # Define UI #' shinyUI(pageWithSidebar( #' #' # Application title #' headerPanel("Hello Shiny!"), #' #' # Sidebar with a slider input #' sidebarPanel( #' sliderInput("obs", #' "Number of observations:", #' min = 0, #' max = 1000, #' value = 500) #' ), #' #' # Show a plot of the generated distribution #' mainPanel( #' plotOutput("distPlot") #' ) #' )) #' #' @export pageWithSidebar <- function(headerPanel, sidebarPanel, mainPanel) { bootstrapPage( # basic application container divs div( class="container-fluid", div(class="row", headerPanel ), div(class="row", sidebarPanel, mainPanel ) ) ) } #' 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 \code{\link{tabPanel}} elements. #' #' @param title The title to display in the navbar #' @param ... \code{\link{tabPanel}} elements to include in the page #' @param id If provided, you can use \code{input$}\emph{\code{id}} in your #' server logic to determine which of the current tabs is active. The value #' will correspond to the \code{value} argument that is passed to #' \code{\link{tabPanel}}. #' @param position Determines whether the navbar should be displayed at the top #' of the page with normal scrolling behavior (\code{"static-top"}), pinned #' at the top (\code{"fixed-top"}), or pinned at the bottom #' (\code{"fixed-bottom"}). Note that using \code{"fixed-top"} or #' \code{"fixed-bottom"} will cause the navbar to overlay your body content, #' unless you add padding, e.g.: #' \code{tags$style(type="text/css", "body {padding-top: 70px;}")} #' @param header Tag of list of tags to display as a common header above all #' tabPanels. #' @param footer Tag or list of tags to display as a common footer below all #' tabPanels #' @param inverse \code{TRUE} to use a dark background and light text for the #' navigation bar #' @param collapsible \code{TRUE} to automatically collapse the navigation #' 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 \code{collapsible} instead. #' @param fluid \code{TRUE} to use a fluid layout. \code{FALSE} to use a fixed #' layout. #' @param responsive This option is deprecated; it is no longer optional with #' Bootstrap 3. #' @param theme Alternative Bootstrap stylesheet (normally a css file within the #' www directory). For example, to use the theme located at #' \code{www/bootstrap.css} you would use \code{theme = "bootstrap.css"}. #' @param windowTitle The title that should be displayed by the browser window. #' Useful if \code{title} is not a string. #' @param icon Optional icon to appear on a \code{navbarMenu} tab. #' #' @return A UI defintion that can be passed to the \link{shinyUI} function. #' #' @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). #' #' @seealso \code{\link{tabPanel}}, \code{\link{tabsetPanel}} #' #' @examples #' shinyUI(navbarPage("App Title", #' tabPanel("Plot"), #' tabPanel("Summary"), #' tabPanel("Table") #' )) #' #' shinyUI(navbarPage("App Title", #' tabPanel("Plot"), #' navbarMenu("More", #' tabPanel("Summary"), #' tabPanel("Table") #' ) #' )) #' @export navbarPage <- function(title, ..., id = NULL, position = c("static-top", "fixed-top", "fixed-bottom"), header = NULL, footer = NULL, inverse = FALSE, collapsible = FALSE, collapsable, fluid = TRUE, responsive = NULL, theme = NULL, windowTitle = title) { if (!missing(collapsable)) { shinyDeprecated("`collapsable` is deprecated; use `collapsible` instead.") collapsible <- collapsable } # alias title so we can avoid conflicts w/ title in withTags pageTitle <- title # navbar class based on options navbarClass <- "navbar navbar-default" position <- match.arg(position) if (!is.null(position)) navbarClass <- paste(navbarClass, " navbar-", position, sep = "") if (inverse) navbarClass <- paste(navbarClass, "navbar-inverse") # build the tabset tabs <- list(...) tabset <- buildTabset(tabs, "nav navbar-nav", NULL, id) # built the container div dynamically to support optional collapsibility if (collapsible) { navId <- paste("navbar-collapse-", p_randomInt(1000, 10000), sep="") containerDiv <- div(class="container", div(class="navbar-header", tags$button(type="button", class="navbar-toggle collapsed", `data-toggle`="collapse", `data-target`=paste0("#", navId), span(class="sr-only", "Toggle navigation"), span(class="icon-bar"), span(class="icon-bar"), span(class="icon-bar") ), span(class="navbar-brand", pageTitle) ), div(class="navbar-collapse collapse", id=navId, tabset$navList) ) } else { containerDiv <- div(class="container", div(class="navbar-header", span(class="navbar-brand", pageTitle) ), tabset$navList ) } # create a default header if necessary if (length(header) == 0) header <- HTML(" ") # function to return plain or fluid class name className <- function(name) { if (fluid) paste(name, "-fluid", sep="") else name } # build the main tab content div contentDiv <- div(class=className("container")) if (!is.null(header)) contentDiv <- tagAppendChild(contentDiv, div(class="row", header)) contentDiv <- tagAppendChild(contentDiv, tabset$content) if (!is.null(footer)) contentDiv <- tagAppendChild(contentDiv, div(class="row", footer)) # build the page bootstrapPage( title = windowTitle, responsive = responsive, theme = theme, tags$nav(class=navbarClass, role="navigation", containerDiv), contentDiv ) } #' @rdname navbarPage #' @export navbarMenu <- function(title, ..., icon = NULL) { structure(list(title = title, tabs = list(...), iconClass = iconClass(icon)), class = "shiny.navbarmenu") } #' Create a header panel #' #' Create a header panel containing an application title. #' #' @param title An application title to display #' @param windowTitle The title that should be displayed by the browser window. #' Useful if \code{title} is not a string. #' @return A headerPanel that can be passed to \link{pageWithSidebar} #' #' @examples #' headerPanel("Hello Shiny!") #' @export headerPanel <- function(title, windowTitle=title) { tagList( tags$head(tags$title(windowTitle)), div(class="col-sm-12", h1(title) ) ) } #' Create a well panel #' #' Creates a panel with a slightly inset border and grey background. Equivalent #' to Bootstrap's \code{well} CSS class. #' #' @param ... UI elements to include inside the panel. #' @return The newly created panel. #' #' @export wellPanel <- function(...) { div(class="well", ...) } #' Create a sidebar panel #' #' Create a sidebar panel containing input controls that can in turn be passed #' to \code{\link{sidebarLayout}}. #' #' @param ... UI elements to include on the sidebar #' @param width The width of the sidebar. For fluid layouts this is out of 12 #' total units; for fixed layouts it is out of whatever the width of the #' sidebar's parent column is. #' @return A sidebar that can be passed to \code{\link{sidebarLayout}} #' #' @examples #' # Sidebar with controls to select a dataset and specify #' # the number of observations to view #' sidebarPanel( #' selectInput("dataset", "Choose a dataset:", #' choices = c("rock", "pressure", "cars")), #' #' numericInput("obs", "Observations:", 10) #' ) #' @export sidebarPanel <- function(..., width = 4) { div(class=paste0("col-sm-", width), tags$form(class="well", ... ) ) } #' Create a main panel #' #' Create a main panel containing output elements that can in turn be passed to #' \code{\link{sidebarLayout}}. #' #' @param ... Output elements to include in the main panel #' @param width The width of the main panel. For fluid layouts this is out of 12 #' total units; for fixed layouts it is out of whatever the width of the main #' panel's parent column is. #' @return A main panel that can be passed to \code{\link{sidebarLayout}}. #' #' @examples #' # Show the caption and plot of the requested variable against mpg #' mainPanel( #' h3(textOutput("caption")), #' plotOutput("mpgPlot") #' ) #' @export mainPanel <- function(..., width = 8) { div(class=paste0("col-sm-", width), ... ) } #' Conditional Panel #' #' Creates a panel that is visible or not, depending on the value of a #' JavaScript expression. The JS expression is evaluated once at startup and #' whenever Shiny detects a relevant change in input/output. #' #' In the JS expression, you can refer to \code{input} and \code{output} #' JavaScript objects that contain the current values of input and output. For #' example, if you have an input with an id of \code{foo}, then you can use #' \code{input.foo} to read its value. (Be sure not to modify the input/output #' objects, as this may cause unpredictable behavior.) #' #' @param condition A JavaScript expression that will be evaluated repeatedly to #' determine whether the panel should be displayed. #' @param ... Elements to include in the panel. #' #' @note You are not recommended to use special JavaScript characters such as a #' period \code{.} in the input id's, but if you do use them anyway, for #' example, \code{inputId = "foo.bar"}, you will have to use #' \code{input["foo.bar"]} instead of \code{input.foo.bar} to read the input #' value. #' @examples #' sidebarPanel( #' selectInput( #' "plotType", "Plot Type", #' c(Scatter = "scatter", #' Histogram = "hist")), #' #' # Only show this panel if the plot type is a histogram #' conditionalPanel( #' condition = "input.plotType == 'hist'", #' selectInput( #' "breaks", "Breaks", #' c("Sturges", #' "Scott", #' "Freedman-Diaconis", #' "[Custom]" = "custom")), #' #' # Only show this panel if Custom is selected #' conditionalPanel( #' condition = "input.breaks == 'custom'", #' sliderInput("breakCount", "Break Count", min=1, max=1000, value=10) #' ) #' ) #' ) #' #' @export conditionalPanel <- function(condition, ...) { div('data-display-if'=condition, ...) } #' Create a text input control #' #' Create an input control for entry of unstructured text values #' #' @param inputId Input variable to assign the control's value to #' @param label Display label for the control #' @param value Initial value #' @return A text input control that can be added to a UI definition. #' #' @family input elements #' @seealso \code{\link{updateTextInput}} #' #' @examples #' textInput("caption", "Caption:", "Data Summary") #' @export textInput <- function(inputId, label, value = "") { div(class = "form-group shiny-input-container", label %AND% tags$label(label, `for` = inputId), tags$input(id = inputId, type="text", class="form-control", value=value) ) } #' Create a password input control #' #' Create an password control for entry of passwords. #' #' @inheritParams textInput #' @return A text input control that can be added to a UI definition. #' #' @family input elements #' @seealso \code{\link{updateTextInput}} #' #' @examples #' passwordInput("password", "Password:") #' @export passwordInput <- function(inputId, label, value = "") { div(class = "form-group shiny-input-container", label %AND% tags$label(label, `for` = inputId), tags$input(id = inputId, type="password", class="form-control", value=value) ) } #' Create a numeric input control #' #' Create an input control for entry of numeric values #' #' @param inputId Input variable to assign the control's value to #' @param label Display label for the control #' @param value Initial value #' @param min Minimum allowed value #' @param max Maximum allowed value #' @param step Interval to use when stepping between min and max #' @return A numeric input control that can be added to a UI definition. #' #' @family input elements #' @seealso \code{\link{updateNumericInput}} #' #' @examples #' numericInput("obs", "Observations:", 10, #' min = 1, max = 100) #' @export numericInput <- function(inputId, label, value, min = NA, max = NA, step = NA) { # build input tag inputTag <- tags$input(id = inputId, type = "number", class="form-control", value = formatNoSci(value)) if (!is.na(min)) inputTag$attribs$min = min if (!is.na(max)) inputTag$attribs$max = max if (!is.na(step)) inputTag$attribs$step = step div(class = "form-group shiny-input-container", label %AND% tags$label(label, `for` = inputId), inputTag ) } #' File Upload Control #' #' Create a file upload control that can be used to upload one or more files. #' #' Whenever a file upload completes, the corresponding input variable is set #' to a dataframe. This dataframe contains one row for each selected file, and #' the following columns: #' \describe{ #' \item{\code{name}}{The filename provided by the web browser. This is #' \strong{not} the path to read to get at the actual data that was uploaded #' (see #' \code{datapath} column).} #' \item{\code{size}}{The size of the uploaded data, in #' bytes.} #' \item{\code{type}}{The MIME type reported by the browser (for example, #' \code{text/plain}), or empty string if the browser didn't know.} #' \item{\code{datapath}}{The path to a temp file that contains the data that was #' uploaded. This file may be deleted if the user performs another upload #' operation.} #' } #' #' @family input elements #' #' @param inputId Input variable to assign the control's value to. #' @param label Display label for the control. #' @param multiple Whether the user should be allowed to select and upload #' multiple files at once. \bold{Does not work on older browsers, including #' Internet Explorer 9 and earlier.} #' @param accept A character vector of MIME types; gives the browser a hint of #' what kind of files the server is expecting. #' #' @export fileInput <- function(inputId, label, multiple = FALSE, accept = NULL) { inputTag <- tags$input(id = inputId, name = inputId, type = "file") if (multiple) inputTag$attribs$multiple <- "multiple" if (length(accept) > 0) inputTag$attribs$accept <- paste(accept, collapse=',') div(class = "form-group shiny-input-container", label %AND% tags$label(label), inputTag, tags$div( id=paste(inputId, "_progress", sep=""), class="progress progress-striped active shiny-file-input-progress", tags$div(class="progress-bar") ) ) } #' Checkbox Input Control #' #' Create a checkbox that can be used to specify logical values. #' #' @param inputId Input variable to assign the control's value to. #' @param label Display label for the control. #' @param value Initial value (\code{TRUE} or \code{FALSE}). #' @return A checkbox control that can be added to a UI definition. #' #' @family input elements #' @seealso \code{\link{checkboxGroupInput}}, \code{\link{updateCheckboxInput}} #' #' @examples #' checkboxInput("outliers", "Show outliers", FALSE) #' @export checkboxInput <- function(inputId, label, value = FALSE) { inputTag <- tags$input(id = inputId, type="checkbox") if (!is.null(value) && value) inputTag$attribs$checked <- "checked" div(class = "form-group shiny-input-container", div(class = "checkbox", tags$label(inputTag, tags$span(label)) ) ) } #' Checkbox Group Input Control #' #' Create a group of checkboxes that can be used to toggle multiple choices #' independently. The server will receive the input as a character vector of the #' selected values. #' #' @param inputId Input variable to assign the control's value to. #' @param label Display label for the control, or \code{NULL}. #' @param choices List of values to show checkboxes for. If elements of the list #' are named then that name rather than the value is displayed to the user. #' @param selected The values that should be initially selected, if any. #' @param inline If \code{TRUE}, render the choices inline (i.e. horizontally) #' @return A list of HTML elements that can be added to a UI definition. #' #' @family input elements #' @seealso \code{\link{checkboxInput}}, \code{\link{updateCheckboxGroupInput}} #' #' @examples #' checkboxGroupInput("variable", "Variable:", #' c("Cylinders" = "cyl", #' "Transmission" = "am", #' "Gears" = "gear")) #' #' @export checkboxGroupInput <- function(inputId, label, choices, selected = NULL, inline = FALSE) { # resolve names choices <- choicesWithNames(choices) if (!is.null(selected)) selected <- validateSelected(selected, choices, inputId) options <- generateOptions(inputId, choices, selected, inline) divClass <- "form-group shiny-input-checkboxgroup shiny-input-container" if (inline) divClass <- paste(divClass, "shiny-input-container-inline") # return label and select tag tags$div(id = inputId, class = divClass, controlLabel(inputId, label), options) } # Before shiny 0.9, `selected` refers to names/labels of `choices`; now it # refers to values. Below is a function for backward compatibility. validateSelected <- function(selected, choices, inputId) { # drop names, otherwise toJSON() keeps them too selected <- unname(selected) # if you are using optgroups, you're using shiny > 0.10.0, and you should # already know that `selected` must be a value instead of a label if (needOptgroup(choices)) return(selected) if (is.list(choices)) choices <- unlist(choices) nms <- names(choices) # labels and values are identical, no need to validate if (identical(nms, unname(choices))) return(selected) # when selected labels instead of values i <- (selected %in% nms) & !(selected %in% choices) if (any(i)) { warnFun <- if (all(i)) { # replace names with values selected <- unname(choices[selected]) warning } else stop # stop when it is ambiguous (some labels == values) warnFun("'selected' must be the values instead of names of 'choices' ", "for the input '", inputId, "'") } selected } # generate options for radio buttons and checkbox groups (type = 'checkbox' or # 'radio') generateOptions <- function(inputId, choices, selected, inline, type = 'checkbox') { # create tags for each of the options ids <- paste0(inputId, seq_along(choices)) # generate a list of options <- mapply( ids, choices, names(choices), FUN = function(id, value, name) { inputTag <- tags$input( type = type, name = inputId, id = id, value = value ) if (value %in% selected) inputTag$attribs$checked <- "checked" # If inline, there's no wrapper div, and the label needs a class like # checkbox-inline. if (inline) { tags$label(class = paste0(type, "-inline"), inputTag, tags$span(name)) } else { tags$div(class = type, tags$label(inputTag, tags$span(name)) ) } }, SIMPLIFY = FALSE, USE.NAMES = FALSE ) div(class = "shiny-options-group", options) } #' Create a help text element #' #' Create help text which can be added to an input form to provide additional #' explanation or context. #' #' @param ... One or more help text strings (or other inline HTML elements) #' @return A help text element that can be added to a UI definition. #' #' @examples #' helpText("Note: while the data view will show only", #' "the specified number of observations, the", #' "summary will be based on the full dataset.") #' @export helpText <- function(...) { span(class="help-block", ...) } controlLabel <- function(controlName, label) { label %AND% tags$label(class = "control-label", `for` = controlName, label) } # Takes a vector or list, and adds names (same as the value) to any entries # without names. choicesWithNames <- function(choices) { # Take a vector or list, and convert to list. Also, if any children are # vectors with length > 1, convert those to list. If the list is unnamed, # convert it to a named list with blank names. listify <- function(obj) { # If a list/vector is unnamed, give it blank names makeNamed <- function(x) { if (is.null(names(x))) names(x) <- character(length(x)) x } res <- lapply(obj, function(val) { if (is.list(val)) listify(val) else if (length(val) == 1 && is.null(names(val))) val else makeNamed(as.list(val)) }) makeNamed(res) } choices <- listify(choices) if (length(choices) == 0) return(choices) # Recurse into any subgroups choices <- mapply(choices, names(choices), FUN = function(choice, name) { if (!is.list(choice)) return(choice) if (name == "") stop('All sub-lists in "choices" must be named.') choicesWithNames(choice) }, SIMPLIFY = FALSE) # default missing names to choice values missing <- names(choices) == "" names(choices)[missing] <- as.character(choices)[missing] choices } #' Create a select list input control #' #' Create a select list that can be used to choose a single or #' multiple items from a list of values. #' #' By default, \code{selectInput()} and \code{selectizeInput()} use the #' JavaScript library \pkg{selectize.js} (\url{https://github.com/brianreavis/selectize.js}) #' to instead of the basic select input element. To use the standard HTML select #' input element, use \code{selectInput()} with \code{selectize=FALSE}. #' #' @param inputId Input variable to assign the control's value to #' @param label Display label for the control, or \code{NULL} #' @param choices List of values to select from. If elements of the list are #' named then that name rather than the value is displayed to the user. #' @param selected The initially selected value (or multiple values if #' \code{multiple = TRUE}). If not specified then defaults to the first value #' for single-select lists and no values for multiple select lists. #' @param multiple Is selection of multiple items allowed? #' @param selectize Whether to use \pkg{selectize.js} or not. #' @return A select list control that can be added to a UI definition. #' #' @family input elements #' @seealso \code{\link{updateSelectInput}} #' #' @examples #' selectInput("variable", "Variable:", #' c("Cylinders" = "cyl", #' "Transmission" = "am", #' "Gears" = "gear")) #' @export selectInput <- function(inputId, label, choices, selected = NULL, multiple = FALSE, selectize = TRUE, width = NULL) { # resolve names choices <- choicesWithNames(choices) # default value if it's not specified if (is.null(selected)) { if (!multiple) selected <- firstChoice(choices) } else selected <- validateSelected(selected, choices, inputId) # create select tag and add options selectTag <- tags$select(id = inputId, selectOptions(choices, selected)) if (multiple) selectTag$attribs$multiple <- "multiple" # return label and select tag res <- div( class = "form-group shiny-input-container", style = if (!is.null(width)) paste0("width: ", validateCssUnit(width)), controlLabel(inputId, label), div(selectTag) ) if (!selectize) return(res) selectizeIt(inputId, res, NULL, nonempty = !multiple && !("" %in% choices)) } firstChoice <- function(choices) { if (length(choices) == 0L) return() choice <- choices[[1]] if (is.list(choice)) firstChoice(choice) else choice } # Create tags for each of the options; use if necessary. # This returns a HTML string instead of tags, because of the 'selected' # attribute. selectOptions <- function(choices, selected = NULL) { html <- mapply(choices, names(choices), FUN = function(choice, label) { if (is.list(choice)) { # If sub-list, create an optgroup and recurse into the sublist sprintf( '\n%s\n', htmlEscape(label), selectOptions(choice, selected) ) } else { # If single item, just return option string sprintf( '', htmlEscape(choice), if (choice %in% selected) ' selected' else '', htmlEscape(label) ) } }) HTML(paste(html, collapse = '\n')) } # need when choices contains sub-lists needOptgroup <- function(choices) { any(vapply(choices, is.list, logical(1))) } #' @rdname selectInput #' @param ... Arguments passed to \code{selectInput()}. #' @param options A list of options. See the documentation of \pkg{selectize.js} #' for possible options (character option values inside \code{\link{I}()} will #' be treated as literal JavaScript code; see \code{\link{renderDataTable}()} #' for details). #' @param width The width of the input, e.g. \code{'400px'}, or \code{'100\%'}; #' see \code{\link{validateCssUnit}}. #' @note The selectize input created from \code{selectizeInput()} allows #' deletion of the selected option even in a single select input, which will #' return an empty string as its value. This is the default behavior of #' \pkg{selectize.js}. However, the selectize input created from #' \code{selectInput(..., selectize = TRUE)} will ignore the empty string #' value when it is a single choice input and the empty string is not in the #' \code{choices} argument. This is to keep compatibility with #' \code{selectInput(..., selectize = FALSE)}. #' @export selectizeInput <- function(inputId, ..., options = NULL, width = NULL) { selectizeIt( inputId, selectInput(inputId, ..., selectize = FALSE, width = width), options ) } # given a select input and its id, selectize it selectizeIt <- function(inputId, select, options, nonempty = FALSE) { res <- checkAsIs(options) selectizeDep <- htmlDependency( "selectize", "0.11.2", c(href = "shared/selectize"), stylesheet = "css/selectize.bootstrap3.css", head = format(tagList( HTML(''), tags$script(src = 'shared/selectize/js/selectize.min.js') )) ) # Insert script on same level as