mirror of
https://github.com/rstudio/shiny.git
synced 2026-01-12 16:38:06 -05:00
Compare commits
1 Commits
bindAfterR
...
v1.7.4.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f5b395485e |
@@ -1,7 +1,7 @@
|
||||
Package: shiny
|
||||
Type: Package
|
||||
Title: Web Application Framework for R
|
||||
Version: 1.7.4.9002
|
||||
Version: 1.7.4.1
|
||||
Authors@R: c(
|
||||
person("Winston", "Chang", role = c("aut", "cre"), email = "winston@rstudio.com", comment = c(ORCID = "0000-0002-1576-2126")),
|
||||
person("Joe", "Cheng", role = "aut", email = "joe@rstudio.com"),
|
||||
@@ -87,7 +87,7 @@ Imports:
|
||||
tools,
|
||||
crayon,
|
||||
rlang (>= 0.4.10),
|
||||
fastmap (>= 1.1.1),
|
||||
fastmap (>= 1.1.0),
|
||||
withr,
|
||||
commonmark (>= 1.7),
|
||||
glue (>= 1.3.2),
|
||||
@@ -111,7 +111,7 @@ Suggests:
|
||||
ragg,
|
||||
showtext,
|
||||
sass
|
||||
URL: https://shiny.rstudio.com/
|
||||
URL: https://shiny.posit.co/
|
||||
BugReports: https://github.com/rstudio/shiny/issues
|
||||
Collate:
|
||||
'globals.R'
|
||||
|
||||
21
NEWS.md
21
NEWS.md
@@ -1,22 +1,9 @@
|
||||
# shiny 1.7.4.9002
|
||||
# shiny 1.7.4.1
|
||||
|
||||
## Full changelog
|
||||
|
||||
### Breaking changes
|
||||
* Closed #3849: In R-devel, a warning was raised when Shiny was loaded because `as.numeric_version()` was called with a number instead of a string. (#3850)
|
||||
|
||||
### New features and improvements
|
||||
|
||||
* Closed #789: `<script>` loaded from dynamic UI are no longer loaded using synchronous `XMLHttpRequest` (via jQuery). (#3666)
|
||||
|
||||
* For `reactiveValues()` objects, whenever the `$names()` or `$values()` methods are called, the keys are now returned in the order that they were inserted. (#3774)
|
||||
|
||||
* `Map` objects are now initialized at load time instead of build time. This avoids potential problems that could arise from storing `fastmap` objects into the built Shiny package. (#3775)
|
||||
|
||||
* Closed #3635: `window.Shiny.outputBindings` and `window.Shiny.inputBindings` gain a `onRegister()` method, to register callbacks that execute whenever a new binding is registered. Internally, Shiny uses this to check whether it should re-bind to the DOM when a binding has been registered. (#3638)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
* Fixed #3771: Sometimes the error `ion.rangeSlider.min.js: i.stopPropagation is not a function` would appear in the JavaScript console. (#3772)
|
||||
|
||||
# shiny 1.7.4
|
||||
|
||||
@@ -167,7 +154,7 @@ This release focuses on improvements in three main areas:
|
||||
|
||||
1. Better theming (and Bootstrap 4) support:
|
||||
* The `theme` argument of `fluidPage()`, `navbarPage()`, and `bootstrapPage()` all now understand `bslib::bs_theme()` objects, which can be used to opt-into Bootstrap 4, use any Bootswatch theme, and/or implement custom themes without writing any CSS.
|
||||
* The `session` object now includes `$setCurrentTheme()` and `$getCurrentTheme()` methods to dynamically update (or obtain) the page's `theme` after initial load, which is useful for things such as [adding a dark mode switch to an app](https://rstudio.github.io/bslib/articles/bslib.html#dynamic) or some other "real-time" theming tool like `bslib::bs_themer()`.
|
||||
* The `session` object now includes `$setCurrentTheme()` and `$getCurrentTheme()` methods to dynamically update (or obtain) the page's `theme` after initial load, which is useful for things such as [adding a dark mode switch to an app](https://rstudio.github.io/bslib/articles/theming.html#dynamic) or some other "real-time" theming tool like `bslib::bs_themer()`.
|
||||
* For more details, see [`{bslib}`'s website](https://rstudio.github.io/bslib/)
|
||||
|
||||
2. Caching of `reactive()` and `render*()` (e.g. `renderText()`, `renderTable()`, etc) expressions.
|
||||
@@ -560,7 +547,7 @@ This is a significant release for Shiny, with a major new feature that was nearl
|
||||
|
||||
* Fixed #1600: URL-encoded bookmarking did not work with sliders that had dates or date-times. (#1961)
|
||||
|
||||
* Fixed #1962: [File dragging and dropping](https://www.rstudio.com/blog/shiny-1-0-4/) broke in the presence of jQuery version 3.0 as introduced by the [rhandsontable](https://jrowen.github.io/rhandsontable/) [htmlwidget](https://www.htmlwidgets.org/). (#2005)
|
||||
* Fixed #1962: [File dragging and dropping](https://posit.co/blog/shiny-1-0-4/) broke in the presence of jQuery version 3.0 as introduced by the [rhandsontable](https://jrowen.github.io/rhandsontable/) [htmlwidget](https://www.htmlwidgets.org/). (#2005)
|
||||
|
||||
* Improved the error handling inside the `addResourcePath()` function, to give end users more informative error messages when the `directoryPath` argument cannot be normalized. This is especially useful for `runtime: shiny_prerendered` Rmd documents, like `learnr` tutorials. (#1968)
|
||||
|
||||
|
||||
@@ -452,10 +452,8 @@ RestoreInputSet <- R6Class("RestoreInputSet",
|
||||
)
|
||||
)
|
||||
|
||||
# This is a fastmap::faststack(); value is assigned in .onLoad().
|
||||
restoreCtxStack <- NULL
|
||||
on_load({
|
||||
restoreCtxStack <- fastmap::faststack()
|
||||
})
|
||||
|
||||
withRestoreContext <- function(ctx, expr) {
|
||||
restoreCtxStack$push(ctx)
|
||||
|
||||
39
R/devmode.R
39
R/devmode.R
@@ -190,10 +190,8 @@ devmode_inform <- function(
|
||||
|
||||
|
||||
|
||||
registered_devmode_options <- NULL
|
||||
on_load({
|
||||
registered_devmode_options <- Map$new()
|
||||
})
|
||||
#' @include map.R
|
||||
registered_devmode_options <- Map$new()
|
||||
|
||||
#' @describeIn devmode Registers a Shiny Developer Mode option with an updated
|
||||
#' value and Developer message. This registration method allows package
|
||||
@@ -342,22 +340,21 @@ get_devmode_option <- function(
|
||||
}
|
||||
|
||||
|
||||
on_load({
|
||||
register_devmode_option(
|
||||
"shiny.autoreload",
|
||||
"Turning on shiny autoreload. To disable, call `options(shiny.autoreload = FALSE)`",
|
||||
TRUE
|
||||
)
|
||||
|
||||
register_devmode_option(
|
||||
"shiny.minified",
|
||||
"Using full shiny javascript file. To use the minified version, call `options(shiny.minified = TRUE)`",
|
||||
FALSE
|
||||
)
|
||||
register_devmode_option(
|
||||
"shiny.autoreload",
|
||||
"Turning on shiny autoreload. To disable, call `options(shiny.autoreload = FALSE)`",
|
||||
TRUE
|
||||
)
|
||||
|
||||
register_devmode_option(
|
||||
"shiny.fullstacktrace",
|
||||
"Turning on full stack trace. To disable, call `options(shiny.fullstacktrace = FALSE)`",
|
||||
TRUE
|
||||
)
|
||||
})
|
||||
register_devmode_option(
|
||||
"shiny.minified",
|
||||
"Using full shiny javascript file. To use the minified version, call `options(shiny.minified = TRUE)`",
|
||||
FALSE
|
||||
)
|
||||
|
||||
register_devmode_option(
|
||||
"shiny.fullstacktrace",
|
||||
"Turning on full stack trace. To disable, call `options(shiny.fullstacktrace = FALSE)`",
|
||||
TRUE
|
||||
)
|
||||
|
||||
20
R/globals.R
20
R/globals.R
@@ -7,27 +7,13 @@
|
||||
# the private seed during load.
|
||||
withPrivateSeed(set.seed(NULL))
|
||||
|
||||
for (expr in on_load_exprs) {
|
||||
eval(expr, envir = environment(.onLoad))
|
||||
}
|
||||
# Create this at the top level, but since the object is from a different
|
||||
# package, we don't want to bake it into the built binary package.
|
||||
restoreCtxStack <<- fastmap::faststack()
|
||||
|
||||
# Make sure these methods are available to knitr if shiny is loaded but not
|
||||
# attached.
|
||||
s3_register("knitr::knit_print", "reactive")
|
||||
s3_register("knitr::knit_print", "shiny.appobj")
|
||||
s3_register("knitr::knit_print", "shiny.render.function")
|
||||
|
||||
# Shiny 1.4.0 bumps jQuery 1.x to 3.x, which caused a problem
|
||||
# with static-rendering of htmlwidgets, and htmlwidgets 1.5
|
||||
# includes a fix for this problem
|
||||
# https://github.com/rstudio/shiny/issues/2630
|
||||
register_upgrade_message("htmlwidgets", 1.5)
|
||||
}
|
||||
|
||||
|
||||
on_load_exprs <- list()
|
||||
# Register an expression to be evaluated when the package is loaded (in the
|
||||
# .onLoad function).
|
||||
on_load <- function(expr) {
|
||||
on_load_exprs[[length(on_load_exprs) + 1]] <<- substitute(expr)
|
||||
}
|
||||
|
||||
@@ -559,6 +559,4 @@ MessageLogger = R6Class(
|
||||
)
|
||||
)
|
||||
|
||||
on_load({
|
||||
rLog <- RLog$new("shiny.reactlog", "shiny.reactlog.console")
|
||||
})
|
||||
rLog <- RLog$new("shiny.reactlog", "shiny.reactlog.console")
|
||||
|
||||
@@ -182,8 +182,8 @@ brushedPoints <- function(df, brush, xvar = NULL, yvar = NULL,
|
||||
# $ xmax : num 3.78
|
||||
# $ ymin : num 17.1
|
||||
# $ ymax : num 20.4
|
||||
# $ panelvar1: chr "6"
|
||||
# $ panelvar2: chr "0
|
||||
# $ panelvar1: int 6
|
||||
# $ panelvar2: int 0
|
||||
# $ coords_css:List of 4
|
||||
# ..$ xmin: int 260
|
||||
# ..$ xmax: int 298
|
||||
@@ -367,8 +367,8 @@ nearPoints <- function(df, coordinfo, xvar = NULL, yvar = NULL,
|
||||
# $ img_css_ratio:List of 2
|
||||
# ..$ x: num 1.25
|
||||
# ..$ y: num 1.25
|
||||
# $ panelvar1 : chr "6"
|
||||
# $ panelvar2 : chr "0"
|
||||
# $ panelvar1 : int 6
|
||||
# $ panelvar2 : int 0
|
||||
# $ mapping :List of 4
|
||||
# ..$ x : chr "wt"
|
||||
# ..$ y : chr "mpg"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#' Insert and remove UI objects
|
||||
#'
|
||||
#' These functions allow you to dynamically add and remove arbitrary UI
|
||||
#' These functions allow you to dynamically add and remove arbirary UI
|
||||
#' into your app, whenever you want, as many times as you want.
|
||||
#' Unlike [renderUI()], the UI generated with `insertUI()` is persistent:
|
||||
#' once it's created, it stays there until removed by `removeUI()`. Each
|
||||
@@ -11,7 +11,7 @@
|
||||
#' function.
|
||||
#'
|
||||
#' It's particularly useful to pair `removeUI` with `insertUI()`, but there is
|
||||
#' no restriction on what you can use it on. Any element that can be selected
|
||||
#' no restriction on what you can use on. Any element that can be selected
|
||||
#' through a jQuery selector can be removed through this function.
|
||||
#'
|
||||
#' @param selector A string that is accepted by jQuery's selector
|
||||
|
||||
@@ -43,10 +43,7 @@ removeModal <- function(session = getDefaultReactiveDomain()) {
|
||||
#' @param title An optional title for the dialog.
|
||||
#' @param footer UI for footer. Use `NULL` for no footer.
|
||||
#' @param size One of `"s"` for small, `"m"` (the default) for medium,
|
||||
#' `"l"` for large, or `"xl"` for extra large. Note that `"xl"` only
|
||||
#' works with Bootstrap 4 and above (to opt-in to Bootstrap 4+,
|
||||
#' pass [bslib::bs_theme()] to the `theme` argument of a page container
|
||||
#' like [fluidPage()]).
|
||||
#' or `"l"` for large.
|
||||
#' @param easyClose If `TRUE`, the modal dialog can be dismissed by
|
||||
#' clicking outside the dialog box, or be pressing the Escape key. If
|
||||
#' `FALSE` (the default), the modal dialog can't be dismissed in those
|
||||
|
||||
@@ -326,9 +326,6 @@ ReactiveValues <- R6Class(
|
||||
.dedupe = logical(0),
|
||||
# Key, asList(), or names() have been retrieved
|
||||
.hasRetrieved = list(),
|
||||
# All names, in insertion order. The names are also stored in the .values
|
||||
# object, but it does not preserve order.
|
||||
.nameOrder = character(0),
|
||||
|
||||
|
||||
initialize = function(
|
||||
@@ -406,11 +403,6 @@ ReactiveValues <- R6Class(
|
||||
return(invisible())
|
||||
}
|
||||
|
||||
# If it's new, append key to the name order
|
||||
if (!key_exists) {
|
||||
.nameOrder[length(.nameOrder) + 1] <<- key
|
||||
}
|
||||
|
||||
# set the value for better logging
|
||||
.values$set(key, value)
|
||||
|
||||
@@ -452,13 +444,14 @@ ReactiveValues <- R6Class(
|
||||
},
|
||||
|
||||
names = function() {
|
||||
nameValues <- .values$keys()
|
||||
if (!isTRUE(.hasRetrieved$names)) {
|
||||
domain <- getDefaultReactiveDomain()
|
||||
rLog$defineNames(.reactId, .nameOrder, .label, domain)
|
||||
rLog$defineNames(.reactId, nameValues, .label, domain)
|
||||
.hasRetrieved$names <<- TRUE
|
||||
}
|
||||
.namesDeps$register()
|
||||
return(.nameOrder)
|
||||
return(nameValues)
|
||||
},
|
||||
|
||||
# Get a metadata value. Does not trigger reactivity.
|
||||
@@ -506,7 +499,7 @@ ReactiveValues <- R6Class(
|
||||
},
|
||||
|
||||
toList = function(all.names=FALSE) {
|
||||
listValue <- .values$mget(.nameOrder)
|
||||
listValue <- .values$values()
|
||||
if (!all.names) {
|
||||
listValue <- listValue[!grepl("^\\.", base::names(listValue))]
|
||||
}
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
# Create a Map object for input handlers and register the defaults.
|
||||
# This is assigned in .onLoad time.
|
||||
inputHandlers <- NULL
|
||||
on_load({
|
||||
inputHandlers <- Map$new()
|
||||
})
|
||||
# Create a map for input handlers and register the defaults.
|
||||
inputHandlers <- Map$new()
|
||||
|
||||
#' Register an Input Handler
|
||||
#'
|
||||
@@ -129,117 +125,115 @@ applyInputHandlers <- function(inputs, shinysession = getDefaultReactiveDomain()
|
||||
inputs
|
||||
}
|
||||
|
||||
on_load({
|
||||
# Takes a list-of-lists and returns a matrix. The lists
|
||||
# must all be the same length. NULL is replaced by NA.
|
||||
registerInputHandler("shiny.matrix", function(data, ...) {
|
||||
if (length(data) == 0)
|
||||
return(matrix(nrow=0, ncol=0))
|
||||
|
||||
m <- matrix(unlist(lapply(data, function(x) {
|
||||
sapply(x, function(y) {
|
||||
ifelse(is.null(y), NA, y)
|
||||
})
|
||||
})), nrow = length(data[[1]]), ncol = length(data))
|
||||
return(m)
|
||||
})
|
||||
# Takes a list-of-lists and returns a matrix. The lists
|
||||
# must all be the same length. NULL is replaced by NA.
|
||||
registerInputHandler("shiny.matrix", function(data, ...) {
|
||||
if (length(data) == 0)
|
||||
return(matrix(nrow=0, ncol=0))
|
||||
|
||||
|
||||
registerInputHandler("shiny.number", function(val, ...){
|
||||
ifelse(is.null(val), NA, val)
|
||||
})
|
||||
|
||||
registerInputHandler("shiny.password", function(val, shinysession, name) {
|
||||
# Mark passwords as not serializable
|
||||
setSerializer(name, serializerUnserializable)
|
||||
val
|
||||
})
|
||||
|
||||
registerInputHandler("shiny.date", function(val, ...){
|
||||
# First replace NULLs with NA, then convert to Date vector
|
||||
datelist <- ifelse(lapply(val, is.null), NA, val)
|
||||
|
||||
res <- NULL
|
||||
tryCatch({
|
||||
res <- as.Date(unlist(datelist))
|
||||
},
|
||||
error = function(e) {
|
||||
# It's possible for client to send a string like "99999-01-01", which
|
||||
# as.Date can't handle.
|
||||
warning(e$message)
|
||||
res <<- as.Date(rep(NA, length(datelist)))
|
||||
}
|
||||
)
|
||||
|
||||
res
|
||||
})
|
||||
|
||||
registerInputHandler("shiny.datetime", function(val, ...){
|
||||
# First replace NULLs with NA, then convert to POSIXct vector
|
||||
times <- lapply(val, function(x) {
|
||||
if (is.null(x)) NA
|
||||
else x
|
||||
m <- matrix(unlist(lapply(data, function(x) {
|
||||
sapply(x, function(y) {
|
||||
ifelse(is.null(y), NA, y)
|
||||
})
|
||||
as.POSIXct(unlist(times), origin = "1970-01-01", tz = "UTC")
|
||||
})
|
||||
|
||||
registerInputHandler("shiny.action", function(val, shinysession, name) {
|
||||
# mark up the action button value with a special class so we can recognize it later
|
||||
class(val) <- c("shinyActionButtonValue", class(val))
|
||||
val
|
||||
})
|
||||
|
||||
registerInputHandler("shiny.file", function(val, shinysession, name) {
|
||||
# This function is only used when restoring a Shiny fileInput. When a file is
|
||||
# uploaded the usual way, it takes a different code path and won't hit this
|
||||
# function.
|
||||
if (is.null(val))
|
||||
return(NULL)
|
||||
|
||||
# The data will be a named list of lists; convert to a data frame.
|
||||
val <- as.data.frame(lapply(val, unlist), stringsAsFactors = FALSE)
|
||||
|
||||
# `val$datapath` should be a filename without a path, for security reasons.
|
||||
if (basename(val$datapath) != val$datapath) {
|
||||
stop("Invalid '/' found in file input path.")
|
||||
}
|
||||
|
||||
# Prepend the persistent dir
|
||||
oldfile <- file.path(getCurrentRestoreContext()$dir, val$datapath)
|
||||
|
||||
# Copy the original file to a new temp dir, so that a restored session can't
|
||||
# modify the original.
|
||||
newdir <- file.path(tempdir(), createUniqueId(12))
|
||||
dir.create(newdir)
|
||||
val$datapath <- file.path(newdir, val$datapath)
|
||||
file.copy(oldfile, val$datapath)
|
||||
|
||||
# Need to mark this input value with the correct serializer. When a file is
|
||||
# uploaded the usual way (instead of being restored), this occurs in
|
||||
# session$`@uploadEnd`.
|
||||
setSerializer(name, serializerFileInput)
|
||||
|
||||
snapshotPreprocessInput(name, snapshotPreprocessorFileInput)
|
||||
|
||||
val
|
||||
})
|
||||
|
||||
|
||||
# to be used with !!!answer
|
||||
registerInputHandler("shiny.symbolList", function(val, ...) {
|
||||
if (is.null(val)) {
|
||||
list()
|
||||
} else {
|
||||
lapply(val, as.symbol)
|
||||
}
|
||||
})
|
||||
# to be used with !!answer
|
||||
registerInputHandler("shiny.symbol", function(val, ...) {
|
||||
if (is.null(val) || identical(val, "")) {
|
||||
NULL
|
||||
} else {
|
||||
as.symbol(val)
|
||||
}
|
||||
})
|
||||
|
||||
})), nrow = length(data[[1]]), ncol = length(data))
|
||||
return(m)
|
||||
})
|
||||
|
||||
|
||||
registerInputHandler("shiny.number", function(val, ...){
|
||||
ifelse(is.null(val), NA, val)
|
||||
})
|
||||
|
||||
registerInputHandler("shiny.password", function(val, shinysession, name) {
|
||||
# Mark passwords as not serializable
|
||||
setSerializer(name, serializerUnserializable)
|
||||
val
|
||||
})
|
||||
|
||||
registerInputHandler("shiny.date", function(val, ...){
|
||||
# First replace NULLs with NA, then convert to Date vector
|
||||
datelist <- ifelse(lapply(val, is.null), NA, val)
|
||||
|
||||
res <- NULL
|
||||
tryCatch({
|
||||
res <- as.Date(unlist(datelist))
|
||||
},
|
||||
error = function(e) {
|
||||
# It's possible for client to send a string like "99999-01-01", which
|
||||
# as.Date can't handle.
|
||||
warning(e$message)
|
||||
res <<- as.Date(rep(NA, length(datelist)))
|
||||
}
|
||||
)
|
||||
|
||||
res
|
||||
})
|
||||
|
||||
registerInputHandler("shiny.datetime", function(val, ...){
|
||||
# First replace NULLs with NA, then convert to POSIXct vector
|
||||
times <- lapply(val, function(x) {
|
||||
if (is.null(x)) NA
|
||||
else x
|
||||
})
|
||||
as.POSIXct(unlist(times), origin = "1970-01-01", tz = "UTC")
|
||||
})
|
||||
|
||||
registerInputHandler("shiny.action", function(val, shinysession, name) {
|
||||
# mark up the action button value with a special class so we can recognize it later
|
||||
class(val) <- c("shinyActionButtonValue", class(val))
|
||||
val
|
||||
})
|
||||
|
||||
registerInputHandler("shiny.file", function(val, shinysession, name) {
|
||||
# This function is only used when restoring a Shiny fileInput. When a file is
|
||||
# uploaded the usual way, it takes a different code path and won't hit this
|
||||
# function.
|
||||
if (is.null(val))
|
||||
return(NULL)
|
||||
|
||||
# The data will be a named list of lists; convert to a data frame.
|
||||
val <- as.data.frame(lapply(val, unlist), stringsAsFactors = FALSE)
|
||||
|
||||
# `val$datapath` should be a filename without a path, for security reasons.
|
||||
if (basename(val$datapath) != val$datapath) {
|
||||
stop("Invalid '/' found in file input path.")
|
||||
}
|
||||
|
||||
# Prepend the persistent dir
|
||||
oldfile <- file.path(getCurrentRestoreContext()$dir, val$datapath)
|
||||
|
||||
# Copy the original file to a new temp dir, so that a restored session can't
|
||||
# modify the original.
|
||||
newdir <- file.path(tempdir(), createUniqueId(12))
|
||||
dir.create(newdir)
|
||||
val$datapath <- file.path(newdir, val$datapath)
|
||||
file.copy(oldfile, val$datapath)
|
||||
|
||||
# Need to mark this input value with the correct serializer. When a file is
|
||||
# uploaded the usual way (instead of being restored), this occurs in
|
||||
# session$`@uploadEnd`.
|
||||
setSerializer(name, serializerFileInput)
|
||||
|
||||
snapshotPreprocessInput(name, snapshotPreprocessorFileInput)
|
||||
|
||||
val
|
||||
})
|
||||
|
||||
|
||||
# to be used with !!!answer
|
||||
registerInputHandler("shiny.symbolList", function(val, ...) {
|
||||
if (is.null(val)) {
|
||||
list()
|
||||
} else {
|
||||
lapply(val, as.symbol)
|
||||
}
|
||||
})
|
||||
# to be used with !!answer
|
||||
registerInputHandler("shiny.symbol", function(val, ...) {
|
||||
if (is.null(val) || identical(val, "")) {
|
||||
NULL
|
||||
} else {
|
||||
as.symbol(val)
|
||||
}
|
||||
})
|
||||
|
||||
14
R/server.R
14
R/server.R
@@ -1,12 +1,7 @@
|
||||
#' @include server-input-handlers.R
|
||||
|
||||
appsByToken <- NULL
|
||||
appsNeedingFlush <- NULL
|
||||
on_load({
|
||||
appsByToken <- Map$new()
|
||||
appsNeedingFlush <- Map$new()
|
||||
})
|
||||
|
||||
appsByToken <- Map$new()
|
||||
appsNeedingFlush <- Map$new()
|
||||
|
||||
# Provide a character representation of the WS that can be used
|
||||
# as a key in a Map.
|
||||
@@ -127,10 +122,7 @@ decodeMessage <- function(data) {
|
||||
return(mainMessage)
|
||||
}
|
||||
|
||||
autoReloadCallbacks <- NULL
|
||||
on_load({
|
||||
autoReloadCallbacks <- Callbacks$new()
|
||||
})
|
||||
autoReloadCallbacks <- Callbacks$new()
|
||||
|
||||
createAppHandlers <- function(httpHandlers, serverFuncSource) {
|
||||
appvars <- new.env()
|
||||
|
||||
@@ -48,32 +48,6 @@ is_installed <- function(pkg, version = NULL) {
|
||||
installed && isTRUE(get_package_version(pkg) >= version)
|
||||
}
|
||||
|
||||
register_upgrade_message <- function(pkg, version, error = FALSE) {
|
||||
|
||||
msg <- sprintf(
|
||||
"This version of '%s' is designed to work with '%s' >= %s.
|
||||
Please upgrade via install.packages('%s').",
|
||||
environmentName(environment(register_upgrade_message)),
|
||||
pkg, version, pkg
|
||||
)
|
||||
|
||||
cond <- if (error) stop else packageStartupMessage
|
||||
|
||||
if (pkg %in% loadedNamespaces() && !is_installed(pkg, version)) {
|
||||
cond(msg)
|
||||
}
|
||||
|
||||
# Always register hook in case pkg is loaded at some
|
||||
# point the future (or, potentially, but less commonly,
|
||||
# unloaded & reloaded)
|
||||
setHook(
|
||||
packageEvent(pkg, "onLoad"),
|
||||
function(...) {
|
||||
if (!is_installed(pkg, version)) cond(msg)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
# Simplified version rlang:::s3_register() that just uses
|
||||
# warning() instead of rlang::warn() when registration fails
|
||||
# https://github.com/r-lib/rlang/blob/main/R/compat-s3-register.R
|
||||
@@ -190,11 +164,9 @@ system_file <- function(..., package = "base") {
|
||||
normalizePath(files, winslash = "/")
|
||||
}
|
||||
|
||||
# A wrapper for `system.file()`, which caches the results, because
|
||||
# `system.file()` can be slow. Note that because of caching, if
|
||||
# `system_file_cached()` is called on a package that isn't installed, then the
|
||||
# package is installed, and then `system_file_cached()` is called again, it will
|
||||
# still return "".
|
||||
# A wrapper for `system.file()`, which caches the package path because
|
||||
# `system.file()` can be slow. If a package is not installed, the result won't
|
||||
# be cached.
|
||||
system_file_cached <- local({
|
||||
pkg_dir_cache <- character()
|
||||
|
||||
@@ -206,7 +178,9 @@ system_file_cached <- local({
|
||||
not_cached <- is.na(match(package, names(pkg_dir_cache)))
|
||||
if (not_cached) {
|
||||
pkg_dir <- system.file(package = package)
|
||||
pkg_dir_cache[[package]] <<- pkg_dir
|
||||
if (nzchar(pkg_dir)) {
|
||||
pkg_dir_cache[[package]] <<- pkg_dir
|
||||
}
|
||||
} else {
|
||||
pkg_dir <- pkg_dir_cache[[package]]
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ NULL
|
||||
|
||||
# @staticimports pkg:staticimports
|
||||
# is_installed get_package_version system_file
|
||||
# s3_register register_upgrade_message
|
||||
# s3_register
|
||||
# any_named any_unnamed
|
||||
|
||||
#' Make a random number generator repeatable
|
||||
|
||||
@@ -16,7 +16,7 @@ Easily build rich and productive interactive web apps in R — no HTML/CSS/J
|
||||
* A prebuilt set of highly sophisticated, customizable, and easy-to-use widgets (e.g., plots, tables, sliders, dropdowns, date pickers, and more).
|
||||
* An attractive default look based on [Bootstrap](https://getbootstrap.com/) which can also be easily customized with the [bslib](https://github.com/rstudio/bslib) package or avoided entirely with more direct R bindings to HTML/CSS/JavaScript.
|
||||
* Seamless integration with [R Markdown](https://shiny.rstudio.com/articles/interactive-docs.html), making it easy to embed numerous applications natively within a larger dynamic document.
|
||||
* Tools for improving and monitoring performance, including native support for [async programming](https://www.rstudio.com/blog/shiny-1-1-0/), [caching](https://talks.cpsievert.me/20201117), [load testing](https://rstudio.github.io/shinyloadtest/), and more.
|
||||
* Tools for improving and monitoring performance, including native support for [async programming](https://posit.co/blog/shiny-1-1-0/), [caching](https://talks.cpsievert.me/20201117), [load testing](https://rstudio.github.io/shinyloadtest/), and more.
|
||||
* [Modules](https://shiny.rstudio.com/articles/modules.html): a framework for reducing code duplication and complexity.
|
||||
* An ability to [bookmark application state](https://shiny.rstudio.com/articles/bookmarking-state.html) and/or [generate code to reproduce output(s)](https://github.com/rstudio/shinymeta).
|
||||
* A rich ecosystem of extension packages for more [custom widgets](http://www.htmlwidgets.org/), [input validation](https://github.com/rstudio/shinyvalidate), [unit testing](https://github.com/rstudio/shinytest), and more.
|
||||
|
||||
@@ -753,7 +753,7 @@
|
||||
x = $handle.offset().left;
|
||||
x += ($handle.width() / 2) - 1;
|
||||
|
||||
this.pointerClick("single", {preventDefault: function () {}, stopPropagation: function () {}, pageX: x});
|
||||
this.pointerClick("single", {preventDefault: function () {}, pageX: x});
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
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
@@ -1,2 +1,2 @@
|
||||
/*! shiny 1.7.4.9002 | (c) 2012-2023 RStudio, PBC. | License: GPL-3 | file LICENSE */
|
||||
/*! shiny 1.7.4.1 | (c) 2012-2023 RStudio, PBC. | License: GPL-3 | file LICENSE */
|
||||
#showcase-well{border-radius:0}.shiny-code{background-color:#fff;margin-bottom:0}.shiny-code code{font-family:Menlo,Consolas,Courier New,monospace}.shiny-code-container{margin-top:20px;clear:both}.shiny-code-container h3{display:inline;margin-right:15px}.showcase-header{font-size:16px;font-weight:400}.showcase-code-link{text-align:right;padding:15px}#showcase-app-container{vertical-align:top}#showcase-code-tabs{margin-right:15px}#showcase-code-tabs pre{border:none;line-height:1em}#showcase-code-tabs .nav,#showcase-code-tabs ul{margin-bottom:0}#showcase-code-tabs .tab-content{border-style:solid;border-color:#e5e5e5;border-width:0px 1px 1px 1px;overflow:auto;border-bottom-right-radius:4px;border-bottom-left-radius:4px}#showcase-app-code{width:100%}#showcase-code-position-toggle{float:right}#showcase-sxs-code{padding-top:20px;vertical-align:top}.showcase-code-license{display:block;text-align:right}#showcase-code-content pre{background-color:#fff}
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,3 +1,3 @@
|
||||
/*! shiny 1.7.4.9002 | (c) 2012-2023 RStudio, PBC. | License: GPL-3 | file LICENSE */
|
||||
/*! shiny 1.7.4.1 | (c) 2012-2023 RStudio, PBC. | License: GPL-3 | file LICENSE */
|
||||
"use strict";(function(){var a=eval;window.addEventListener("message",function(i){var e=i.data;e.code&&a(e.code)});})();
|
||||
//# sourceMappingURL=shiny-testmode.js.map
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"version": 3,
|
||||
"sources": ["../../../srcts/src/utils/eval.ts", "../../../srcts/extras/shiny-testmode.ts"],
|
||||
"sourcesContent": ["//esbuild.github.io/content-types/#direct-eval\n//tl/dr;\n// * Direct usage of `eval(\"x\")` is bad with bundled code.\n// * Instead, use indirect calls to `eval` such as `indirectEval(\"x\")`\n// * Even just renaming the function works well enough.\n// > This is known as \"indirect eval\" because eval is not being called directly, and so does not trigger the grammatical special case for direct eval in the JavaScript VM. You can call indirect eval using any syntax at all except for an expression of the exact form eval('x'). For example, var eval2 = eval; eval2('x') and [eval][0]('x') and window.eval('x') are all indirect eval calls.\n// > When you use indirect eval, the code is evaluated in the global scope instead of in the inline scope of the caller.\n\nvar indirectEval = eval;\nexport { indirectEval };", "/* eslint-disable unicorn/filename-case */\nimport { indirectEval } from \"../src/utils/eval\";\n\n// Listen for messages from parent frame. This file is only added when the\n// shiny.testmode option is TRUE.\nwindow.addEventListener(\"message\", function (e) {\n var message = e.data;\n if (message.code) indirectEval(message.code);\n});"],
|
||||
"mappings": ";yBAQA,IAAIA,EAAe,KCHnB,OAAO,iBAAiB,UAAW,SAAUC,EAAG,CAC9C,IAAIC,EAAUD,EAAE,KACZC,EAAQ,MAAMC,EAAaD,EAAQ,IAAI,CAC7C,CAAC",
|
||||
"sourcesContent": ["//esbuild.github.io/content-types/#direct-eval\n//tl/dr;\n// * Direct usage of `eval(\"x\")` is bad with bundled code.\n// * Instead, use indirect calls to `eval` such as `indirectEval(\"x\")`\n// * Even just renaming the function works well enough.\n// > This is known as \"indirect eval\" because eval is not being called directly, and so does not trigger the grammatical special case for direct eval in the JavaScript VM. You can call indirect eval using any syntax at all except for an expression of the exact form eval('x'). For example, var eval2 = eval; eval2('x') and [eval][0]('x') and window.eval('x') are all indirect eval calls.\n// > When you use indirect eval, the code is evaluated in the global scope instead of in the inline scope of the caller.\nvar indirectEval = eval;\nexport { indirectEval };", "/* eslint-disable unicorn/filename-case */\nimport { indirectEval } from \"../src/utils/eval\"; // Listen for messages from parent frame. This file is only added when the\n// shiny.testmode option is TRUE.\n\nwindow.addEventListener(\"message\", function (e) {\n var message = e.data;\n if (message.code) indirectEval(message.code);\n});"],
|
||||
"mappings": ";yBAOA,IAAIA,EAAe,KCHnB,OAAO,iBAAiB,UAAW,SAAUC,EAAG,CAC9C,IAAIC,EAAUD,EAAE,KACZC,EAAQ,MAAMC,EAAaD,EAAQ,IAAI,CAC7C,CAAC",
|
||||
"names": ["indirectEval", "e", "message", "indirectEval"]
|
||||
}
|
||||
|
||||
13305
inst/www/shared/shiny.js
13305
inst/www/shared/shiny.js
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
2
inst/www/shared/shiny.min.css
vendored
2
inst/www/shared/shiny.min.css
vendored
File diff suppressed because one or more lines are too long
5
inst/www/shared/shiny.min.js
vendored
5
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
@@ -64,7 +64,7 @@ updated and all observers have been run (default).}
|
||||
\item{session}{The shiny session. Advanced use only.}
|
||||
}
|
||||
\description{
|
||||
These functions allow you to dynamically add and remove arbitrary UI
|
||||
These functions allow you to dynamically add and remove arbirary UI
|
||||
into your app, whenever you want, as many times as you want.
|
||||
Unlike \code{\link[=renderUI]{renderUI()}}, the UI generated with \code{insertUI()} is persistent:
|
||||
once it's created, it stays there until removed by \code{removeUI()}. Each
|
||||
@@ -76,7 +76,7 @@ function.
|
||||
}
|
||||
\details{
|
||||
It's particularly useful to pair \code{removeUI} with \code{insertUI()}, but there is
|
||||
no restriction on what you can use it on. Any element that can be selected
|
||||
no restriction on what you can use on. Any element that can be selected
|
||||
through a jQuery selector can be removed through this function.
|
||||
}
|
||||
\examples{
|
||||
|
||||
@@ -24,10 +24,7 @@ modalButton(label, icon = NULL)
|
||||
\item{footer}{UI for footer. Use \code{NULL} for no footer.}
|
||||
|
||||
\item{size}{One of \code{"s"} for small, \code{"m"} (the default) for medium,
|
||||
\code{"l"} for large, or \code{"xl"} for extra large. Note that \code{"xl"} only
|
||||
works with Bootstrap 4 and above (to opt-in to Bootstrap 4+,
|
||||
pass \code{\link[bslib:bs_theme]{bslib::bs_theme()}} to the \code{theme} argument of a page container
|
||||
like \code{\link[=fluidPage]{fluidPage()}}).}
|
||||
or \code{"l"} for large.}
|
||||
|
||||
\item{easyClose}{If \code{TRUE}, the modal dialog can be dismissed by
|
||||
clicking outside the dialog box, or be pressing the Escape key. If
|
||||
|
||||
10
package.json
10
package.json
@@ -3,7 +3,7 @@
|
||||
"homepage": "https://shiny.rstudio.com",
|
||||
"repository": "github:rstudio/shiny",
|
||||
"name": "@types/rstudio-shiny",
|
||||
"version": "1.7.4-alpha.9002",
|
||||
"version": "1.7.4-alpha.1",
|
||||
"license": "GPL-3.0-only",
|
||||
"main": "",
|
||||
"browser": "",
|
||||
@@ -40,11 +40,12 @@
|
||||
"@types/jest": "^26.0.23",
|
||||
"@types/jqueryui": "1.12.16",
|
||||
"@types/lodash": "^4.14.170",
|
||||
"@types/node": "^18.14.2",
|
||||
"@types/node": "^15.6.1",
|
||||
"@types/showdown": "^1.9.3",
|
||||
"@typescript-eslint/eslint-plugin": "^5.38.1",
|
||||
"@typescript-eslint/parser": "^5.38.1",
|
||||
"autoprefixer": "^10.2.6",
|
||||
"bootstrap-datepicker": "1.9.0",
|
||||
"browserslist": "^4.19.1",
|
||||
"caniuse-lite": "^1.0.30001312",
|
||||
"core-js": "^3.13.0",
|
||||
@@ -59,8 +60,9 @@
|
||||
"eslint-plugin-prettier": "^4.2.1",
|
||||
"eslint-plugin-unicorn": "^43.0.2",
|
||||
"fs-readdir-recursive": "^1.1.0",
|
||||
"ion-rangeslider": "2.3.1",
|
||||
"jest": "^26.6.3",
|
||||
"jquery": "^3.6.0",
|
||||
"jquery": "3.6.0",
|
||||
"lodash": "^4.17.21",
|
||||
"madge": "^4.0.2",
|
||||
"node-gyp": "^8.1.0",
|
||||
@@ -69,6 +71,8 @@
|
||||
"prettier": "^2.7.1",
|
||||
"readcontrol": "^1.0.0",
|
||||
"replace": "^1.2.1",
|
||||
"selectize": "0.12.4",
|
||||
"strftime": "0.9.2",
|
||||
"ts-jest": "^26",
|
||||
"ts-node": "^10.9.1",
|
||||
"type-coverage": "^2.22.0",
|
||||
|
||||
@@ -2,7 +2,7 @@ import $ from "jquery";
|
||||
|
||||
import { OutputBinding } from "./outputBinding";
|
||||
import { shinyUnbindAll } from "../../shiny/initedMethods";
|
||||
import { renderContentAsync } from "../../shiny/render";
|
||||
import { renderContent } from "../../shiny/render";
|
||||
import type { ErrorsMessageValue } from "../../shiny/shinyapp";
|
||||
|
||||
class HtmlOutputBinding extends OutputBinding {
|
||||
@@ -13,11 +13,11 @@ class HtmlOutputBinding extends OutputBinding {
|
||||
shinyUnbindAll(el);
|
||||
this.renderError(el, err);
|
||||
}
|
||||
override async renderValue(
|
||||
renderValue(
|
||||
el: HTMLElement,
|
||||
data: Parameters<typeof renderContentAsync>[1]
|
||||
): Promise<void> {
|
||||
await renderContentAsync(el, data);
|
||||
data: Parameters<typeof renderContent>[1]
|
||||
): void {
|
||||
renderContent(el, data);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ class OutputBinding {
|
||||
throw "Not implemented";
|
||||
scope;
|
||||
}
|
||||
renderValue(el: HTMLElement, data: unknown): Promise<void> | void {
|
||||
renderValue(el: HTMLElement, data: unknown): void {
|
||||
throw "Not implemented";
|
||||
el;
|
||||
data;
|
||||
@@ -21,9 +21,9 @@ class OutputBinding {
|
||||
return el.getAttribute("data-input-id") || el.id;
|
||||
}
|
||||
|
||||
async onValueChange(el: HTMLElement, data: unknown): Promise<void> {
|
||||
onValueChange(el: HTMLElement, data: unknown): void {
|
||||
this.clearError(el);
|
||||
await this.renderValue(el, data);
|
||||
this.renderValue(el, data);
|
||||
}
|
||||
onValueError(el: HTMLElement, err: ErrorsMessageValue): void {
|
||||
this.renderError(el, err);
|
||||
|
||||
@@ -31,8 +31,8 @@ class OutputBindingAdapter {
|
||||
getId(): string {
|
||||
return this.binding.getId(this.el);
|
||||
}
|
||||
async onValueChange(data: unknown): Promise<void> {
|
||||
await this.binding.onValueChange(this.el, data);
|
||||
onValueChange(data: unknown): void {
|
||||
this.binding.onValueChange(this.el, data);
|
||||
}
|
||||
onValueError(err: ErrorsMessageValue): void {
|
||||
this.binding.onValueError(this.el, err);
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { mergeSort } from "../utils";
|
||||
import { Callbacks } from "../utils/callbacks";
|
||||
|
||||
interface BindingBase {
|
||||
name: string;
|
||||
@@ -15,7 +14,6 @@ class BindingRegistry<Binding extends BindingBase> {
|
||||
name!: string;
|
||||
bindings: Array<BindingObj<Binding>> = [];
|
||||
bindingNames: { [key: string]: BindingObj<Binding> } = {};
|
||||
registerCallbacks: Callbacks = new Callbacks();
|
||||
|
||||
register(binding: Binding, bindingName: string, priority = 0): void {
|
||||
const bindingObj = { binding, priority };
|
||||
@@ -25,12 +23,6 @@ class BindingRegistry<Binding extends BindingBase> {
|
||||
this.bindingNames[bindingName] = bindingObj;
|
||||
binding.name = bindingName;
|
||||
}
|
||||
|
||||
this.registerCallbacks.invoke();
|
||||
}
|
||||
|
||||
onRegister(fn: () => void, once = true): void {
|
||||
this.registerCallbacks.register(fn, once);
|
||||
}
|
||||
|
||||
setPriority(bindingName: string, priority: number): void {
|
||||
|
||||
@@ -58,7 +58,6 @@ type BrushOpts = {
|
||||
type Brush = {
|
||||
reset: () => void;
|
||||
|
||||
hasOldBrush: () => boolean;
|
||||
importOldBrush: () => void;
|
||||
isInsideBrush: (offsetCss: Offset) => boolean;
|
||||
isInResizeArea: (offsetCss: Offset) => boolean;
|
||||
@@ -174,15 +173,10 @@ function createBrush(
|
||||
if ($div) $div.remove();
|
||||
}
|
||||
|
||||
function hasOldBrush(): boolean {
|
||||
const oldDiv = $el.find("#" + el.id + "_brush");
|
||||
return oldDiv.length > 0;
|
||||
}
|
||||
|
||||
// If there's an existing brush div, use that div to set the new brush's
|
||||
// settings, provided that the x, y, and panel variables have the same names,
|
||||
// and there's a panel with matching panel variable values.
|
||||
function importOldBrush(): void {
|
||||
function importOldBrush() {
|
||||
const oldDiv = $el.find("#" + el.id + "_brush");
|
||||
|
||||
if (oldDiv.length === 0) return;
|
||||
@@ -623,7 +617,6 @@ function createBrush(
|
||||
return {
|
||||
reset: reset,
|
||||
|
||||
hasOldBrush,
|
||||
importOldBrush: importOldBrush,
|
||||
isInsideBrush: isInsideBrush,
|
||||
isInResizeArea: isInResizeArea,
|
||||
|
||||
@@ -57,9 +57,6 @@ function createClickHandler(
|
||||
): CreateHandler {
|
||||
const clickInfoSender = coordmap.mouseCoordinateSender(inputId, clip);
|
||||
|
||||
// Send initial (null) value on creation.
|
||||
clickInfoSender(null);
|
||||
|
||||
return {
|
||||
mousedown: function (e) {
|
||||
// Listen for left mouse button only
|
||||
@@ -93,9 +90,6 @@ function createHoverHandler(
|
||||
hoverInfoSender = new Throttler(null, sendHoverInfo, delay);
|
||||
else hoverInfoSender = new Debouncer(null, sendHoverInfo, delay);
|
||||
|
||||
// Send initial (null) value on creation.
|
||||
hoverInfoSender.immediateCall(null);
|
||||
|
||||
// What to do when mouse exits the image
|
||||
let mouseout: () => void;
|
||||
|
||||
@@ -239,11 +233,6 @@ function createBrushHandler(
|
||||
brushInfoSender = new Debouncer(null, sendBrushInfo, opts.brushDelay);
|
||||
}
|
||||
|
||||
// Send initial (null) value on creation.
|
||||
if (!brush.hasOldBrush()) {
|
||||
brushInfoSender.immediateCall();
|
||||
}
|
||||
|
||||
function mousedown(e: JQuery.MouseDownEvent) {
|
||||
// This can happen when mousedown inside the graphic, then mouseup
|
||||
// outside, then mousedown inside. Just ignore the second
|
||||
|
||||
@@ -6,9 +6,9 @@ import type { ShinyApp } from "../shiny/shinyapp";
|
||||
class InputBatchSender implements InputPolicy {
|
||||
target!: InputPolicy; // We need this field to satisfy the InputPolicy interface
|
||||
shinyapp: ShinyApp;
|
||||
timerId: ReturnType<typeof setTimeout> | null = null;
|
||||
pendingData: { [key: string]: unknown } = {};
|
||||
reentrant = false;
|
||||
sendIsEnqueued = false;
|
||||
lastChanceCallback: Array<() => void> = [];
|
||||
|
||||
constructor(shinyapp: ShinyApp) {
|
||||
@@ -21,11 +21,8 @@ class InputBatchSender implements InputPolicy {
|
||||
if (!this.reentrant) {
|
||||
if (opts.priority === "event") {
|
||||
this._sendNow();
|
||||
} else if (!this.sendIsEnqueued) {
|
||||
this.shinyapp.actionQueue.enqueue(() => {
|
||||
this.sendIsEnqueued = false;
|
||||
this._sendNow();
|
||||
});
|
||||
} else if (!this.timerId) {
|
||||
this.timerId = setTimeout(this._sendNow.bind(this), 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -37,6 +34,7 @@ class InputBatchSender implements InputPolicy {
|
||||
|
||||
this.reentrant = true;
|
||||
try {
|
||||
this.timerId = null;
|
||||
this.lastChanceCallback.forEach((callback) => callback());
|
||||
const currentData = this.pendingData;
|
||||
|
||||
|
||||
@@ -6,14 +6,7 @@ import { $escape, compareVersion } from "../utils";
|
||||
import { showNotification, removeNotification } from "./notifications";
|
||||
import { showModal, removeModal } from "./modal";
|
||||
import { showReconnectDialog, hideReconnectDialog } from "./reconnectDialog";
|
||||
import {
|
||||
renderContentAsync,
|
||||
renderContent,
|
||||
renderDependenciesAsync,
|
||||
renderDependencies,
|
||||
renderHtmlAsync,
|
||||
renderHtml,
|
||||
} from "./render";
|
||||
import { renderContent, renderDependencies, renderHtml } from "./render";
|
||||
import { initShiny } from "./init";
|
||||
import type {
|
||||
shinyBindAll,
|
||||
@@ -47,11 +40,8 @@ interface Shiny {
|
||||
createSocket?: () => WebSocket;
|
||||
showReconnectDialog: typeof showReconnectDialog;
|
||||
hideReconnectDialog: typeof hideReconnectDialog;
|
||||
renderDependenciesAsync: typeof renderDependenciesAsync;
|
||||
renderDependencies: typeof renderDependencies;
|
||||
renderContentAsync: typeof renderContentAsync;
|
||||
renderContent: typeof renderContent;
|
||||
renderHtmlAsync: typeof renderHtmlAsync;
|
||||
renderHtml: typeof renderHtml;
|
||||
user: string;
|
||||
progressHandlers?: ShinyApp["progressHandlers"];
|
||||
@@ -100,11 +90,8 @@ function setShiny(windowShiny_: Shiny): void {
|
||||
windowShiny.addCustomMessageHandler = addCustomMessageHandler;
|
||||
windowShiny.showReconnectDialog = showReconnectDialog;
|
||||
windowShiny.hideReconnectDialog = hideReconnectDialog;
|
||||
windowShiny.renderDependenciesAsync = renderDependenciesAsync;
|
||||
windowShiny.renderDependencies = renderDependencies;
|
||||
windowShiny.renderContentAsync = renderContentAsync;
|
||||
windowShiny.renderContent = renderContent;
|
||||
windowShiny.renderHtmlAsync = renderHtmlAsync;
|
||||
windowShiny.renderHtml = renderHtml;
|
||||
|
||||
$(function () {
|
||||
|
||||
@@ -150,24 +150,6 @@ function initShiny(windowShiny: Shiny): void {
|
||||
(x) => x.value
|
||||
);
|
||||
|
||||
// When future bindings are registered, rebind to the DOM once the current
|
||||
// event loop is done. This is necessary since the binding might be registered
|
||||
// after Shiny has already bound to the DOM (#3635)
|
||||
let enqueuedCount = 0;
|
||||
const enqueueRebind = () => {
|
||||
enqueuedCount++;
|
||||
windowShiny.shinyapp?.actionQueue.enqueue(() => {
|
||||
enqueuedCount--;
|
||||
// If this function is scheduled more than once in the queue, don't do anything.
|
||||
// Only do the bindAll when we get to the last instance of this function in the queue.
|
||||
if (enqueuedCount === 0) {
|
||||
windowShiny.bindAll?.(document.documentElement);
|
||||
}
|
||||
});
|
||||
};
|
||||
inputBindings.onRegister(enqueueRebind, false);
|
||||
outputBindings.onRegister(enqueueRebind, false);
|
||||
|
||||
// The server needs to know the size of each image and plot output element,
|
||||
// in case it is auto-sizing
|
||||
$(".shiny-image-output, .shiny-plot-output, .shiny-report-size").each(
|
||||
|
||||
@@ -1,28 +1,12 @@
|
||||
import $ from "jquery";
|
||||
import { shinyUnbindAll } from "./initedMethods";
|
||||
import type { HtmlDep } from "./render";
|
||||
import { renderContentAsync, renderDependenciesAsync } from "./render";
|
||||
import { renderContent } from "./render";
|
||||
|
||||
// Show a modal dialog. This is meant to handle two types of cases: one is
|
||||
// that the content is a Bootstrap modal dialog, and the other is that the
|
||||
// content is non-Bootstrap. Bootstrap modals require some special handling,
|
||||
// which is coded in here.
|
||||
async function show({
|
||||
html = "",
|
||||
deps = [],
|
||||
}: {
|
||||
html?: string;
|
||||
deps?: HtmlDep[];
|
||||
} = {}): Promise<void> {
|
||||
// Normally we'd first create the modal's DOM elements, then call
|
||||
// `renderContentAsync(x, {html: html, deps: deps})`, but that has a potential
|
||||
// problem with async rendering. If we did that, then an empty modal (from
|
||||
// this function) could show up and then sit there empty while the
|
||||
// dependencies load (asynchronously), and only after all that get filled with
|
||||
// content for the modal. So instead we'll render the deps here, then render
|
||||
// the modal, then render the content in the modal.
|
||||
await renderDependenciesAsync(deps);
|
||||
|
||||
function show({ html = "", deps = [] } = {}): void {
|
||||
// If there was an existing Bootstrap modal, then there will be a modal-
|
||||
// backdrop div that was added outside of the modal wrapper, and it must be
|
||||
// removed; otherwise there can be multiple of these divs.
|
||||
@@ -60,7 +44,7 @@ async function show({
|
||||
});
|
||||
|
||||
// Set/replace contents of wrapper with html.
|
||||
await renderContentAsync($modal, { html: html });
|
||||
renderContent($modal, { html: html, deps: deps });
|
||||
}
|
||||
|
||||
function remove(): void {
|
||||
|
||||
@@ -3,13 +3,12 @@ import $ from "jquery";
|
||||
import { $escape, randomId } from "../utils";
|
||||
import { shinyUnbindAll } from "./initedMethods";
|
||||
import type { HtmlDep } from "./render";
|
||||
import { renderDependenciesAsync } from "./render";
|
||||
import { renderContentAsync } from "./render";
|
||||
import { renderContent } from "./render";
|
||||
|
||||
// Milliseconds to fade in or out
|
||||
const fadeDuration = 250;
|
||||
|
||||
async function show({
|
||||
function show({
|
||||
html = "",
|
||||
action = "",
|
||||
deps = [],
|
||||
@@ -25,18 +24,9 @@ async function show({
|
||||
id?: string | null;
|
||||
closeButton?: boolean;
|
||||
type?: string | null;
|
||||
} = {}): Promise<ReturnType<typeof randomId>> {
|
||||
} = {}): ReturnType<typeof randomId> {
|
||||
if (!id) id = randomId();
|
||||
|
||||
// Normally we'd first create the notification's DOM elements, then call
|
||||
// `renderContentAsync(x, {html: html, deps: deps})`, but that has a potential
|
||||
// problem with async rendering. If we did that, then an empty notification
|
||||
// (from this function) could show up and then sit there empty while the
|
||||
// dependencies load (asynchronously), and only after all that get filled with
|
||||
// content for the notification. So instead we'll render the deps here, then
|
||||
// render the notification, then render the content in the notification.
|
||||
await renderDependenciesAsync(deps);
|
||||
|
||||
// Create panel if necessary
|
||||
createPanel();
|
||||
|
||||
@@ -52,8 +42,7 @@ async function show({
|
||||
`<div class="shiny-notification-content-action">${action}</div>`;
|
||||
const $content = $notification.find(".shiny-notification-content");
|
||||
|
||||
// Set/replace contents of wrapper with html.
|
||||
await renderContentAsync($content, { html: newHtml });
|
||||
renderContent($content, { html: newHtml, deps: deps });
|
||||
|
||||
// Remove any existing classes of the form 'shiny-notification-xxxx'.
|
||||
// The xxxx would be strings like 'warning'.
|
||||
|
||||
@@ -12,73 +12,15 @@ import { sendImageSizeFns } from "./sendImageSize";
|
||||
import { renderHtml as singletonsRenderHtml } from "./singletons";
|
||||
import type { WherePosition } from "./singletons";
|
||||
|
||||
// There are synchronous and asynchronous versions of the exported functions
|
||||
// renderContent(), renderHtml(), and renderDependencies(). This is because they
|
||||
// the original versions of these functions were synchronous, but we added
|
||||
// support for asynchronous rendering, to avoid the deprecated XMLHttpRequest
|
||||
// function (https://github.com/rstudio/shiny/pull/3666).
|
||||
//
|
||||
// At the bottom, there is the appendScriptTags(), which calls $.append(), which
|
||||
// in turn calls (synchronous) XMLHttpRequest(); and its counterpart
|
||||
// appendScriptTagsAsync(), which uses a different (asynchronous) method. The
|
||||
// sync and async versions of this function necessitate the sync and async
|
||||
// versions of the other functions.
|
||||
//
|
||||
// The async versions of these functions are used internally and should be used
|
||||
// for new external code when possible, but for backward compatibility for
|
||||
// external code that calls these functions, we'll keep the synchronous versions
|
||||
// around as well.
|
||||
|
||||
// =============================================================================
|
||||
// renderContent
|
||||
// =============================================================================
|
||||
// Render HTML in a DOM element, add dependencies, and bind Shiny
|
||||
// inputs/outputs. `content` can be null, a string, or an object with
|
||||
// properties 'html' and 'deps'.
|
||||
async function renderContentAsync(
|
||||
el: BindScope,
|
||||
content: string | { html: string; deps?: HtmlDep[] } | null,
|
||||
where: WherePosition = "replace"
|
||||
): Promise<void> {
|
||||
if (where === "replace") {
|
||||
shinyUnbindAll(el);
|
||||
}
|
||||
|
||||
let html = "";
|
||||
let dependencies: HtmlDep[] = [];
|
||||
|
||||
if (content === null) {
|
||||
html = "";
|
||||
} else if (typeof content === "string") {
|
||||
html = content;
|
||||
} else if (typeof content === "object") {
|
||||
html = content.html;
|
||||
dependencies = content.deps || [];
|
||||
}
|
||||
|
||||
await renderHtmlAsync(html, el, dependencies, where);
|
||||
|
||||
let scope: BindScope = el;
|
||||
|
||||
if (where === "replace") {
|
||||
shinyInitializeInputs(el);
|
||||
shinyBindAll(el);
|
||||
} else {
|
||||
const $parent = $(el).parent();
|
||||
|
||||
if ($parent.length > 0) {
|
||||
scope = $parent;
|
||||
if (where === "beforeBegin" || where === "afterEnd") {
|
||||
const $grandparent = $parent.parent();
|
||||
|
||||
if ($grandparent.length > 0) scope = $grandparent;
|
||||
}
|
||||
}
|
||||
shinyInitializeInputs(scope);
|
||||
shinyBindAll(scope);
|
||||
function renderDependencies(dependencies: HtmlDep[] | null): void {
|
||||
if (dependencies) {
|
||||
dependencies.forEach(renderDependency);
|
||||
}
|
||||
}
|
||||
|
||||
// Render HTML in a DOM element, add dependencies, and bind Shiny
|
||||
// inputs/outputs. `content` can be null, a string, or an object with
|
||||
// properties 'html' and 'deps'.
|
||||
function renderContent(
|
||||
el: BindScope,
|
||||
content: string | { html: string; deps?: HtmlDep[] } | null,
|
||||
@@ -123,20 +65,6 @@ function renderContent(
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// renderHtml
|
||||
// =============================================================================
|
||||
// Render HTML in a DOM element, inserting singletons into head as needed
|
||||
async function renderHtmlAsync(
|
||||
html: string,
|
||||
el: BindScope,
|
||||
dependencies: HtmlDep[],
|
||||
where: WherePosition = "replace"
|
||||
): Promise<ReturnType<typeof singletonsRenderHtml>> {
|
||||
await renderDependenciesAsync(dependencies);
|
||||
return singletonsRenderHtml(html, el, where);
|
||||
}
|
||||
|
||||
// Render HTML in a DOM element, inserting singletons into head as needed
|
||||
function renderHtml(
|
||||
html: string,
|
||||
@@ -148,30 +76,6 @@ function renderHtml(
|
||||
return singletonsRenderHtml(html, el, where);
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// renderDependencies
|
||||
// =============================================================================
|
||||
async function renderDependenciesAsync(
|
||||
dependencies: HtmlDep[] | null
|
||||
): Promise<void> {
|
||||
if (dependencies) {
|
||||
for (const dep of dependencies) {
|
||||
await renderDependencyAsync(dep);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function renderDependencies(dependencies: HtmlDep[] | null): void {
|
||||
if (dependencies) {
|
||||
for (const dep of dependencies) {
|
||||
renderDependency(dep);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// HTML dependency types
|
||||
// =============================================================================
|
||||
type HtmlDepVersion = string;
|
||||
|
||||
type MetaItem = {
|
||||
@@ -222,10 +126,6 @@ type HtmlDepNormalized = {
|
||||
attachment: AttachmentItem[];
|
||||
head?: string;
|
||||
};
|
||||
|
||||
// =============================================================================
|
||||
// renderDependency helper functions
|
||||
// =============================================================================
|
||||
const htmlDependencies: { [key: string]: HtmlDepVersion } = {};
|
||||
|
||||
function registerDependency(name: string, version: HtmlDepVersion): void {
|
||||
@@ -247,6 +147,93 @@ function needsRestyle(dep: HtmlDepNormalized) {
|
||||
return htmlDependencies[names[idx]] === dep.version;
|
||||
}
|
||||
|
||||
// Client-side dependency resolution and rendering
|
||||
function renderDependency(dep_: HtmlDep) {
|
||||
const dep = normalizeHtmlDependency(dep_);
|
||||
|
||||
// Convert stylesheet objs to links early, because if `restyle` is true, we'll
|
||||
// pass them through to `addStylesheetsAndRestyle` below.
|
||||
const stylesheetLinks = dep.stylesheet.map((x) => {
|
||||
// Add "rel" and "type" fields if not already present.
|
||||
if (!hasDefinedProperty(x, "rel")) x.rel = "stylesheet";
|
||||
if (!hasDefinedProperty(x, "type")) x.type = "text/css";
|
||||
|
||||
const link = document.createElement("link");
|
||||
|
||||
Object.entries(x).forEach(function ([attr, val]: [
|
||||
string,
|
||||
string | undefined
|
||||
]) {
|
||||
if (attr === "href") {
|
||||
val = encodeURI(val as string);
|
||||
}
|
||||
// If val isn't truthy (e.g., null), consider it a boolean attribute
|
||||
link.setAttribute(attr, val ? val : "");
|
||||
});
|
||||
|
||||
return link;
|
||||
});
|
||||
|
||||
// If a restyle is needed, do that stuff and return. Note that other items
|
||||
// (like scripts) aren't added, because they would have been added in a
|
||||
// previous run.
|
||||
if (needsRestyle(dep)) {
|
||||
addStylesheetsAndRestyle(stylesheetLinks);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (hasDefinedProperty(htmlDependencies, dep.name)) return false;
|
||||
|
||||
registerDependency(dep.name, dep.version);
|
||||
|
||||
const $head = $("head").first();
|
||||
|
||||
// Add each type of element to the DOM.
|
||||
|
||||
dep.meta.forEach((x) => {
|
||||
const meta = document.createElement("meta");
|
||||
|
||||
for (const [attr, val] of Object.entries(x)) {
|
||||
meta.setAttribute(attr, val);
|
||||
}
|
||||
$head.append(meta);
|
||||
});
|
||||
|
||||
if (stylesheetLinks.length !== 0) {
|
||||
$head.append(stylesheetLinks);
|
||||
}
|
||||
|
||||
dep.script.forEach((x) => {
|
||||
const script = document.createElement("script");
|
||||
|
||||
Object.entries(x).forEach(function ([attr, val]) {
|
||||
if (attr === "src") {
|
||||
val = encodeURI(val);
|
||||
}
|
||||
// If val isn't truthy (e.g., null), consider it a boolean attribute
|
||||
script.setAttribute(attr, val ? val : "");
|
||||
});
|
||||
|
||||
$head.append(script);
|
||||
});
|
||||
|
||||
dep.attachment.forEach((x) => {
|
||||
const link = $("<link rel='attachment'>")
|
||||
.attr("id", dep.name + "-" + x.key + "-attachment")
|
||||
.attr("href", encodeURI(x.href));
|
||||
|
||||
$head.append(link);
|
||||
});
|
||||
|
||||
if (dep.head) {
|
||||
const $newHead = $("<head></head>");
|
||||
|
||||
$newHead.html(dep.head);
|
||||
$head.append($newHead.children());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function addStylesheetsAndRestyle(links: HTMLLinkElement[]): void {
|
||||
const $head = $("head").first();
|
||||
|
||||
@@ -367,206 +354,6 @@ function addStylesheetsAndRestyle(links: HTMLLinkElement[]): void {
|
||||
});
|
||||
}
|
||||
|
||||
function getStylesheetLinkTags(dep: HtmlDepNormalized): HTMLLinkElement[] {
|
||||
// Convert stylesheet objs to links early, because if `restyle` is true, we'll
|
||||
// pass them through to `addStylesheetsAndRestyle` below.
|
||||
return dep.stylesheet.map((x) => {
|
||||
// Add "rel" and "type" fields if not already present.
|
||||
if (!hasDefinedProperty(x, "rel")) x.rel = "stylesheet";
|
||||
if (!hasDefinedProperty(x, "type")) x.type = "text/css";
|
||||
|
||||
const link = document.createElement("link");
|
||||
|
||||
Object.entries(x).forEach(function ([attr, val]: [
|
||||
string,
|
||||
string | undefined
|
||||
]) {
|
||||
if (attr === "href") {
|
||||
val = encodeURI(val as string);
|
||||
}
|
||||
// If val isn't truthy (e.g., null), consider it a boolean attribute
|
||||
link.setAttribute(attr, val ? val : "");
|
||||
});
|
||||
|
||||
return link;
|
||||
});
|
||||
}
|
||||
|
||||
function appendStylesheetLinkTags(
|
||||
dep: HtmlDepNormalized,
|
||||
$head: JQuery<HTMLElement>
|
||||
): void {
|
||||
const stylesheetLinks = getStylesheetLinkTags(dep);
|
||||
|
||||
if (stylesheetLinks.length !== 0) {
|
||||
$head.append(stylesheetLinks);
|
||||
}
|
||||
}
|
||||
|
||||
function appendScriptTags(dep: HtmlDepNormalized, $head: JQuery<HTMLElement>) {
|
||||
dep.script.forEach((x) => {
|
||||
const script = document.createElement("script");
|
||||
|
||||
Object.entries(x).forEach(function ([attr, val]) {
|
||||
if (attr === "src") {
|
||||
val = encodeURI(val);
|
||||
}
|
||||
// If val isn't truthy (e.g., null), consider it a boolean attribute
|
||||
script.setAttribute(attr, val ? val : "");
|
||||
});
|
||||
|
||||
$head.append(script);
|
||||
});
|
||||
}
|
||||
|
||||
async function appendScriptTagsAsync(dep: HtmlDepNormalized): Promise<void> {
|
||||
const scriptPromises: Array<Promise<any>> = [];
|
||||
|
||||
dep.script.forEach((x) => {
|
||||
const script = document.createElement("script");
|
||||
|
||||
if (!hasDefinedProperty(x, "async")) {
|
||||
// Set async to false by default, so that if there are multiple script
|
||||
// tags, they are guaranteed to run in order. For dynamically added
|
||||
// <script> tags, browsers set async to true by default, which differs
|
||||
// from static <script> tags in the html, which default to false.
|
||||
//
|
||||
// Refs:
|
||||
// https://stackoverflow.com/a/8996894/412655
|
||||
// https://jason-ge.medium.com/dynamically-load-javascript-files-in-order-5318ac6bcc61
|
||||
//
|
||||
// Note that one odd thing about these dynamically-created <script> tags
|
||||
// is that even though the JS object's `x.script` property is true, it
|
||||
// does NOT show up as a property on the <script> element.
|
||||
script.async = false;
|
||||
}
|
||||
|
||||
Object.entries(x).forEach(function ([attr, val]) {
|
||||
if (attr === "src") {
|
||||
val = encodeURI(val);
|
||||
}
|
||||
// If val isn't truthy (e.g., null), consider it a boolean attribute
|
||||
script.setAttribute(attr, val ? val : "");
|
||||
});
|
||||
|
||||
const p = new Promise((resolve, reject) => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
script.onload = (e: Event) => {
|
||||
resolve(null);
|
||||
};
|
||||
script.onerror = (e: Event | string) => {
|
||||
reject(e);
|
||||
};
|
||||
});
|
||||
|
||||
scriptPromises.push(p);
|
||||
document.head.append(script);
|
||||
});
|
||||
|
||||
await Promise.allSettled(scriptPromises);
|
||||
}
|
||||
|
||||
function appendMetaTags(
|
||||
dep: HtmlDepNormalized,
|
||||
$head: JQuery<HTMLElement>
|
||||
): void {
|
||||
dep.meta.forEach((x) => {
|
||||
const meta = document.createElement("meta");
|
||||
|
||||
for (const [attr, val] of Object.entries(x)) {
|
||||
meta.setAttribute(attr, val);
|
||||
}
|
||||
$head.append(meta);
|
||||
});
|
||||
}
|
||||
|
||||
function appendAttachmentLinkTags(
|
||||
dep: HtmlDepNormalized,
|
||||
$head: JQuery<HTMLElement>
|
||||
): void {
|
||||
dep.attachment.forEach((x) => {
|
||||
const link = $("<link rel='attachment'>")
|
||||
.attr("id", dep.name + "-" + x.key + "-attachment")
|
||||
.attr("href", encodeURI(x.href));
|
||||
|
||||
$head.append(link);
|
||||
});
|
||||
}
|
||||
|
||||
function appendExtraHeadContent(
|
||||
dep: HtmlDepNormalized,
|
||||
$head: JQuery<HTMLElement>
|
||||
): void {
|
||||
if (dep.head) {
|
||||
const $newHead = $("<head></head>");
|
||||
|
||||
$newHead.html(dep.head);
|
||||
$head.append($newHead.children());
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// renderDependency
|
||||
// =============================================================================
|
||||
// Client-side dependency resolution and rendering
|
||||
async function renderDependencyAsync(dep_: HtmlDep): Promise<boolean> {
|
||||
const dep = normalizeHtmlDependency(dep_);
|
||||
|
||||
// If a restyle is needed, do that stuff and return. Note that other items
|
||||
// (like scripts) aren't added, because they would have been added in a
|
||||
// previous run.
|
||||
if (needsRestyle(dep)) {
|
||||
addStylesheetsAndRestyle(getStylesheetLinkTags(dep));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (hasDefinedProperty(htmlDependencies, dep.name)) return false;
|
||||
|
||||
registerDependency(dep.name, dep.version);
|
||||
|
||||
const $head = $("head").first();
|
||||
|
||||
// Add each type of element to the DOM.
|
||||
appendMetaTags(dep, $head);
|
||||
appendStylesheetLinkTags(dep, $head);
|
||||
await appendScriptTagsAsync(dep);
|
||||
appendAttachmentLinkTags(dep, $head);
|
||||
appendExtraHeadContent(dep, $head);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Old-school synchronous version of renderDependencyAsync. This function is
|
||||
// here to preserve compatibility with outside packages that use it. The
|
||||
// implementation is the same except that it calls appendScriptTags() instead of
|
||||
// appendScriptTagsAsync().
|
||||
function renderDependency(dep_: HtmlDep): boolean {
|
||||
const dep = normalizeHtmlDependency(dep_);
|
||||
|
||||
// If a restyle is needed, do that stuff and return. Note that other items
|
||||
// (like scripts) aren't added, because they would have been added in a
|
||||
// previous run.
|
||||
if (needsRestyle(dep)) {
|
||||
addStylesheetsAndRestyle(getStylesheetLinkTags(dep));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (hasDefinedProperty(htmlDependencies, dep.name)) return false;
|
||||
|
||||
registerDependency(dep.name, dep.version);
|
||||
|
||||
const $head = $("head").first();
|
||||
|
||||
// Add each type of element to the DOM.
|
||||
appendMetaTags(dep, $head);
|
||||
appendStylesheetLinkTags(dep, $head);
|
||||
appendScriptTags(dep, $head);
|
||||
appendAttachmentLinkTags(dep, $head);
|
||||
appendExtraHeadContent(dep, $head);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Convert legacy HtmlDependency to new HTMLDependency format. This is
|
||||
// idempotent; new HTMLDependency objects are returned unchanged.
|
||||
function normalizeHtmlDependency(dep: HtmlDep): HtmlDepNormalized {
|
||||
@@ -683,13 +470,5 @@ function normalizeHtmlDependency(dep: HtmlDep): HtmlDepNormalized {
|
||||
return result;
|
||||
}
|
||||
|
||||
export {
|
||||
renderContentAsync,
|
||||
renderContent,
|
||||
renderHtmlAsync,
|
||||
renderHtml,
|
||||
renderDependenciesAsync,
|
||||
renderDependencies,
|
||||
registerDependency,
|
||||
};
|
||||
export { renderDependencies, renderContent, renderHtml, registerDependency };
|
||||
export type { HtmlDep };
|
||||
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
import { isQt } from "../utils/browser";
|
||||
import { showNotification, removeNotification } from "./notifications";
|
||||
import { showModal, removeModal } from "./modal";
|
||||
import { renderContentAsync, renderHtmlAsync } from "./render";
|
||||
import { renderContent, renderHtml } from "./render";
|
||||
import type { HtmlDep } from "./render";
|
||||
import { hideReconnectDialog, showReconnectDialog } from "./reconnectDialog";
|
||||
import { resetBrush } from "../imageutils/resetBrush";
|
||||
@@ -25,10 +25,9 @@ import type { InputBinding } from "../bindings";
|
||||
import { indirectEval } from "../utils/eval";
|
||||
import type { WherePosition } from "./singletons";
|
||||
import type { UploadInitValue, UploadEndValue } from "../file/fileProcessor";
|
||||
import { AsyncQueue } from "../utils/asyncQueue";
|
||||
|
||||
type ResponseValue = UploadEndValue | UploadInitValue;
|
||||
type Handler = (message: any) => Promise<void> | void;
|
||||
type Handler = (message: any) => void;
|
||||
|
||||
type ShinyWebSocket = WebSocket & {
|
||||
allowReconnect?: boolean;
|
||||
@@ -109,13 +108,6 @@ function addCustomMessageHandler(type: string, handler: Handler): void {
|
||||
class ShinyApp {
|
||||
$socket: ShinyWebSocket | null = null;
|
||||
|
||||
// An asynchronous queue of functions. This is sort of like an event loop for
|
||||
// Shiny, to allow scheduling async callbacks so that they can run in order
|
||||
// without overlapping. This is used for handling incoming messages from the
|
||||
// server and scheduling outgoing messages to the server, and can be used for
|
||||
// other things tasks as well.
|
||||
actionQueue = new AsyncQueue<() => Promise<void> | void>();
|
||||
|
||||
config: {
|
||||
workerId: string;
|
||||
sessionId: string;
|
||||
@@ -236,11 +228,9 @@ class ShinyApp {
|
||||
|
||||
socket.send(msg as string);
|
||||
}
|
||||
|
||||
this.startActionQueueLoop();
|
||||
};
|
||||
socket.onmessage = (e) => {
|
||||
this.actionQueue.enqueue(async () => await this.dispatchMessage(e.data));
|
||||
this.dispatchMessage(e.data);
|
||||
};
|
||||
// Called when a successfully-opened websocket is closed, or when an
|
||||
// attempt to open a connection fails.
|
||||
@@ -263,19 +253,6 @@ class ShinyApp {
|
||||
return socket;
|
||||
}
|
||||
|
||||
async startActionQueueLoop(): Promise<void> {
|
||||
// eslint-disable-next-line no-constant-condition
|
||||
while (true) {
|
||||
const action = await this.actionQueue.dequeue();
|
||||
|
||||
try {
|
||||
await action();
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sendInput(values: InputValues): void {
|
||||
const msg = JSON.stringify({
|
||||
method: "update",
|
||||
@@ -482,7 +459,7 @@ class ShinyApp {
|
||||
}
|
||||
}
|
||||
|
||||
async receiveOutput<T>(name: string, value: T): Promise<T | undefined> {
|
||||
receiveOutput<T>(name: string, value: T): T | undefined {
|
||||
const binding = this.$bindings[name];
|
||||
const evt: ShinyEventValue = $.Event("shiny:value");
|
||||
|
||||
@@ -501,7 +478,7 @@ class ShinyApp {
|
||||
$(binding ? binding.el : document).trigger(evt);
|
||||
|
||||
if (!evt.isDefaultPrevented() && binding) {
|
||||
await binding.onValueChange(evt.value);
|
||||
binding.onValueChange(evt.value);
|
||||
}
|
||||
|
||||
return value;
|
||||
@@ -621,7 +598,7 @@ class ShinyApp {
|
||||
// // Added in shiny init method
|
||||
// Shiny.addCustomMessageHandler = addCustomMessageHandler;
|
||||
|
||||
async dispatchMessage(data: ArrayBufferLike | string): Promise<void> {
|
||||
dispatchMessage(data: ArrayBufferLike | string): void {
|
||||
let msgObj: ShinyEventMessage["message"] = {};
|
||||
|
||||
if (typeof data === "string") {
|
||||
@@ -650,7 +627,7 @@ class ShinyApp {
|
||||
if (evt.isDefaultPrevented()) return;
|
||||
|
||||
// Send msgObj.foo and msgObj.bar to appropriate handlers
|
||||
await this._sendMessagesToHandlers(
|
||||
this._sendMessagesToHandlers(
|
||||
evt.message,
|
||||
messageHandlers,
|
||||
messageHandlerOrder
|
||||
@@ -663,11 +640,11 @@ class ShinyApp {
|
||||
|
||||
// A function for sending messages to the appropriate handlers.
|
||||
// - msgObj: the object containing messages, with format {msgObj.foo, msObj.bar
|
||||
private async _sendMessagesToHandlers(
|
||||
private _sendMessagesToHandlers(
|
||||
msgObj: { [key: string]: unknown },
|
||||
handlers: { [key: string]: Handler },
|
||||
handlerOrder: string[]
|
||||
): Promise<void> {
|
||||
): void {
|
||||
// Dispatch messages to handlers, if handler is present
|
||||
for (let i = 0; i < handlerOrder.length; i++) {
|
||||
const msgType = handlerOrder[i];
|
||||
@@ -675,7 +652,7 @@ class ShinyApp {
|
||||
if (hasOwnProperty(msgObj, msgType)) {
|
||||
// Execute each handler with 'this' referring to the present value of
|
||||
// 'this'
|
||||
await handlers[msgType].call(this, msgObj[msgType]);
|
||||
handlers[msgType].call(this, msgObj[msgType]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -685,7 +662,7 @@ class ShinyApp {
|
||||
// * Use arrow functions to allow the Types to propagate.
|
||||
// * However, `_sendMessagesToHandlers()` will adjust the `this` context to the same _`this`_.
|
||||
|
||||
addMessageHandler("values", async (message: { [key: string]: any }) => {
|
||||
addMessageHandler("values", (message: { [key: string]: any }) => {
|
||||
for (const name in this.$bindings) {
|
||||
if (hasOwnProperty(this.$bindings, name))
|
||||
this.$bindings[name].showProgress(false);
|
||||
@@ -693,14 +670,17 @@ class ShinyApp {
|
||||
|
||||
for (const key in message) {
|
||||
if (hasOwnProperty(message, key)) {
|
||||
await this.receiveOutput(key, message[key]);
|
||||
this.receiveOutput(key, message[key]);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
addMessageHandler(
|
||||
"errors",
|
||||
(message: { [key: string]: ErrorsMessageValue }) => {
|
||||
function (
|
||||
this: ShinyApp,
|
||||
message: { [key: string]: ErrorsMessageValue }
|
||||
) {
|
||||
for (const key in message) {
|
||||
if (hasOwnProperty(message, key))
|
||||
this.receiveError(key, message[key]);
|
||||
@@ -745,10 +725,13 @@ class ShinyApp {
|
||||
|
||||
addMessageHandler(
|
||||
"progress",
|
||||
async (message: { type: string; message: { id: string } }) => {
|
||||
function (
|
||||
this: ShinyApp,
|
||||
message: { type: string; message: { id: string } }
|
||||
) {
|
||||
if (message.type && message.message) {
|
||||
// @ts-expect-error; Unknown values handled with followup if statement
|
||||
const handler = await this.progressHandlers[message.type];
|
||||
const handler = this.progressHandlers[message.type];
|
||||
|
||||
if (handler) handler.call(this, message.message);
|
||||
}
|
||||
@@ -757,13 +740,13 @@ class ShinyApp {
|
||||
|
||||
addMessageHandler(
|
||||
"notification",
|
||||
async (
|
||||
(
|
||||
message:
|
||||
| { type: "remove"; message: string }
|
||||
| { type: "show"; message: Parameters<typeof showNotification>[0] }
|
||||
| { type: void }
|
||||
) => {
|
||||
if (message.type === "show") await showNotification(message.message);
|
||||
if (message.type === "show") showNotification(message.message);
|
||||
else if (message.type === "remove") removeNotification(message.message);
|
||||
else throw "Unkown notification type: " + message.type;
|
||||
}
|
||||
@@ -771,13 +754,13 @@ class ShinyApp {
|
||||
|
||||
addMessageHandler(
|
||||
"modal",
|
||||
async (
|
||||
(
|
||||
message:
|
||||
| { type: "remove"; message: string }
|
||||
| { type: "show"; message: Parameters<typeof showModal>[0] }
|
||||
| { type: void }
|
||||
) => {
|
||||
if (message.type === "show") await showModal(message.message);
|
||||
if (message.type === "show") showModal(message.message);
|
||||
// For 'remove', message content isn't used
|
||||
else if (message.type === "remove") removeModal();
|
||||
else throw "Unkown modal type: " + message.type;
|
||||
@@ -877,12 +860,12 @@ class ShinyApp {
|
||||
|
||||
addMessageHandler(
|
||||
"shiny-insert-ui",
|
||||
async (message: {
|
||||
(message: {
|
||||
selector: string;
|
||||
content: { html: string; deps: HtmlDep[] };
|
||||
multiple: boolean;
|
||||
multiple: false | void;
|
||||
where: WherePosition;
|
||||
}): Promise<void> => {
|
||||
}) => {
|
||||
const targets = $(message.selector);
|
||||
|
||||
if (targets.length === 0) {
|
||||
@@ -894,24 +877,19 @@ class ShinyApp {
|
||||
message.selector +
|
||||
'") could not be found in the DOM.'
|
||||
);
|
||||
await renderHtmlAsync(
|
||||
message.content.html,
|
||||
$([]),
|
||||
message.content.deps
|
||||
);
|
||||
renderHtml(message.content.html, $([]), message.content.deps);
|
||||
} else {
|
||||
for (const target of targets) {
|
||||
await renderContentAsync(target, message.content, message.where);
|
||||
// If multiple is false, only render to the first target.
|
||||
if (message.multiple === false) break;
|
||||
}
|
||||
targets.each(function (i, target) {
|
||||
renderContent(target, message.content, message.where);
|
||||
return message.multiple;
|
||||
});
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
addMessageHandler(
|
||||
"shiny-remove-ui",
|
||||
(message: { selector: string; multiple: boolean }) => {
|
||||
(message: { selector: string; multiple: false | void }) => {
|
||||
const els = $(message.selector);
|
||||
|
||||
els.each(function (i, el) {
|
||||
@@ -919,8 +897,8 @@ class ShinyApp {
|
||||
$(el).remove();
|
||||
// If `multiple` is false, returning false terminates the function
|
||||
// and no other elements are removed; if `multiple` is true,
|
||||
// returning nothing continues removing all remaining elements.
|
||||
return message.multiple === false ? false : undefined;
|
||||
// returning true continues removing all remaining elements.
|
||||
return message.multiple;
|
||||
});
|
||||
}
|
||||
);
|
||||
@@ -1000,7 +978,7 @@ class ShinyApp {
|
||||
|
||||
addMessageHandler(
|
||||
"shiny-insert-tab",
|
||||
async (message: {
|
||||
(message: {
|
||||
inputId: string;
|
||||
divTag: { html: string; deps: HtmlDep[] };
|
||||
liTag: { html: string; deps: HtmlDep[] };
|
||||
@@ -1008,7 +986,7 @@ class ShinyApp {
|
||||
position: "after" | "before" | void;
|
||||
select: boolean;
|
||||
menuName: string;
|
||||
}): Promise<void> => {
|
||||
}) => {
|
||||
const $parentTabset = getTabset(message.inputId);
|
||||
let $tabset = $parentTabset;
|
||||
const $tabContent = getTabContent($tabset);
|
||||
@@ -1082,7 +1060,7 @@ class ShinyApp {
|
||||
}
|
||||
}
|
||||
|
||||
await renderContentAsync($liTag[0], {
|
||||
renderContent($liTag[0], {
|
||||
html: $liTag.html(),
|
||||
deps: message.liTag.deps,
|
||||
});
|
||||
@@ -1117,13 +1095,13 @@ class ShinyApp {
|
||||
// lower-level functions that renderContent uses. Like if we pre-process
|
||||
// the value of message.divTag.html for singletons, we could do that, then
|
||||
// render dependencies, then do $tabContent.append($divTag).
|
||||
await renderContentAsync(
|
||||
renderContent(
|
||||
$tabContent[0],
|
||||
{ html: "", deps: message.divTag.deps },
|
||||
// @ts-expect-error; TODO-barret; There is no usage of beforeend
|
||||
"beforeend"
|
||||
);
|
||||
for (const el of $divTag.get()) {
|
||||
$divTag.get().forEach((el) => {
|
||||
// Must not use jQuery for appending el to the doc, we don't want any
|
||||
// scripts to run (since they will run when renderContent takes a crack).
|
||||
$tabContent[0].appendChild(el);
|
||||
@@ -1132,8 +1110,8 @@ class ShinyApp {
|
||||
// and not the whole tag. That's fine in this case because we control the
|
||||
// R code that generates this HTML, and we know that the element is not
|
||||
// a script tag.
|
||||
await renderContentAsync(el, el.innerHTML || el.textContent);
|
||||
}
|
||||
renderContent(el, el.innerHTML || el.textContent);
|
||||
});
|
||||
|
||||
if (message.select) {
|
||||
$liTag.find("a").tab("show");
|
||||
@@ -1401,17 +1379,17 @@ class ShinyApp {
|
||||
},
|
||||
|
||||
// Open a page-level progress bar
|
||||
open: async function (message: {
|
||||
open: function (message: {
|
||||
style: "notification" | "old";
|
||||
id: string;
|
||||
}): Promise<void> {
|
||||
}): void {
|
||||
if (message.style === "notification") {
|
||||
// For new-style (starting in Shiny 0.14) progress indicators that use
|
||||
// the notification API.
|
||||
|
||||
// Progress bar starts hidden; will be made visible if a value is provided
|
||||
// during updates.
|
||||
await showNotification({
|
||||
showNotification({
|
||||
html:
|
||||
`<div id="shiny-progress-${message.id}" class="shiny-progress-notification">` +
|
||||
'<div class="progress active" style="display: none;"><div class="progress-bar"></div></div>' +
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
// Adapted from https://stackoverflow.com/a/47157945/412655
|
||||
|
||||
export class AsyncQueue<T> {
|
||||
private $promises: Array<Promise<T>> = [];
|
||||
private $resolvers: Array<(x: T) => void> = [];
|
||||
|
||||
private _add() {
|
||||
const p: Promise<T> = new Promise((resolve) => {
|
||||
this.$resolvers.push(resolve);
|
||||
});
|
||||
|
||||
this.$promises.push(p);
|
||||
}
|
||||
|
||||
enqueue(x: T): void {
|
||||
if (!this.$resolvers.length) this._add();
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
const resolve = this.$resolvers.shift()!;
|
||||
|
||||
resolve(x);
|
||||
}
|
||||
|
||||
async dequeue(): Promise<T> {
|
||||
if (!this.$promises.length) this._add();
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
const promise = this.$promises.shift()!;
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
isEmpty(): boolean {
|
||||
return !this.$promises.length;
|
||||
}
|
||||
|
||||
isBlocked(): boolean {
|
||||
return !!this.$resolvers.length;
|
||||
}
|
||||
|
||||
get length(): number {
|
||||
return this.$promises.length - this.$resolvers.length;
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
type Cb = {
|
||||
once: boolean;
|
||||
fn: () => void;
|
||||
};
|
||||
|
||||
type Cbs = {
|
||||
[key: string]: Cb;
|
||||
};
|
||||
|
||||
class Callbacks {
|
||||
callbacks: Cbs = {};
|
||||
id = 0;
|
||||
|
||||
register(fn: () => void, once = true): () => void {
|
||||
this.id += 1;
|
||||
const id = this.id;
|
||||
|
||||
this.callbacks[id] = { fn, once };
|
||||
return () => {
|
||||
delete this.callbacks[id];
|
||||
};
|
||||
}
|
||||
|
||||
invoke(): void {
|
||||
for (const id in this.callbacks) {
|
||||
const cb = this.callbacks[id];
|
||||
|
||||
try {
|
||||
cb.fn();
|
||||
} finally {
|
||||
if (cb.once) delete this.callbacks[id];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
clear(): void {
|
||||
this.callbacks = {};
|
||||
}
|
||||
|
||||
count(): number {
|
||||
return Object.keys(this.callbacks).length;
|
||||
}
|
||||
}
|
||||
|
||||
export { Callbacks };
|
||||
@@ -1,5 +1,5 @@
|
||||
import { InputBinding } from "./inputBinding";
|
||||
type ActionButtonReceiveMessageData = {
|
||||
declare type ActionButtonReceiveMessageData = {
|
||||
label?: string;
|
||||
icon?: string | [];
|
||||
};
|
||||
|
||||
6
srcts/types/src/bindings/input/checkbox.d.ts
vendored
6
srcts/types/src/bindings/input/checkbox.d.ts
vendored
@@ -1,7 +1,7 @@
|
||||
import { InputBinding } from "./inputBinding";
|
||||
type CheckedHTMLElement = HTMLInputElement;
|
||||
type CheckboxChecked = CheckedHTMLElement["checked"];
|
||||
type CheckboxReceiveMessageData = {
|
||||
declare type CheckedHTMLElement = HTMLInputElement;
|
||||
declare type CheckboxChecked = CheckedHTMLElement["checked"];
|
||||
declare type CheckboxReceiveMessageData = {
|
||||
value?: CheckboxChecked;
|
||||
label?: string;
|
||||
};
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import { InputBinding } from "./inputBinding";
|
||||
import type { CheckedHTMLElement } from "./checkbox";
|
||||
type CheckboxGroupHTMLElement = CheckedHTMLElement;
|
||||
type ValueLabelObject = {
|
||||
declare type CheckboxGroupHTMLElement = CheckedHTMLElement;
|
||||
declare type ValueLabelObject = {
|
||||
value: HTMLInputElement["value"];
|
||||
label: string;
|
||||
};
|
||||
type CheckboxGroupReceiveMessageData = {
|
||||
declare type CheckboxGroupReceiveMessageData = {
|
||||
options?: string;
|
||||
value?: Parameters<CheckboxGroupInputBinding["setValue"]>[1];
|
||||
label: string;
|
||||
};
|
||||
type CheckboxGroupValue = CheckboxGroupHTMLElement["value"];
|
||||
declare type CheckboxGroupValue = CheckboxGroupHTMLElement["value"];
|
||||
declare class CheckboxGroupInputBinding extends InputBinding {
|
||||
find(scope: HTMLElement): JQuery<HTMLElement>;
|
||||
getValue(el: CheckboxGroupHTMLElement): CheckboxGroupValue[];
|
||||
|
||||
2
srcts/types/src/bindings/input/date.d.ts
vendored
2
srcts/types/src/bindings/input/date.d.ts
vendored
@@ -9,7 +9,7 @@ declare global {
|
||||
bsDatepicker(methodName: string, params: Date | null): void;
|
||||
}
|
||||
}
|
||||
type DateReceiveMessageData = {
|
||||
declare type DateReceiveMessageData = {
|
||||
label: string;
|
||||
min?: Date | null;
|
||||
max?: Date | null;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { formatDateUTC } from "../../utils";
|
||||
import { DateInputBindingBase } from "./date";
|
||||
type DateRangeReceiveMessageData = {
|
||||
declare type DateRangeReceiveMessageData = {
|
||||
label: string;
|
||||
min?: Date;
|
||||
max?: Date;
|
||||
|
||||
2
srcts/types/src/bindings/input/index.d.ts
vendored
2
srcts/types/src/bindings/input/index.d.ts
vendored
@@ -1,7 +1,7 @@
|
||||
import { BindingRegistry } from "../registry";
|
||||
import { InputBinding } from "./inputBinding";
|
||||
import { FileInputBinding } from "./fileinput";
|
||||
type InitInputBindings = {
|
||||
declare type InitInputBindings = {
|
||||
inputBindings: BindingRegistry<InputBinding>;
|
||||
fileInputBinding: FileInputBinding;
|
||||
};
|
||||
|
||||
4
srcts/types/src/bindings/input/number.d.ts
vendored
4
srcts/types/src/bindings/input/number.d.ts
vendored
@@ -1,6 +1,6 @@
|
||||
import { TextInputBindingBase } from "./text";
|
||||
type NumberHTMLElement = HTMLInputElement;
|
||||
type NumberReceiveMessageData = {
|
||||
declare type NumberHTMLElement = HTMLInputElement;
|
||||
declare type NumberReceiveMessageData = {
|
||||
label: string;
|
||||
value?: string | null;
|
||||
min?: string | null;
|
||||
|
||||
6
srcts/types/src/bindings/input/radio.d.ts
vendored
6
srcts/types/src/bindings/input/radio.d.ts
vendored
@@ -1,10 +1,10 @@
|
||||
import { InputBinding } from "./inputBinding";
|
||||
type RadioHTMLElement = HTMLInputElement;
|
||||
type ValueLabelObject = {
|
||||
declare type RadioHTMLElement = HTMLInputElement;
|
||||
declare type ValueLabelObject = {
|
||||
value: HTMLInputElement["value"];
|
||||
label: string;
|
||||
};
|
||||
type RadioReceiveMessageData = {
|
||||
declare type RadioReceiveMessageData = {
|
||||
value?: string | [];
|
||||
options?: ValueLabelObject[];
|
||||
label: string;
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
/// <reference types="selectize" />
|
||||
import { InputBinding } from "./inputBinding";
|
||||
import type { NotUndefined } from "../../utils/extraTypes";
|
||||
type SelectHTMLElement = HTMLSelectElement & {
|
||||
declare type SelectHTMLElement = HTMLSelectElement & {
|
||||
nonempty: boolean;
|
||||
};
|
||||
type SelectInputReceiveMessageData = {
|
||||
declare type SelectInputReceiveMessageData = {
|
||||
label: string;
|
||||
options?: string;
|
||||
config?: string;
|
||||
url?: string;
|
||||
value?: string;
|
||||
};
|
||||
type SelectizeOptions = Selectize.IOptions<string, unknown>;
|
||||
type SelectizeInfo = Selectize.IApi<string, unknown> & {
|
||||
declare type SelectizeOptions = Selectize.IOptions<string, unknown>;
|
||||
declare type SelectizeInfo = Selectize.IApi<string, unknown> & {
|
||||
settings: SelectizeOptions;
|
||||
};
|
||||
declare class SelectInputBinding extends InputBinding {
|
||||
|
||||
4
srcts/types/src/bindings/input/slider.d.ts
vendored
4
srcts/types/src/bindings/input/slider.d.ts
vendored
@@ -1,7 +1,7 @@
|
||||
import type { TextHTMLElement } from "./text";
|
||||
import { TextInputBindingBase } from "./text";
|
||||
type TimeFormatter = (fmt: string, dt: Date) => string;
|
||||
type SliderReceiveMessageData = {
|
||||
declare type TimeFormatter = (fmt: string, dt: Date) => string;
|
||||
declare type SliderReceiveMessageData = {
|
||||
label: string;
|
||||
value?: Array<number | string> | number | string;
|
||||
min?: number;
|
||||
|
||||
2
srcts/types/src/bindings/input/tabinput.d.ts
vendored
2
srcts/types/src/bindings/input/tabinput.d.ts
vendored
@@ -1,5 +1,5 @@
|
||||
import { InputBinding } from "./inputBinding";
|
||||
type TabInputReceiveMessageData = {
|
||||
declare type TabInputReceiveMessageData = {
|
||||
value?: string;
|
||||
};
|
||||
declare class BootstrapTabInputBinding extends InputBinding {
|
||||
|
||||
4
srcts/types/src/bindings/input/text.d.ts
vendored
4
srcts/types/src/bindings/input/text.d.ts
vendored
@@ -1,6 +1,6 @@
|
||||
import { InputBinding } from "./inputBinding";
|
||||
type TextHTMLElement = HTMLInputElement;
|
||||
type TextReceiveMessageData = {
|
||||
declare type TextHTMLElement = HTMLInputElement;
|
||||
declare type TextReceiveMessageData = {
|
||||
label: string;
|
||||
value?: TextHTMLElement["value"];
|
||||
placeholder?: TextHTMLElement["placeholder"];
|
||||
|
||||
4
srcts/types/src/bindings/output/html.d.ts
vendored
4
srcts/types/src/bindings/output/html.d.ts
vendored
@@ -1,9 +1,9 @@
|
||||
import { OutputBinding } from "./outputBinding";
|
||||
import { renderContentAsync } from "../../shiny/render";
|
||||
import { renderContent } from "../../shiny/render";
|
||||
import type { ErrorsMessageValue } from "../../shiny/shinyapp";
|
||||
declare class HtmlOutputBinding extends OutputBinding {
|
||||
find(scope: HTMLElement): JQuery<HTMLElement>;
|
||||
onValueError(el: HTMLElement, err: ErrorsMessageValue): void;
|
||||
renderValue(el: HTMLElement, data: Parameters<typeof renderContentAsync>[1]): Promise<void>;
|
||||
renderValue(el: HTMLElement, data: Parameters<typeof renderContent>[1]): void;
|
||||
}
|
||||
export { HtmlOutputBinding };
|
||||
|
||||
2
srcts/types/src/bindings/output/index.d.ts
vendored
2
srcts/types/src/bindings/output/index.d.ts
vendored
@@ -1,6 +1,6 @@
|
||||
import { BindingRegistry } from "../registry";
|
||||
import { OutputBinding } from "./outputBinding";
|
||||
type InitOutputBindings = {
|
||||
declare type InitOutputBindings = {
|
||||
outputBindings: BindingRegistry<OutputBinding>;
|
||||
};
|
||||
declare function initOutputBindings(): InitOutputBindings;
|
||||
|
||||
@@ -2,9 +2,9 @@ import type { ErrorsMessageValue } from "../../shiny/shinyapp";
|
||||
declare class OutputBinding {
|
||||
name: string;
|
||||
find(scope: HTMLElement | JQuery<HTMLElement>): JQuery<HTMLElement>;
|
||||
renderValue(el: HTMLElement, data: unknown): Promise<void> | void;
|
||||
renderValue(el: HTMLElement, data: unknown): void;
|
||||
getId(el: HTMLElement): string;
|
||||
onValueChange(el: HTMLElement, data: unknown): Promise<void>;
|
||||
onValueChange(el: HTMLElement, data: unknown): void;
|
||||
onValueError(el: HTMLElement, err: ErrorsMessageValue): void;
|
||||
renderError(el: HTMLElement, err: ErrorsMessageValue): void;
|
||||
clearError(el: HTMLElement): void;
|
||||
|
||||
2
srcts/types/src/bindings/outputAdapter.d.ts
vendored
2
srcts/types/src/bindings/outputAdapter.d.ts
vendored
@@ -8,7 +8,7 @@ declare class OutputBindingAdapter {
|
||||
binding: OutputBinding;
|
||||
constructor(el: HTMLElement, binding: OutpuBindingWithResize);
|
||||
getId(): string;
|
||||
onValueChange(data: unknown): Promise<void>;
|
||||
onValueChange(data: unknown): void;
|
||||
onValueError(err: ErrorsMessageValue): void;
|
||||
showProgress(show: boolean): void;
|
||||
onResize(): void;
|
||||
|
||||
3
srcts/types/src/bindings/registry.d.ts
vendored
3
srcts/types/src/bindings/registry.d.ts
vendored
@@ -1,4 +1,3 @@
|
||||
import { Callbacks } from "../utils/callbacks";
|
||||
interface BindingBase {
|
||||
name: string;
|
||||
}
|
||||
@@ -13,9 +12,7 @@ declare class BindingRegistry<Binding extends BindingBase> {
|
||||
bindingNames: {
|
||||
[key: string]: BindingObj<Binding>;
|
||||
};
|
||||
registerCallbacks: Callbacks;
|
||||
register(binding: Binding, bindingName: string, priority?: number): void;
|
||||
onRegister(fn: () => void, once?: boolean): void;
|
||||
setPriority(bindingName: string, priority: number): void;
|
||||
getPriority(bindingName: string): number | false;
|
||||
getBindings(): Array<BindingObj<Binding>>;
|
||||
|
||||
4
srcts/types/src/events/jQueryEvents.d.ts
vendored
4
srcts/types/src/events/jQueryEvents.d.ts
vendored
@@ -1,7 +1,7 @@
|
||||
import type { JQueryEventHandlerBase } from "bootstrap";
|
||||
import "jquery";
|
||||
type EvtPrefix<T extends string> = `${T}.${string}`;
|
||||
type EvtFn<T extends JQuery.Event> = ((evt: T) => void) | null | undefined;
|
||||
declare type EvtPrefix<T extends string> = `${T}.${string}`;
|
||||
declare type EvtFn<T extends JQuery.Event> = ((evt: T) => void) | null | undefined;
|
||||
declare global {
|
||||
interface JQuery {
|
||||
on(events: EvtPrefix<"change">, handler: EvtFn<JQuery.DragEvent>): this;
|
||||
|
||||
8
srcts/types/src/file/fileProcessor.d.ts
vendored
8
srcts/types/src/file/fileProcessor.d.ts
vendored
@@ -1,11 +1,11 @@
|
||||
import type { ShinyApp } from "../shiny/shinyapp";
|
||||
type JobId = string;
|
||||
type UploadUrl = string;
|
||||
type UploadInitValue = {
|
||||
declare type JobId = string;
|
||||
declare type UploadUrl = string;
|
||||
declare type UploadInitValue = {
|
||||
jobId: JobId;
|
||||
uploadUrl: UploadUrl;
|
||||
};
|
||||
type UploadEndValue = never;
|
||||
declare type UploadEndValue = never;
|
||||
declare class FileProcessor {
|
||||
files: File[];
|
||||
fileIndex: number;
|
||||
|
||||
13
srcts/types/src/imageutils/createBrush.d.ts
vendored
13
srcts/types/src/imageutils/createBrush.d.ts
vendored
@@ -1,15 +1,15 @@
|
||||
import type { Coordmap } from "./initCoordmap";
|
||||
import type { Panel } from "./initPanelScales";
|
||||
import type { Offset } from "./findbox";
|
||||
type Bounds = {
|
||||
declare type Bounds = {
|
||||
xmin: number;
|
||||
xmax: number;
|
||||
ymin: number;
|
||||
ymax: number;
|
||||
};
|
||||
type BoundsCss = Bounds;
|
||||
type BoundsData = Bounds;
|
||||
type ImageState = {
|
||||
declare type BoundsCss = Bounds;
|
||||
declare type BoundsData = Bounds;
|
||||
declare type ImageState = {
|
||||
brushing: boolean;
|
||||
dragging: boolean;
|
||||
resizing: boolean;
|
||||
@@ -26,7 +26,7 @@ type ImageState = {
|
||||
panel: Panel | null;
|
||||
changeStartBounds: Bounds;
|
||||
};
|
||||
type BrushOpts = {
|
||||
declare type BrushOpts = {
|
||||
brushDirection: "x" | "xy" | "y";
|
||||
brushClip: boolean;
|
||||
brushFill: string;
|
||||
@@ -36,9 +36,8 @@ type BrushOpts = {
|
||||
brushDelay?: number;
|
||||
brushResetOnNew?: boolean;
|
||||
};
|
||||
type Brush = {
|
||||
declare type Brush = {
|
||||
reset: () => void;
|
||||
hasOldBrush: () => boolean;
|
||||
importOldBrush: () => void;
|
||||
isInsideBrush: (offsetCss: Offset) => boolean;
|
||||
isInResizeArea: (offsetCss: Offset) => boolean;
|
||||
|
||||
10
srcts/types/src/imageutils/createHandlers.d.ts
vendored
10
srcts/types/src/imageutils/createHandlers.d.ts
vendored
@@ -3,14 +3,14 @@ import type { BoundsCss, Bounds, BrushOpts } from "./createBrush";
|
||||
import type { Offset } from "./findbox";
|
||||
import type { Coordmap } from "./initCoordmap";
|
||||
import type { Panel } from "./initPanelScales";
|
||||
type CreateHandler = {
|
||||
declare type CreateHandler = {
|
||||
mousemove?: (e: JQuery.MouseMoveEvent) => void;
|
||||
mouseout?: (e: JQuery.MouseOutEvent) => void;
|
||||
mousedown?: (e: JQuery.MouseDownEvent) => void;
|
||||
onResetImg: () => void;
|
||||
onResize: ((e: JQuery.ResizeEvent) => void) | null;
|
||||
};
|
||||
type BrushInfo = {
|
||||
declare type BrushInfo = {
|
||||
xmin: number;
|
||||
xmax: number;
|
||||
ymin: number;
|
||||
@@ -28,9 +28,9 @@ type BrushInfo = {
|
||||
brushId?: string;
|
||||
outputId?: string;
|
||||
};
|
||||
type InputId = Parameters<Coordmap["mouseCoordinateSender"]>[0];
|
||||
type Clip = Parameters<Coordmap["mouseCoordinateSender"]>[1];
|
||||
type NullOutside = Parameters<Coordmap["mouseCoordinateSender"]>[2];
|
||||
declare type InputId = Parameters<Coordmap["mouseCoordinateSender"]>[0];
|
||||
declare type Clip = Parameters<Coordmap["mouseCoordinateSender"]>[1];
|
||||
declare type NullOutside = Parameters<Coordmap["mouseCoordinateSender"]>[2];
|
||||
declare function createClickHandler(inputId: InputId, clip: Clip, coordmap: Coordmap): CreateHandler;
|
||||
declare function createHoverHandler(inputId: InputId, delay: number, delayType: string | "throttle", clip: Clip, nullOutside: NullOutside, coordmap: Coordmap): CreateHandler;
|
||||
declare function createBrushHandler(inputId: InputId, $el: JQuery<HTMLElement>, opts: BrushOpts, coordmap: Coordmap, outputId: BrushInfo["outputId"]): CreateHandler;
|
||||
|
||||
2
srcts/types/src/imageutils/findbox.d.ts
vendored
2
srcts/types/src/imageutils/findbox.d.ts
vendored
@@ -1,5 +1,5 @@
|
||||
import type { Bounds } from "./createBrush";
|
||||
type Offset = {
|
||||
declare type Offset = {
|
||||
x: number;
|
||||
y: number;
|
||||
};
|
||||
|
||||
8
srcts/types/src/imageutils/initCoordmap.d.ts
vendored
8
srcts/types/src/imageutils/initCoordmap.d.ts
vendored
@@ -3,13 +3,13 @@ import type { Offset } from "./findbox";
|
||||
import type { Bounds } from "./createBrush";
|
||||
import type { Panel, PanelInit } from "./initPanelScales";
|
||||
declare function findOrigin($el: JQuery<HTMLElement>): Offset;
|
||||
type OffsetCss = {
|
||||
declare type OffsetCss = {
|
||||
[key: string]: number;
|
||||
};
|
||||
type OffsetImg = {
|
||||
declare type OffsetImg = {
|
||||
[key: string]: number;
|
||||
};
|
||||
type CoordmapInit = {
|
||||
declare type CoordmapInit = {
|
||||
panels: PanelInit[];
|
||||
dims: {
|
||||
height: number;
|
||||
@@ -19,7 +19,7 @@ type CoordmapInit = {
|
||||
width: null;
|
||||
};
|
||||
};
|
||||
type Coordmap = {
|
||||
declare type Coordmap = {
|
||||
panels: Panel[];
|
||||
dims: {
|
||||
height: number;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { Offset } from "./findbox";
|
||||
import type { Bounds } from "./createBrush";
|
||||
type PanelInit = {
|
||||
declare type PanelInit = {
|
||||
domain: {
|
||||
top: number;
|
||||
bottom: number;
|
||||
@@ -24,7 +24,7 @@ type PanelInit = {
|
||||
[key: string]: number | string;
|
||||
};
|
||||
};
|
||||
type Panel = PanelInit & {
|
||||
declare type Panel = PanelInit & {
|
||||
scaleDataToImg: {
|
||||
(val: Bounds, clip?: boolean): Bounds;
|
||||
};
|
||||
|
||||
@@ -3,11 +3,11 @@ import type { ShinyApp } from "../shiny/shinyapp";
|
||||
declare class InputBatchSender implements InputPolicy {
|
||||
target: InputPolicy;
|
||||
shinyapp: ShinyApp;
|
||||
timerId: ReturnType<typeof setTimeout> | null;
|
||||
pendingData: {
|
||||
[key: string]: unknown;
|
||||
};
|
||||
reentrant: boolean;
|
||||
sendIsEnqueued: boolean;
|
||||
lastChanceCallback: Array<() => void>;
|
||||
constructor(shinyapp: ShinyApp);
|
||||
setInput(nameType: string, value: unknown, opts: InputPolicyOpts): void;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { InputPolicy, InputPolicyOpts } from "./inputPolicy";
|
||||
type LastSentValues = {
|
||||
declare type LastSentValues = {
|
||||
[key: string]: {
|
||||
[key: string]: string;
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { InputBinding } from "../bindings";
|
||||
type EventPriority = "deferred" | "event" | "immediate";
|
||||
type InputPolicyOpts = {
|
||||
declare type EventPriority = "deferred" | "event" | "immediate";
|
||||
declare type InputPolicyOpts = {
|
||||
priority: EventPriority;
|
||||
el?: HTMLElement;
|
||||
binding?: InputBinding;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { InputPolicy, InputPolicyOpts } from "./inputPolicy";
|
||||
import type { InputRatePolicy } from "./inputRatePolicy";
|
||||
type RatePolicyModes = "debounce" | "direct" | "throttle";
|
||||
declare type RatePolicyModes = "debounce" | "direct" | "throttle";
|
||||
declare class InputRateDecorator implements InputPolicy {
|
||||
target: InputPolicy;
|
||||
inputRatePolicies: {
|
||||
|
||||
4
srcts/types/src/shiny/bind.d.ts
vendored
4
srcts/types/src/shiny/bind.d.ts
vendored
@@ -1,8 +1,8 @@
|
||||
import type { InputBinding, OutputBinding } from "../bindings";
|
||||
import type { BindingRegistry } from "../bindings/registry";
|
||||
import type { InputRateDecorator, InputValidateDecorator } from "../inputPolicies";
|
||||
type BindScope = HTMLElement | JQuery<HTMLElement>;
|
||||
type BindInputsCtx = {
|
||||
declare type BindScope = HTMLElement | JQuery<HTMLElement>;
|
||||
declare type BindInputsCtx = {
|
||||
inputs: InputValidateDecorator;
|
||||
inputsRate: InputRateDecorator;
|
||||
inputBindings: BindingRegistry<InputBinding>;
|
||||
|
||||
5
srcts/types/src/shiny/index.d.ts
vendored
5
srcts/types/src/shiny/index.d.ts
vendored
@@ -4,7 +4,7 @@ import { $escape, compareVersion } from "../utils";
|
||||
import { showNotification, removeNotification } from "./notifications";
|
||||
import { showModal, removeModal } from "./modal";
|
||||
import { showReconnectDialog, hideReconnectDialog } from "./reconnectDialog";
|
||||
import { renderContentAsync, renderContent, renderDependenciesAsync, renderDependencies, renderHtmlAsync, renderHtml } from "./render";
|
||||
import { renderContent, renderDependencies, renderHtml } from "./render";
|
||||
import type { shinyBindAll, shinyForgetLastInputValue, shinySetInputValue, shinyInitializeInputs, shinyUnbindAll } from "./initedMethods";
|
||||
import type { Handler, ShinyApp } from "./shinyapp";
|
||||
import { addCustomMessageHandler } from "./shinyapp";
|
||||
@@ -30,11 +30,8 @@ interface Shiny {
|
||||
createSocket?: () => WebSocket;
|
||||
showReconnectDialog: typeof showReconnectDialog;
|
||||
hideReconnectDialog: typeof hideReconnectDialog;
|
||||
renderDependenciesAsync: typeof renderDependenciesAsync;
|
||||
renderDependencies: typeof renderDependencies;
|
||||
renderContentAsync: typeof renderContentAsync;
|
||||
renderContent: typeof renderContent;
|
||||
renderHtmlAsync: typeof renderHtmlAsync;
|
||||
renderHtml: typeof renderHtml;
|
||||
user: string;
|
||||
progressHandlers?: ShinyApp["progressHandlers"];
|
||||
|
||||
9
srcts/types/src/shiny/modal.d.ts
vendored
9
srcts/types/src/shiny/modal.d.ts
vendored
@@ -1,7 +1,6 @@
|
||||
import type { HtmlDep } from "./render";
|
||||
declare function show({ html, deps, }?: {
|
||||
html?: string;
|
||||
deps?: HtmlDep[];
|
||||
}): Promise<void>;
|
||||
declare function show({ html, deps }?: {
|
||||
html?: string | undefined;
|
||||
deps?: never[] | undefined;
|
||||
}): void;
|
||||
declare function remove(): void;
|
||||
export { show as showModal, remove as removeModal };
|
||||
|
||||
2
srcts/types/src/shiny/notifications.d.ts
vendored
2
srcts/types/src/shiny/notifications.d.ts
vendored
@@ -8,6 +8,6 @@ declare function show({ html, action, deps, duration, id, closeButton, type, }?:
|
||||
id?: string | null;
|
||||
closeButton?: boolean;
|
||||
type?: string | null;
|
||||
}): Promise<ReturnType<typeof randomId>>;
|
||||
}): ReturnType<typeof randomId>;
|
||||
declare function remove(id: string): void;
|
||||
export { show as showNotification, remove as removeNotification };
|
||||
|
||||
22
srcts/types/src/shiny/render.d.ts
vendored
22
srcts/types/src/shiny/render.d.ts
vendored
@@ -1,39 +1,33 @@
|
||||
import type { BindScope } from "./bind";
|
||||
import { renderHtml as singletonsRenderHtml } from "./singletons";
|
||||
import type { WherePosition } from "./singletons";
|
||||
declare function renderContentAsync(el: BindScope, content: string | {
|
||||
html: string;
|
||||
deps?: HtmlDep[];
|
||||
} | null, where?: WherePosition): Promise<void>;
|
||||
declare function renderDependencies(dependencies: HtmlDep[] | null): void;
|
||||
declare function renderContent(el: BindScope, content: string | {
|
||||
html: string;
|
||||
deps?: HtmlDep[];
|
||||
} | null, where?: WherePosition): void;
|
||||
declare function renderHtmlAsync(html: string, el: BindScope, dependencies: HtmlDep[], where?: WherePosition): Promise<ReturnType<typeof singletonsRenderHtml>>;
|
||||
declare function renderHtml(html: string, el: BindScope, dependencies: HtmlDep[], where?: WherePosition): ReturnType<typeof singletonsRenderHtml>;
|
||||
declare function renderDependenciesAsync(dependencies: HtmlDep[] | null): Promise<void>;
|
||||
declare function renderDependencies(dependencies: HtmlDep[] | null): void;
|
||||
type HtmlDepVersion = string;
|
||||
type MetaItem = {
|
||||
declare type HtmlDepVersion = string;
|
||||
declare type MetaItem = {
|
||||
name: string;
|
||||
content: string;
|
||||
[x: string]: string;
|
||||
};
|
||||
type StylesheetItem = {
|
||||
declare type StylesheetItem = {
|
||||
href: string;
|
||||
rel?: string;
|
||||
type?: string;
|
||||
};
|
||||
type ScriptItem = {
|
||||
declare type ScriptItem = {
|
||||
src: string;
|
||||
[x: string]: string;
|
||||
};
|
||||
type AttachmentItem = {
|
||||
declare type AttachmentItem = {
|
||||
key: string;
|
||||
href: string;
|
||||
[x: string]: string;
|
||||
};
|
||||
type HtmlDep = {
|
||||
declare type HtmlDep = {
|
||||
name: string;
|
||||
version: HtmlDepVersion;
|
||||
restyle?: boolean;
|
||||
@@ -51,5 +45,5 @@ type HtmlDep = {
|
||||
head?: string;
|
||||
};
|
||||
declare function registerDependency(name: string, version: HtmlDepVersion): void;
|
||||
export { renderContentAsync, renderContent, renderHtmlAsync, renderHtml, renderDependenciesAsync, renderDependencies, registerDependency, };
|
||||
export { renderDependencies, renderContent, renderHtml, registerDependency };
|
||||
export type { HtmlDep };
|
||||
|
||||
25
srcts/types/src/shiny/shinyapp.d.ts
vendored
25
srcts/types/src/shiny/shinyapp.d.ts
vendored
@@ -1,26 +1,24 @@
|
||||
import type { OutputBindingAdapter } from "../bindings/outputAdapter";
|
||||
import type { UploadInitValue, UploadEndValue } from "../file/fileProcessor";
|
||||
import { AsyncQueue } from "../utils/asyncQueue";
|
||||
type ResponseValue = UploadEndValue | UploadInitValue;
|
||||
type Handler = (message: any) => Promise<void> | void;
|
||||
type ShinyWebSocket = WebSocket & {
|
||||
declare type ResponseValue = UploadEndValue | UploadInitValue;
|
||||
declare type Handler = (message: any) => void;
|
||||
declare type ShinyWebSocket = WebSocket & {
|
||||
allowReconnect?: boolean;
|
||||
};
|
||||
type ErrorsMessageValue = {
|
||||
declare type ErrorsMessageValue = {
|
||||
message: string;
|
||||
call: string[];
|
||||
type?: string[];
|
||||
};
|
||||
type OnSuccessRequest = (value: ResponseValue) => void;
|
||||
type OnErrorRequest = (err: string) => void;
|
||||
type InputValues = {
|
||||
declare type OnSuccessRequest = (value: ResponseValue) => void;
|
||||
declare type OnErrorRequest = (err: string) => void;
|
||||
declare type InputValues = {
|
||||
[key: string]: unknown;
|
||||
};
|
||||
type MessageValue = Parameters<WebSocket["send"]>[0];
|
||||
declare type MessageValue = Parameters<WebSocket["send"]>[0];
|
||||
declare function addCustomMessageHandler(type: string, handler: Handler): void;
|
||||
declare class ShinyApp {
|
||||
$socket: ShinyWebSocket | null;
|
||||
actionQueue: AsyncQueue<() => Promise<void> | void>;
|
||||
config: {
|
||||
workerId: string;
|
||||
sessionId: string;
|
||||
@@ -52,7 +50,6 @@ declare class ShinyApp {
|
||||
private scheduledReconnect;
|
||||
reconnect(): void;
|
||||
createSocket(): ShinyWebSocket;
|
||||
startActionQueueLoop(): Promise<void>;
|
||||
sendInput(values: InputValues): void;
|
||||
$notifyDisconnected(): void;
|
||||
$removeSocket(): void;
|
||||
@@ -66,13 +63,13 @@ declare class ShinyApp {
|
||||
makeRequest(method: string, args: unknown[], onSuccess: OnSuccessRequest, onError: OnErrorRequest, blobs: Array<ArrayBuffer | Blob | string> | undefined): void;
|
||||
$sendMsg(msg: MessageValue): void;
|
||||
receiveError(name: string, error: ErrorsMessageValue): void;
|
||||
receiveOutput<T>(name: string, value: T): Promise<T | undefined>;
|
||||
receiveOutput<T>(name: string, value: T): T | undefined;
|
||||
bindOutput(id: string, binding: OutputBindingAdapter): OutputBindingAdapter;
|
||||
unbindOutput(id: string, binding: OutputBindingAdapter): boolean;
|
||||
private _narrowScopeComponent;
|
||||
private _narrowScope;
|
||||
$updateConditionals(): void;
|
||||
dispatchMessage(data: ArrayBufferLike | string): Promise<void>;
|
||||
dispatchMessage(data: ArrayBufferLike | string): void;
|
||||
private _sendMessagesToHandlers;
|
||||
private _init;
|
||||
progressHandlers: {
|
||||
@@ -82,7 +79,7 @@ declare class ShinyApp {
|
||||
open: (message: {
|
||||
style: "notification" | "old";
|
||||
id: string;
|
||||
}) => Promise<void>;
|
||||
}) => void;
|
||||
update: (message: {
|
||||
style: "notification" | "old";
|
||||
id: string;
|
||||
|
||||
2
srcts/types/src/shiny/singletons.d.ts
vendored
2
srcts/types/src/shiny/singletons.d.ts
vendored
@@ -2,7 +2,7 @@ import type { BindScope } from "./bind";
|
||||
declare const knownSingletons: {
|
||||
[key: string]: boolean;
|
||||
};
|
||||
type WherePosition = "afterBegin" | "afterEnd" | "beforeBegin" | "beforeEnd" | "replace";
|
||||
declare type WherePosition = "afterBegin" | "afterEnd" | "beforeBegin" | "beforeEnd" | "replace";
|
||||
declare function renderHtml(html: string, el: BindScope, where: WherePosition): ReturnType<typeof processHtml>;
|
||||
declare function registerNames(s: string[] | string): void;
|
||||
declare function processHtml(val: string): {
|
||||
|
||||
10
srcts/types/src/utils/asyncQueue.d.ts
vendored
10
srcts/types/src/utils/asyncQueue.d.ts
vendored
@@ -1,10 +0,0 @@
|
||||
export declare class AsyncQueue<T> {
|
||||
private $promises;
|
||||
private $resolvers;
|
||||
private _add;
|
||||
enqueue(x: T): void;
|
||||
dequeue(): Promise<T>;
|
||||
isEmpty(): boolean;
|
||||
isBlocked(): boolean;
|
||||
get length(): number;
|
||||
}
|
||||
16
srcts/types/src/utils/callbacks.d.ts
vendored
16
srcts/types/src/utils/callbacks.d.ts
vendored
@@ -1,16 +0,0 @@
|
||||
type Cb = {
|
||||
once: boolean;
|
||||
fn: () => void;
|
||||
};
|
||||
type Cbs = {
|
||||
[key: string]: Cb;
|
||||
};
|
||||
declare class Callbacks {
|
||||
callbacks: Cbs;
|
||||
id: number;
|
||||
register(fn: () => void, once?: boolean): () => void;
|
||||
invoke(): void;
|
||||
clear(): void;
|
||||
count(): number;
|
||||
}
|
||||
export { Callbacks };
|
||||
10
srcts/types/src/utils/extraTypes.d.ts
vendored
10
srcts/types/src/utils/extraTypes.d.ts
vendored
@@ -1,11 +1,11 @@
|
||||
type AnyFunction = (...args: any[]) => any;
|
||||
type AnyVoidFunction = (...args: any[]) => void;
|
||||
type MapValuesUnion<T> = T[keyof T];
|
||||
type MapWithResult<X, R> = {
|
||||
declare type AnyFunction = (...args: any[]) => any;
|
||||
declare type AnyVoidFunction = (...args: any[]) => void;
|
||||
declare type MapValuesUnion<T> = T[keyof T];
|
||||
declare type MapWithResult<X, R> = {
|
||||
[Property in keyof X]: R;
|
||||
};
|
||||
/**
|
||||
* Exclude undefined from T
|
||||
*/
|
||||
type NotUndefined<T> = T extends undefined ? never : T;
|
||||
declare type NotUndefined<T> = T extends undefined ? never : T;
|
||||
export type { AnyFunction, AnyVoidFunction, MapValuesUnion, MapWithResult, NotUndefined, };
|
||||
|
||||
2
srcts/types/src/utils/userAgent.d.ts
vendored
2
srcts/types/src/utils/userAgent.d.ts
vendored
@@ -1,4 +1,4 @@
|
||||
type UserAgent = typeof window.navigator.userAgent;
|
||||
declare type UserAgent = typeof window.navigator.userAgent;
|
||||
declare let userAgent: UserAgent;
|
||||
declare function setUserAgent(userAgent_: UserAgent): void;
|
||||
export type { UserAgent };
|
||||
|
||||
@@ -142,41 +142,6 @@
|
||||
<div class="content-footer"></div>
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
Code
|
||||
dropdown_active
|
||||
Output
|
||||
<div class="tabbable">
|
||||
<ul class="nav nav-tabs" data-tabsetid="4785">
|
||||
<li>
|
||||
<a href="#tab-4785-1" data-toggle="tab" data-bs-toggle="tab" data-value="A">A</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#tab-4785-2" data-toggle="tab" data-bs-toggle="tab" data-value="B">
|
||||
<i aria-label="github icon" class="fab fa-github fa-fw" role="presentation"></i>
|
||||
B
|
||||
</a>
|
||||
</li>
|
||||
<li class="dropdown active">
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown" data-bs-toggle="dropdown" data-value="Menu">
|
||||
Menu
|
||||
<b class="caret"></b>
|
||||
</a>
|
||||
<ul class="dropdown-menu" data-tabsetid="1502">
|
||||
<li class="active">
|
||||
<a href="#tab-1502-1" data-toggle="tab" data-bs-toggle="tab" data-value="C">C</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="tab-content" data-tabsetid="4785">
|
||||
<div class="tab-pane" data-value="A" id="tab-4785-1">a</div>
|
||||
<div class="tab-pane" data-value="B" data-icon-class="fab fa-github fa-fw" id="tab-4785-2">b</div>
|
||||
<div class="tab-pane active" data-value="C" id="tab-1502-1">c</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
# navbarPage() markup is correct
|
||||
|
||||
Code
|
||||
|
||||
@@ -126,23 +126,6 @@ test_that("ReactiveValues", {
|
||||
expect_error(values$a <- 1)
|
||||
})
|
||||
|
||||
test_that("reactiveValues keys are sorted", {
|
||||
values <- reactiveValues(b=2, a=0)
|
||||
values$C <- 13
|
||||
values$A <- 0
|
||||
values$c <- 3
|
||||
values$B <- 12
|
||||
# Setting an existing value shouldn't change order
|
||||
values$a <- 1
|
||||
values$A <- 11
|
||||
|
||||
expect_identical(isolate(names(values)), c("b", "a", "C", "A", "c", "B"))
|
||||
expect_identical(
|
||||
isolate(reactiveValuesToList(values)),
|
||||
list(b=2, a=1, C=13, A=11, c=3, B=12)
|
||||
)
|
||||
})
|
||||
|
||||
test_that("reactiveValues() has useful print method", {
|
||||
verify_output(test_path("print-reactiveValues.txt"), {
|
||||
x <- reactiveValues(x = 1, y = 2, z = 3)
|
||||
|
||||
@@ -41,6 +41,7 @@ panels <- list(
|
||||
)
|
||||
|
||||
test_that("tabsetPanel() markup is correct", {
|
||||
|
||||
default <- tabset_panel(!!!panels)
|
||||
pills <- tabset_panel(
|
||||
!!!panels, type = "pills", selected = "B",
|
||||
@@ -53,11 +54,6 @@ test_that("tabsetPanel() markup is correct", {
|
||||
# BS4
|
||||
expect_snapshot_bslib(default)
|
||||
expect_snapshot_bslib(pills)
|
||||
|
||||
# Make sure .active class gets added to both the .dropdown as well as the
|
||||
# .dropdown-menu's tab
|
||||
dropdown_active <- tabset_panel(!!!panels, selected = "C")
|
||||
expect_snapshot2(dropdown_active)
|
||||
})
|
||||
|
||||
test_that("navbarPage() markup is correct", {
|
||||
|
||||
@@ -218,7 +218,3 @@ reference:
|
||||
- shinyUI
|
||||
- shinyServer
|
||||
- exprToFunction
|
||||
# This section is silently dropped by pkgdown https://github.com/r-lib/pkgdown/pull/1783
|
||||
- title: internal
|
||||
contents:
|
||||
- shiny-package
|
||||
|
||||
@@ -2,15 +2,6 @@ diff --git a/inst/www/shared/ionrangeslider/js/ion.rangeSlider.js b/inst/www/sha
|
||||
index 2fe2c8d..89d204e 100644
|
||||
--- a/inst/www/shared/ionrangeslider/js/ion.rangeSlider.js
|
||||
+++ b/inst/www/shared/ionrangeslider/js/ion.rangeSlider.js
|
||||
@@ -753,7 +753,7 @@
|
||||
x = $handle.offset().left;
|
||||
x += ($handle.width() / 2) - 1;
|
||||
|
||||
- this.pointerClick("single", {preventDefault: function () {}, pageX: x});
|
||||
+ this.pointerClick("single", {preventDefault: function () {}, stopPropagation: function () {}, pageX: x});
|
||||
}
|
||||
},
|
||||
|
||||
@@ -816,6 +816,7 @@
|
||||
*/
|
||||
pointerDown: function (target, e) {
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
{
|
||||
"declaration": true,
|
||||
"compilerOptions": {
|
||||
"target": "es2020",
|
||||
"target": "ES5",
|
||||
"isolatedModules": true,
|
||||
"esModuleInterop": true,
|
||||
"declaration": true,
|
||||
"declarationDir": "./srcts/types",
|
||||
"emitDeclarationOnly": true,
|
||||
"moduleResolution": "node",
|
||||
// Can not use `types: []` to disable injecting NodeJS types. More types are
|
||||
// needed than just the DOM's `window.setTimeout`
|
||||
// "types": [],
|
||||
|
||||
Reference in New Issue
Block a user