Skip to content

Commit 8dd6dab

Browse files
committed
add selectize=TRUE to selectInput(), but disable deletion for single input
hopefully this is a good compromise for rstudio#404
1 parent c090c6a commit 8dd6dab

File tree

3 files changed

+43
-11
lines changed

3 files changed

+43
-11
lines changed

NEWS

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ shiny 0.8.0.99
1313
selectize.js (https://github.com/brianreavis/selectize.js), which extends
1414
the basic select input in many aspects.
1515

16+
* The `selectInput()` function also gained a new argument `selectize = TRUE`
17+
to makes use of selectize.js by default. If you want to revert back to the
18+
original select input, you have to call selectInput(..., selectize = FALSE).
19+
1620
* Upgraded to Bootstrap 2.3.2 and jQuery 1.11.0.
1721

1822
* Make `tags$head()` and `singleton()` behave correctly when used with

R/bootstrap.R

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -686,6 +686,7 @@ choicesWithNames <- function(choices) {
686686
#' \code{multiple = TRUE}). If not specified then defaults to the first value
687687
#' for single-select lists and no values for multiple select lists.
688688
#' @param multiple Is selection of multiple items allowed?
689+
#' @param selectize Whether to use \pkg{selectize.js} or not.
689690
#' @return A select list control that can be added to a UI definition.
690691
#'
691692
#' @family input elements
@@ -697,11 +698,8 @@ choicesWithNames <- function(choices) {
697698
#' "Transmission" = "am",
698699
#' "Gears" = "gear"))
699700
#' @export
700-
selectInput <- function(inputId,
701-
label,
702-
choices,
703-
selected = NULL,
704-
multiple = FALSE) {
701+
selectInput <- function(inputId, label, choices, selected = NULL,
702+
multiple = FALSE, selectize = TRUE) {
705703
# resolve names
706704
choices <- choicesWithNames(choices)
707705

@@ -731,21 +729,36 @@ selectInput <- function(inputId,
731729
selectTag <- tagSetChildren(selectTag, list = optionTags)
732730

733731
# return label and select tag
734-
tagList(controlLabel(inputId, label), selectTag)
732+
res <- tagList(controlLabel(inputId, label), selectTag)
733+
if (!selectize) return(res)
734+
selectizeIt(inputId, res, NULL, nonempty = !multiple && !("" %in% choices))
735735
}
736736

737737
#' @rdname selectInput
738-
#' @param ... arguments passed to \code{selectInput()}
739-
#' @param options a list of options; see the documentation of \pkg{selectize.js}
738+
#' @param ... Arguments passed to \code{selectInput()}.
739+
#' @param options A list of options. See the documentation of \pkg{selectize.js}
740740
#' for possible options (character option values inside \code{\link{I}()} will
741741
#' be treated as literal JavaScript code; see \code{\link{renderDataTable}()}
742-
#' for details)
742+
#' for details).
743+
#' @note The selectize input created from \code{selectizeInput()} allows
744+
#' deletion of the selected option even in a single select input, which will
745+
#' return an empty string as its value. This is the default behavior of
746+
#' \pkg{selectize.js}. However, the selectize input created from
747+
#' \code{selectInput(..., selectize = TRUE)} will ignore the empty string
748+
#' value when it is a single choice input and the empty string is not in the
749+
#' \code{choices} argument. This is to keep compatibility with
750+
#' \code{selectInput(..., selectize = FALSE)}.
743751
#' @export
744752
selectizeInput <- function(inputId, ..., options = NULL) {
753+
selectizeIt(inputId, selectInput(inputId, ..., selectize = FALSE), options)
754+
}
755+
756+
# given a select input and its id, selectize it
757+
selectizeIt <- function(inputId, select, options, nonempty = FALSE) {
745758
res <- checkAsIs(options)
746759

747760
tagList(
748-
selectInput(inputId, ...),
761+
select,
749762
singleton(tags$head(
750763
tags$link(rel = 'stylesheet', type = 'text/css',
751764
href = 'shared/selectize/css/selectize.bootstrap2.css'),
@@ -755,7 +768,8 @@ selectizeInput <- function(inputId, ..., options = NULL) {
755768
tags$script(src = 'shared/selectize/js/selectize.min.js')
756769
)),
757770
tags$script(
758-
type = 'application/json', `data-for` = inputId,
771+
type = 'application/json',
772+
`data-for` = inputId, `data-nonempty` = if (nonempty) '',
759773
`data-eval` = if (length(res$eval)) HTML(toJSON(res$eval)),
760774
if (length(res$options)) HTML(toJSON(res$options)) else '{}'
761775
)

inst/www/shared/shiny.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2041,6 +2041,20 @@
20412041
valueField: 'value',
20422042
searchField: ['label']
20432043
}, JSON.parse(config.html()));
2044+
if (config.data('nonempty') !== undefined) {
2045+
options = $.extend(options, {
2046+
onItemRemove: function(value) {
2047+
if (this.getValue() === "")
2048+
$("select[id=" + el.id + "]").empty().append($("<option/>", {
2049+
"value": value, "selected": true
2050+
})).trigger("change");
2051+
},
2052+
onDropdownClose: function($dropdown) {
2053+
if (this.getValue() === "")
2054+
this.setValue($("select[id=" + el.id + "]").val());
2055+
}
2056+
});
2057+
}
20442058
// options that should be eval()ed
20452059
if (config.data('eval') instanceof Array)
20462060
$.each(config.data('eval'), function(i, x) {

0 commit comments

Comments
 (0)