mirror of
https://github.com/rstudio/shiny.git
synced 2026-04-07 03:00:20 -04:00
Merge branch 'master' into selectize-remote
This commit is contained in:
4
NEWS.md
4
NEWS.md
@@ -11,6 +11,10 @@ shiny 1.1.0.9000
|
||||
|
||||
* Resolved [#1933](https://github.com/rstudio/shiny/issues/1933): extended server-side selectize to lists and optgroups. ([#2102](https://github.com/rstudio/shiny/pull/2102))
|
||||
|
||||
* Resolved [#1935](https://github.com/rstudio/shiny/issues/1935): correctly returns plot coordinates when using outer margins. ([#2108](https://github.com/rstudio/shiny/pull/2108))
|
||||
|
||||
* Resolved [#2019](https://github.com/rstudio/shiny/issues/2019): `updateInputSlider` now changes the slider formatting if the input type changes.[#2099](https://github.com/rstudio/shiny/pull/2099)
|
||||
|
||||
### Documentation Updates
|
||||
|
||||
* Addressed [#1864](https://github.com/rstudio/shiny/issues/1864) by changing `optgroup` documentation to use `list` instead of `c`. [#2084](https://github.com/rstudio/shiny/pull/2084)
|
||||
|
||||
@@ -86,24 +86,10 @@ sliderInput <- function(inputId, label, min, max, value, step = NULL,
|
||||
version = "0.10.2.2")
|
||||
}
|
||||
|
||||
if (inherits(min, "Date")) {
|
||||
if (!inherits(max, "Date") || !inherits(value, "Date"))
|
||||
stop("`min`, `max`, and `value must all be Date or non-Date objects")
|
||||
dataType <- "date"
|
||||
dataType <- getSliderType(min, max, value)
|
||||
|
||||
if (is.null(timeFormat))
|
||||
timeFormat <- "%F"
|
||||
|
||||
} else if (inherits(min, "POSIXt")) {
|
||||
if (!inherits(max, "POSIXt") || !inherits(value, "POSIXt"))
|
||||
stop("`min`, `max`, and `value must all be POSIXt or non-POSIXt objects")
|
||||
dataType <- "datetime"
|
||||
|
||||
if (is.null(timeFormat))
|
||||
timeFormat <- "%F %T"
|
||||
|
||||
} else {
|
||||
dataType <- "number"
|
||||
if (is.null(timeFormat)) {
|
||||
timeFormat <- switch(dataType, date = "%F", datetime = "%F %T", number = NULL)
|
||||
}
|
||||
|
||||
# Restore bookmarked values here, after doing the type checking, because the
|
||||
@@ -250,7 +236,7 @@ findStepSize <- function(min, max, step) {
|
||||
# values to calculate the step size.
|
||||
pretty_steps <- pretty(c(min, max), n = 100)
|
||||
n_steps <- length(pretty_steps) - 1
|
||||
|
||||
|
||||
# Fix for #2061: Windows has low-significance digits (like 17 digits out)
|
||||
# even at the boundaries of pretty()'s output. Use signif(digits = 10),
|
||||
# which should be way way less significant than any data we'd want to keep.
|
||||
|
||||
@@ -425,10 +425,10 @@ getPrevPlotCoordmap <- function(width, height) {
|
||||
),
|
||||
# The bounds of the plot area, in DOM pixels
|
||||
range = list(
|
||||
left = graphics::grconvertX(usrBounds[1], 'user', 'nfc') * width,
|
||||
right = graphics::grconvertX(usrBounds[2], 'user', 'nfc') * width,
|
||||
bottom = (1-graphics::grconvertY(usrBounds[3], 'user', 'nfc')) * height - 1,
|
||||
top = (1-graphics::grconvertY(usrBounds[4], 'user', 'nfc')) * height - 1
|
||||
left = graphics::grconvertX(usrBounds[1], 'user', 'ndc') * width,
|
||||
right = graphics::grconvertX(usrBounds[2], 'user', 'ndc') * width,
|
||||
bottom = (1-graphics::grconvertY(usrBounds[3], 'user', 'ndc')) * height - 1,
|
||||
top = (1-graphics::grconvertY(usrBounds[4], 'user', 'ndc')) * height - 1
|
||||
),
|
||||
log = list(
|
||||
x = if (graphics::par('xlog')) 10 else NULL,
|
||||
|
||||
@@ -383,13 +383,17 @@ updateNumericInput <- function(session, inputId, label = NULL, value = NULL,
|
||||
session$sendInputMessage(inputId, message)
|
||||
}
|
||||
|
||||
#' Change the value of a slider input on the client
|
||||
#' Update Slider Input Widget
|
||||
#'
|
||||
#' Change the value of a slider input on the client.
|
||||
#'
|
||||
#' @template update-input
|
||||
#' @param value The value to set for the input object.
|
||||
#' @param min Minimum value.
|
||||
#' @param max Maximum value.
|
||||
#' @param step Step size.
|
||||
#' @param timeFormat Date and POSIXt formatting.
|
||||
#' @param timezone The timezone offset for POSIXt objects.
|
||||
#'
|
||||
#' @seealso \code{\link{sliderInput}}
|
||||
#'
|
||||
@@ -422,22 +426,15 @@ updateNumericInput <- function(session, inputId, label = NULL, value = NULL,
|
||||
#' }
|
||||
#' @export
|
||||
updateSliderInput <- function(session, inputId, label = NULL, value = NULL,
|
||||
min = NULL, max = NULL, step = NULL)
|
||||
min = NULL, max = NULL, step = NULL, timeFormat = NULL, timezone = NULL)
|
||||
{
|
||||
# Make sure that value, min, max all have the same type, because we need
|
||||
# special handling for dates and datetimes.
|
||||
vals <- dropNulls(list(value, min, max))
|
||||
dataType <- getSliderType(min, max, value)
|
||||
|
||||
type <- unique(lapply(vals, function(x) {
|
||||
if (inherits(x, "Date")) "date"
|
||||
else if (inherits(x, "POSIXt")) "datetime"
|
||||
else "number"
|
||||
}))
|
||||
if (length(type) > 1) {
|
||||
stop("Type mismatch for value, min, and max")
|
||||
if (is.null(timeFormat)) {
|
||||
timeFormat <- switch(dataType, date = "%F", datetime = "%F %T", number = NULL)
|
||||
}
|
||||
|
||||
if ((length(type) == 1) && (type == "date" || type == "datetime")) {
|
||||
if (dataType == "date" || dataType == "datetime") {
|
||||
to_ms <- function(x) 1000 * as.numeric(as.POSIXct(x))
|
||||
if (!is.null(min)) min <- to_ms(min)
|
||||
if (!is.null(max)) max <- to_ms(max)
|
||||
@@ -449,7 +446,10 @@ updateSliderInput <- function(session, inputId, label = NULL, value = NULL,
|
||||
value = formatNoSci(value),
|
||||
min = formatNoSci(min),
|
||||
max = formatNoSci(max),
|
||||
step = formatNoSci(step)
|
||||
step = formatNoSci(step),
|
||||
`data-type` = dataType,
|
||||
`time-format` = timeFormat,
|
||||
timezone = timezone
|
||||
))
|
||||
session$sendInputMessage(inputId, message)
|
||||
}
|
||||
|
||||
21
R/utils.R
21
R/utils.R
@@ -1679,14 +1679,14 @@ createVarPromiseDomain <- function(env, name, value) {
|
||||
force(env)
|
||||
force(name)
|
||||
force(value)
|
||||
|
||||
|
||||
promises::new_promise_domain(
|
||||
wrapOnFulfilled = function(onFulfilled) {
|
||||
function(...) {
|
||||
orig <- env[[name]]
|
||||
env[[name]] <- value
|
||||
on.exit(env[[name]] <- orig)
|
||||
|
||||
|
||||
onFulfilled(...)
|
||||
}
|
||||
},
|
||||
@@ -1695,7 +1695,7 @@ createVarPromiseDomain <- function(env, name, value) {
|
||||
orig <- env[[name]]
|
||||
env[[name]] <- value
|
||||
on.exit(env[[name]] <- orig)
|
||||
|
||||
|
||||
onRejected(...)
|
||||
}
|
||||
},
|
||||
@@ -1707,4 +1707,17 @@ createVarPromiseDomain <- function(env, name, value) {
|
||||
force(expr)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
getSliderType <- function(min, max, value) {
|
||||
vals <- dropNulls(list(value, min, max))
|
||||
type <- unique(lapply(vals, function(x) {
|
||||
if (inherits(x, "Date")) "date"
|
||||
else if (inherits(x, "POSIXt")) "datetime"
|
||||
else "number"
|
||||
}))
|
||||
if (length(type) > 1) {
|
||||
stop("Type mismatch for `min`, `max`, and `value`. Each must be Date, POSIXt, or number.")
|
||||
}
|
||||
type[[1]]
|
||||
}
|
||||
|
||||
@@ -2,10 +2,11 @@
|
||||
% Please edit documentation in R/update-input.R
|
||||
\name{updateSliderInput}
|
||||
\alias{updateSliderInput}
|
||||
\title{Change the value of a slider input on the client}
|
||||
\title{Update Slider Input Widget}
|
||||
\usage{
|
||||
updateSliderInput(session, inputId, label = NULL, value = NULL,
|
||||
min = NULL, max = NULL, step = NULL)
|
||||
min = NULL, max = NULL, step = NULL, timeFormat = NULL,
|
||||
timezone = NULL)
|
||||
}
|
||||
\arguments{
|
||||
\item{session}{The \code{session} object passed to function given to
|
||||
@@ -22,9 +23,13 @@ updateSliderInput(session, inputId, label = NULL, value = NULL,
|
||||
\item{max}{Maximum value.}
|
||||
|
||||
\item{step}{Step size.}
|
||||
|
||||
\item{timeFormat}{Date and POSIXt formatting.}
|
||||
|
||||
\item{timezone}{The timezone offset for POSIXt objects.}
|
||||
}
|
||||
\description{
|
||||
Change the value of a slider input on the client
|
||||
Change the value of a slider input on the client.
|
||||
}
|
||||
\details{
|
||||
The input updater functions send a message to the client, telling it to
|
||||
|
||||
@@ -6,6 +6,38 @@ function forceIonSliderUpdate(slider) {
|
||||
console.log("Couldn't force ion slider to update");
|
||||
}
|
||||
|
||||
function getTypePrettifyer(dataType, timeFormat, timezone) {
|
||||
var timeFormatter;
|
||||
var prettify;
|
||||
if (dataType === 'date') {
|
||||
timeFormatter = strftime.utc();
|
||||
prettify = function(num) {
|
||||
return timeFormatter(timeFormat, new Date(num));
|
||||
};
|
||||
|
||||
} else if (dataType === 'datetime') {
|
||||
if (timezone)
|
||||
timeFormatter = strftime.timezone(timezone);
|
||||
else
|
||||
timeFormatter = strftime;
|
||||
|
||||
prettify = function(num) {
|
||||
return timeFormatter(timeFormat, new Date(num));
|
||||
};
|
||||
|
||||
} else {
|
||||
// The default prettify function for ion.rangeSlider adds thousands
|
||||
// separators after the decimal mark, so we have our own version here.
|
||||
// (#1958)
|
||||
prettify = function(num) {
|
||||
// When executed, `this` will refer to the `IonRangeSlider.options`
|
||||
// object.
|
||||
return formatNumber(num, this.prettify_separator);
|
||||
};
|
||||
}
|
||||
return prettify;
|
||||
}
|
||||
|
||||
var sliderInputBinding = {};
|
||||
$.extend(sliderInputBinding, textInputBinding, {
|
||||
find: function(scope) {
|
||||
@@ -90,13 +122,31 @@ $.extend(sliderInputBinding, textInputBinding, {
|
||||
msg.from = data.value;
|
||||
}
|
||||
}
|
||||
if (data.hasOwnProperty('min')) msg.min = data.min;
|
||||
if (data.hasOwnProperty('max')) msg.max = data.max;
|
||||
if (data.hasOwnProperty('step')) msg.step = data.step;
|
||||
var sliderFeatures = ['min', 'max', 'step'];
|
||||
for (var i = 0; i < sliderFeatures.length; i++) {
|
||||
var feats = sliderFeatures[i];
|
||||
if (data.hasOwnProperty(feats)) {
|
||||
msg[feats] = data[feats];
|
||||
}
|
||||
}
|
||||
|
||||
if (data.hasOwnProperty('label'))
|
||||
$el.parent().find('label[for="' + $escape(el.id) + '"]').text(data.label);
|
||||
|
||||
var domElements = ['data-type', 'time-format', 'timezone'];
|
||||
for (var i = 0; i < domElements.length; i++) {
|
||||
var elem = domElements[i];
|
||||
if (data.hasOwnProperty(elem)) {
|
||||
$el.data(elem, data[elem]);
|
||||
}
|
||||
}
|
||||
|
||||
var dataType = $el.data('data-type');
|
||||
var timeFormat = $el.data('time-format');
|
||||
var timezone = $el.data('timezone');
|
||||
|
||||
msg.prettify = getTypePrettifyer(dataType, timeFormat, timezone);
|
||||
|
||||
$el.data('immediate', true);
|
||||
try {
|
||||
slider.update(msg);
|
||||
@@ -118,36 +168,9 @@ $.extend(sliderInputBinding, textInputBinding, {
|
||||
var $el = $(el);
|
||||
var dataType = $el.data('data-type');
|
||||
var timeFormat = $el.data('time-format');
|
||||
var timeFormatter;
|
||||
var timezone = $el.data('timezone');
|
||||
|
||||
// Set up formatting functions
|
||||
if (dataType === 'date') {
|
||||
timeFormatter = strftime.utc();
|
||||
opts.prettify = function(num) {
|
||||
return timeFormatter(timeFormat, new Date(num));
|
||||
};
|
||||
|
||||
} else if (dataType === 'datetime') {
|
||||
var timezone = $el.data('timezone');
|
||||
if (timezone)
|
||||
timeFormatter = strftime.timezone(timezone);
|
||||
else
|
||||
timeFormatter = strftime;
|
||||
|
||||
opts.prettify = function(num) {
|
||||
return timeFormatter(timeFormat, new Date(num));
|
||||
};
|
||||
|
||||
} else {
|
||||
// The default prettify function for ion.rangeSlider adds thousands
|
||||
// separators after the decimal mark, so we have our own version here.
|
||||
// (#1958)
|
||||
opts.prettify = function(num) {
|
||||
// When executed, `this` will refer to the `IonRangeSlider.options`
|
||||
// object.
|
||||
return formatNumber(num, this.prettify_separator);
|
||||
};
|
||||
}
|
||||
opts.prettify = getTypePrettifyer(dataType, timeFormat, timezone);
|
||||
|
||||
$el.ionRangeSlider(opts);
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user