mirror of
https://github.com/rstudio/shiny.git
synced 2026-01-13 00:48:09 -05:00
Compare commits
18 Commits
fix-routin
...
fix-cswsh
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
42b40e2fd1 | ||
|
|
6325067130 | ||
|
|
1a4e52dc73 | ||
|
|
08383ad8b9 | ||
|
|
ecf6bfe9a7 | ||
|
|
f7528568e5 | ||
|
|
51f653b66f | ||
|
|
460a93a5fd | ||
|
|
3ea4c8eb1d | ||
|
|
f237de559d | ||
|
|
8c7abbac44 | ||
|
|
1710316142 | ||
|
|
2d856f4f09 | ||
|
|
ab219e3408 | ||
|
|
673be3dd77 | ||
|
|
b25e6feabb | ||
|
|
e6b22d86b6 | ||
|
|
9c5196ee63 |
179
DESCRIPTION
179
DESCRIPTION
@@ -1,122 +1,132 @@
|
||||
Package: shiny
|
||||
Type: Package
|
||||
Package: shiny
|
||||
Title: Web Application Framework for R
|
||||
Version: 1.10.0.9001
|
||||
Version: 1.11.1.9000
|
||||
Authors@R: c(
|
||||
person("Winston", "Chang", role = c("aut", "cre"), email = "winston@posit.co", comment = c(ORCID = "0000-0002-1576-2126")),
|
||||
person("Joe", "Cheng", role = "aut", email = "joe@posit.co"),
|
||||
person("JJ", "Allaire", role = "aut", email = "jj@posit.co"),
|
||||
person("Carson", "Sievert", role = "aut", email = "carson@posit.co", comment = c(ORCID = "0000-0002-4958-2844")),
|
||||
person("Barret", "Schloerke", role = "aut", email = "barret@posit.co", comment = c(ORCID = "0000-0001-9986-114X")),
|
||||
person("Yihui", "Xie", role = "aut", email = "yihui@posit.co"),
|
||||
person("Winston", "Chang", , "winston@posit.co", role = "aut",
|
||||
comment = c(ORCID = "0000-0002-1576-2126")),
|
||||
person("Joe", "Cheng", , "joe@posit.co", role = "aut"),
|
||||
person("JJ", "Allaire", , "jj@posit.co", role = "aut"),
|
||||
person("Carson", "Sievert", , "carson@posit.co", role = c("aut", "cre"),
|
||||
comment = c(ORCID = "0000-0002-4958-2844")),
|
||||
person("Barret", "Schloerke", , "barret@posit.co", role = "aut",
|
||||
comment = c(ORCID = "0000-0001-9986-114X")),
|
||||
person("Garrick", "Aden-Buie", , "garrick@adenbuie.com", role = "aut",
|
||||
comment = c(ORCID = "0000-0002-7111-0077")),
|
||||
person("Yihui", "Xie", , "yihui@posit.co", role = "aut"),
|
||||
person("Jeff", "Allen", role = "aut"),
|
||||
person("Jonathan", "McPherson", role = "aut", email = "jonathan@posit.co"),
|
||||
person("Jonathan", "McPherson", , "jonathan@posit.co", role = "aut"),
|
||||
person("Alan", "Dipert", role = "aut"),
|
||||
person("Barbara", "Borges", role = "aut"),
|
||||
person("Posit Software, PBC", role = c("cph", "fnd")),
|
||||
person(family = "jQuery Foundation", role = "cph",
|
||||
comment = "jQuery library and jQuery UI library"),
|
||||
person(family = "jQuery contributors", role = c("ctb", "cph"),
|
||||
comment = "jQuery library; authors listed in inst/www/shared/jquery-AUTHORS.txt"),
|
||||
person(family = "jQuery UI contributors", role = c("ctb", "cph"),
|
||||
comment = "jQuery UI library; authors listed in inst/www/shared/jqueryui/AUTHORS.txt"),
|
||||
person("Posit Software, PBC", role = c("cph", "fnd"),
|
||||
comment = c(ROR = "03wc8by49")),
|
||||
person(, "jQuery Foundation", role = "cph",
|
||||
comment = "jQuery library and jQuery UI library"),
|
||||
person(, "jQuery contributors", role = c("ctb", "cph"),
|
||||
comment = "jQuery library; authors listed in inst/www/shared/jquery-AUTHORS.txt"),
|
||||
person(, "jQuery UI contributors", role = c("ctb", "cph"),
|
||||
comment = "jQuery UI library; authors listed in inst/www/shared/jqueryui/AUTHORS.txt"),
|
||||
person("Mark", "Otto", role = "ctb",
|
||||
comment = "Bootstrap library"),
|
||||
comment = "Bootstrap library"),
|
||||
person("Jacob", "Thornton", role = "ctb",
|
||||
comment = "Bootstrap library"),
|
||||
person(family = "Bootstrap contributors", role = "ctb",
|
||||
comment = "Bootstrap library"),
|
||||
person(family = "Twitter, Inc", role = "cph",
|
||||
comment = "Bootstrap library"),
|
||||
comment = "Bootstrap library"),
|
||||
person(, "Bootstrap contributors", role = "ctb",
|
||||
comment = "Bootstrap library"),
|
||||
person(, "Twitter, Inc", role = "cph",
|
||||
comment = "Bootstrap library"),
|
||||
person("Prem Nawaz", "Khan", role = "ctb",
|
||||
comment = "Bootstrap accessibility plugin"),
|
||||
comment = "Bootstrap accessibility plugin"),
|
||||
person("Victor", "Tsaran", role = "ctb",
|
||||
comment = "Bootstrap accessibility plugin"),
|
||||
comment = "Bootstrap accessibility plugin"),
|
||||
person("Dennis", "Lembree", role = "ctb",
|
||||
comment = "Bootstrap accessibility plugin"),
|
||||
comment = "Bootstrap accessibility plugin"),
|
||||
person("Srinivasu", "Chakravarthula", role = "ctb",
|
||||
comment = "Bootstrap accessibility plugin"),
|
||||
comment = "Bootstrap accessibility plugin"),
|
||||
person("Cathy", "O'Connor", role = "ctb",
|
||||
comment = "Bootstrap accessibility plugin"),
|
||||
person(family = "PayPal, Inc", role = "cph",
|
||||
comment = "Bootstrap accessibility plugin"),
|
||||
comment = "Bootstrap accessibility plugin"),
|
||||
person(, "PayPal, Inc", role = "cph",
|
||||
comment = "Bootstrap accessibility plugin"),
|
||||
person("Stefan", "Petre", role = c("ctb", "cph"),
|
||||
comment = "Bootstrap-datepicker library"),
|
||||
comment = "Bootstrap-datepicker library"),
|
||||
person("Andrew", "Rowls", role = c("ctb", "cph"),
|
||||
comment = "Bootstrap-datepicker library"),
|
||||
comment = "Bootstrap-datepicker library"),
|
||||
person("Brian", "Reavis", role = c("ctb", "cph"),
|
||||
comment = "selectize.js library"),
|
||||
comment = "selectize.js library"),
|
||||
person("Salmen", "Bejaoui", role = c("ctb", "cph"),
|
||||
comment = "selectize-plugin-a11y library"),
|
||||
comment = "selectize-plugin-a11y library"),
|
||||
person("Denis", "Ineshin", role = c("ctb", "cph"),
|
||||
comment = "ion.rangeSlider library"),
|
||||
comment = "ion.rangeSlider library"),
|
||||
person("Sami", "Samhuri", role = c("ctb", "cph"),
|
||||
comment = "Javascript strftime library"),
|
||||
person(family = "SpryMedia Limited", role = c("ctb", "cph"),
|
||||
comment = "DataTables library"),
|
||||
comment = "Javascript strftime library"),
|
||||
person(, "SpryMedia Limited", role = c("ctb", "cph"),
|
||||
comment = "DataTables library"),
|
||||
person("John", "Fraser", role = c("ctb", "cph"),
|
||||
comment = "showdown.js library"),
|
||||
comment = "showdown.js library"),
|
||||
person("John", "Gruber", role = c("ctb", "cph"),
|
||||
comment = "showdown.js library"),
|
||||
comment = "showdown.js library"),
|
||||
person("Ivan", "Sagalaev", role = c("ctb", "cph"),
|
||||
comment = "highlight.js library"),
|
||||
person(given = "R Core Team", role = c("ctb", "cph"),
|
||||
comment = "tar implementation from R")
|
||||
)
|
||||
comment = "highlight.js library"),
|
||||
person("R Core Team", role = c("ctb", "cph"),
|
||||
comment = "tar implementation from R")
|
||||
)
|
||||
Description: Makes it incredibly easy to build interactive web
|
||||
applications with R. Automatic "reactive" binding between inputs and
|
||||
outputs and extensive prebuilt widgets make it possible to build
|
||||
beautiful, responsive, and powerful applications with minimal effort.
|
||||
License: GPL-3 | file LICENSE
|
||||
URL: https://shiny.posit.co/, https://github.com/rstudio/shiny
|
||||
BugReports: https://github.com/rstudio/shiny/issues
|
||||
Depends:
|
||||
R (>= 3.0.2),
|
||||
methods
|
||||
methods,
|
||||
R (>= 3.0.2)
|
||||
Imports:
|
||||
utils,
|
||||
grDevices,
|
||||
httpuv (>= 1.5.2),
|
||||
mime (>= 0.3),
|
||||
jsonlite (>= 0.9.16),
|
||||
xtable,
|
||||
fontawesome (>= 0.4.0),
|
||||
htmltools (>= 0.5.4),
|
||||
R6 (>= 2.0),
|
||||
sourcetools,
|
||||
later (>= 1.0.0),
|
||||
promises (>= 1.3.2),
|
||||
tools,
|
||||
cli,
|
||||
rlang (>= 0.4.10),
|
||||
fastmap (>= 1.1.1),
|
||||
withr,
|
||||
commonmark (>= 1.7),
|
||||
glue (>= 1.3.2),
|
||||
bslib (>= 0.6.0),
|
||||
cachem (>= 1.1.0),
|
||||
lifecycle (>= 0.2.0)
|
||||
cli,
|
||||
commonmark (>= 1.7),
|
||||
fastmap (>= 1.1.1),
|
||||
fontawesome (>= 0.4.0),
|
||||
glue (>= 1.3.2),
|
||||
grDevices,
|
||||
htmltools (>= 0.5.4),
|
||||
httpuv (>= 1.5.2),
|
||||
jsonlite (>= 0.9.16),
|
||||
later (>= 1.0.0),
|
||||
lifecycle (>= 0.2.0),
|
||||
mime (>= 0.3),
|
||||
promises (>= 1.3.2),
|
||||
R6 (>= 2.0),
|
||||
rlang (>= 0.4.10),
|
||||
sourcetools,
|
||||
tools,
|
||||
utils,
|
||||
withr,
|
||||
xtable
|
||||
Suggests:
|
||||
Cairo (>= 1.5-5),
|
||||
coro (>= 1.1.0),
|
||||
datasets,
|
||||
DT,
|
||||
Cairo (>= 1.5-5),
|
||||
testthat (>= 3.2.1),
|
||||
knitr (>= 1.6),
|
||||
markdown,
|
||||
rmarkdown,
|
||||
ggplot2,
|
||||
reactlog (>= 1.0.0),
|
||||
magrittr,
|
||||
yaml,
|
||||
mirai,
|
||||
future,
|
||||
dygraphs,
|
||||
future,
|
||||
ggplot2,
|
||||
knitr (>= 1.6),
|
||||
magrittr,
|
||||
markdown,
|
||||
mirai,
|
||||
ragg,
|
||||
showtext,
|
||||
reactlog (>= 1.0.0),
|
||||
rmarkdown,
|
||||
sass,
|
||||
watcher
|
||||
URL: https://shiny.posit.co/,
|
||||
https://github.com/rstudio/shiny
|
||||
BugReports: https://github.com/rstudio/shiny/issues
|
||||
showtext,
|
||||
testthat (>= 3.2.1),
|
||||
watcher,
|
||||
yaml
|
||||
Config/Needs/check: shinytest2
|
||||
Config/testthat/edition: 3
|
||||
Encoding: UTF-8
|
||||
Roxygen: list(markdown = TRUE)
|
||||
RoxygenNote: 7.3.2
|
||||
Collate:
|
||||
'globals.R'
|
||||
'app-state.R'
|
||||
@@ -202,6 +212,7 @@ Collate:
|
||||
'test.R'
|
||||
'update-input.R'
|
||||
'utils-lang.R'
|
||||
'utils-tags.R'
|
||||
'version_bs_date_picker.R'
|
||||
'version_ion_range_slider.R'
|
||||
'version_jquery.R'
|
||||
@@ -209,9 +220,3 @@ Collate:
|
||||
'version_selectize.R'
|
||||
'version_strftime.R'
|
||||
'viewer.R'
|
||||
RoxygenNote: 7.3.2
|
||||
Encoding: UTF-8
|
||||
Roxygen: list(markdown = TRUE)
|
||||
Config/testthat/edition: 3
|
||||
Config/Needs/check:
|
||||
shinytest2
|
||||
|
||||
54
NEWS.md
54
NEWS.md
@@ -2,15 +2,27 @@
|
||||
|
||||
## New features
|
||||
|
||||
* `textInput()`, `textAreaInput()`, `numericInput()` and `passwordInput()` all gain an `updateOn` option. `updateOn = "change"` is the default and previous behavior, where the input value updates immediately whenever the value changes. With `updateOn = "blur"`, the input value will update only when the text input loses focus or when the user presses Enter (or Cmd/Ctrl + Enter for `textAreaInput()`). (#4183)
|
||||
* The `icon` argument of `updateActionButton()`/`updateActionLink()` nows allows values other than `shiny::icon()` (e.g., `fontawesome::fa()`, `bsicons::bs_icon()`, etc). (#4249)
|
||||
|
||||
* `textAreaInput()` gains a `autoresize` option, which automatically resizes the text area to fit its content. (#4210)
|
||||
## Bug fixes
|
||||
|
||||
* The family of `update*Input()` functions can now render HTML content passed to the `label` argument (e.g., `updateInputText(label = tags$b("New label"))`). (#3996)
|
||||
* `updateActionButton()`/`updateActionLink()` now correctly renders HTML content passed to the `label` argument. (#4249)
|
||||
|
||||
## Changes
|
||||
|
||||
* Shiny no longer suspends input changes when _any_ `<input type="submit">` or `<button type="submit">` is on the page. Instead, it now only suspends when a `submitButton()` is present. If you have reason for creating a submit button from custom HTML, add a CSS class of `shiny-submit-button` to the button. (#4209)
|
||||
* The return value of `actionButton()`/`actionLink()` changed slightly: `label` and `icon` are wrapped in an additional HTML container element. This allows for: 1. `updateActionButton()`/`updateActionLink()` to distinguish between the `label` and `icon` when making updates and 2. spacing between `label` and `icon` to be more easily customized via CSS.
|
||||
|
||||
# shiny 1.11.1
|
||||
|
||||
This is a patch release primarily for addressing the bugs introduced in v1.11.0.
|
||||
|
||||
## Bug fixes
|
||||
|
||||
* Fixed an issue where `InputBinding` implementations that don't pass a value to their `subscribe` callback were no longer notifying Shiny of input changes. (#4243)
|
||||
|
||||
* `updateActionButton()` and `updateActionLink()` once again handle `label` updates correctly. (#4245)
|
||||
|
||||
# shiny 1.11.0
|
||||
|
||||
## Improvements
|
||||
|
||||
@@ -21,13 +33,29 @@
|
||||
* Shows a spinner on `tableOutput()`. (#4172)
|
||||
* Places a minimum height on recalculating outputs so that the spinner is always visible. (#4172)
|
||||
|
||||
* Shiny now uses `{cli}` instead of `{crayon}` for rich log messages. (@olivroy #4170)
|
||||
* Shiny now uses `{cli}` instead of `{crayon}` for rich log messages. (thanks @olivroy, #4170)
|
||||
|
||||
* Shiny's Typescript assets are now compiled to ES2021 instead of ES5. (#4066)
|
||||
* `renderPlot()` was updated to accommodate changes in ggplot2 v4.0.0. (#4226)
|
||||
|
||||
* When adding the new tab via `insertTab()` or `bslib::nav_insert()`, the underlying JavaScript no longer renders content twice. (#4179)
|
||||
|
||||
## New features
|
||||
|
||||
* `textInput()`, `textAreaInput()`, `numericInput()` and `passwordInput()` all gain an `updateOn` option. `updateOn = "change"` is the default and previous behavior, where the input value updates immediately whenever the value changes. With `updateOn = "blur"`, the input value will update only when the text input loses focus or when the user presses Enter (or Cmd/Ctrl + Enter for `textAreaInput()`). (#4183)
|
||||
|
||||
* `textAreaInput()` gains a `autoresize` option, which automatically resizes the text area to fit its content. (#4210)
|
||||
|
||||
* The family of `update*Input()` functions can now render HTML content passed to the `label` argument (e.g., `updateInputText(label = tags$b("New label"))`). (#3996)
|
||||
|
||||
* `ExtendedTask` now catches synchronous values and errors and returns them via `$result()`. Previously, the extended task function was required to always return a promise. This change makes it easier to use `ExtendedTask` with a function that may return early or do some synchronous work before returning a promise. (#4225)
|
||||
|
||||
* `renderPlot()` was updated to accomodate changes in ggplot2 v4.0.0. (#4226)
|
||||
* The `callback` argument of Shiny.js' `InputBinding.subscribe()` method gains support for a value of `"event"`. This makes it possible for an input binding to use event priority when updating the value (i.e., send immediately and always resend, even if the value hasn't changed). (#4211)
|
||||
|
||||
## Changes
|
||||
|
||||
* Shiny no longer suspends input changes when _any_ `<input type="submit">` or `<button type="submit">` is on the page. Instead, it now only suspends when a `submitButton()` is present. If you have reason for creating a submit button from custom HTML, add a CSS class of `shiny-submit-button` to the button. (#4209)
|
||||
|
||||
* Shiny's JavaScript assets are now compiled to ES2021 instead of ES5. (#4066)
|
||||
|
||||
* Upgraded jQuery from 3.6.0 to 3.7.1. (#3969)
|
||||
|
||||
@@ -35,15 +63,13 @@
|
||||
|
||||
## Bug fixes
|
||||
|
||||
* `runExample("08_html")` now (correctly) requests to 'shiny.min.css', eliminating a network request failure. (#4220)
|
||||
|
||||
* Fixed a bug with modals where calling `removeModal()` too quickly after `showModal()` would fail to remove the modal if the remove modal message was received while the modal was in the process of being revealed. (#4173)
|
||||
|
||||
* The Shiny Client Console (enabled with `shiny::devmode()`) no longer displays duplicate warning or error message. (#4177)
|
||||
|
||||
* Updated the JavaScript used when inserting a tab to avoid rendering dynamic UI elements twice when adding the new tab via `insertTab()` or `bslib::nav_insert()`. (#4179)
|
||||
* Synchronous errors that occur inside a `ExtendedTask` no longer stop the session. (#4225)
|
||||
|
||||
* Fixed an issue with `ExtendedTask` where synchronous errors would cause an error that would stop the current session. (#4225)
|
||||
* Calling `removeModal()` immediately after `showModal()` no longer fails to remove the modal (this would sometimes happen if the remove message was received while the modal was in the process of being revealed). (#4173)
|
||||
|
||||
* `runExample("08_html")` now (correctly) requests to 'shiny.min.css', eliminating a network request failure. (#4220)
|
||||
|
||||
* `shiny::shinyAppTemplate()` no longer errors without a call to `library(shiny)`. (#3870)
|
||||
|
||||
@@ -394,7 +420,7 @@ This release focuses on improvements in three main areas:
|
||||
|
||||
* Fixed #2951: screen readers correctly announce labels and date formats for `dateInput()` and `dateRangeInput()` widgets. (#2978)
|
||||
|
||||
* Closed #2847: `selectInput()` is reasonably accessible for screen readers even when `selectize` option is set to TRUE. To improve `selectize.js` accessibility, we have added [selectize-plugin-a11y](https://github.com/SLMNBJ/selectize-plugin-a11y) by default. (#2993)
|
||||
* Closed #2847: `selectInput()` is reasonably accessible for screen readers even when `selectize` option is set to TRUE. To improve `selectize.js` accessibility, we have added [selectize-plugin-a11y](https://github.com/SalmenBejaoui/selectize-plugin-a11y) by default. (#2993)
|
||||
|
||||
* Closed #612: Added `alt` argument to `renderPlot()` and `renderCachedPlot()` to specify descriptive texts for `plotOutput()` objects, which is essential for screen readers. By default, alt text is set to the static text, "Plot object," but even dynamic text can be made with reactive function. (#3006, thanks @trafficonese and @leonawicz for the original PR and discussion via #2494)
|
||||
|
||||
|
||||
@@ -56,13 +56,24 @@ actionButton <- function(inputId, label, icon = NULL, width = NULL,
|
||||
|
||||
value <- restoreInput(id = inputId, default = NULL)
|
||||
|
||||
tags$button(id=inputId,
|
||||
icon <- validateIcon(icon)
|
||||
|
||||
if (!is.null(icon)) {
|
||||
icon <- span(icon, class = "action-icon")
|
||||
}
|
||||
|
||||
if (!is.null(label)) {
|
||||
label <- span(label, class = "action-label")
|
||||
}
|
||||
|
||||
tags$button(
|
||||
id = inputId,
|
||||
style = css(width = validateCssUnit(width)),
|
||||
type="button",
|
||||
class="btn btn-default action-button",
|
||||
type = "button",
|
||||
class = "btn btn-default action-button",
|
||||
`data-val` = value,
|
||||
disabled = if (isTRUE(disabled)) NA else NULL,
|
||||
list(validateIcon(icon), label),
|
||||
icon, label,
|
||||
...
|
||||
)
|
||||
}
|
||||
@@ -72,30 +83,40 @@ actionButton <- function(inputId, label, icon = NULL, width = NULL,
|
||||
actionLink <- function(inputId, label, icon = NULL, ...) {
|
||||
value <- restoreInput(id = inputId, default = NULL)
|
||||
|
||||
tags$a(id=inputId,
|
||||
href="#",
|
||||
class="action-button",
|
||||
icon <- validateIcon(icon)
|
||||
|
||||
if (!is.null(icon)) {
|
||||
icon <- span(icon, class = "action-icon")
|
||||
}
|
||||
|
||||
if (!is.null(label)) {
|
||||
label <- span(label, class = "action-label")
|
||||
}
|
||||
|
||||
tags$a(
|
||||
id = inputId,
|
||||
href = "#",
|
||||
class = "action-button action-link",
|
||||
`data-val` = value,
|
||||
list(validateIcon(icon), label),
|
||||
icon, label,
|
||||
...
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
# Check that the icon parameter is valid:
|
||||
# 1) Check if the user wants to actually add an icon:
|
||||
# -- if icon=NULL, it means leave the icon unchanged
|
||||
# -- if icon=character(0), it means don't add an icon or, more usefully,
|
||||
# remove the previous icon
|
||||
# 2) If so, check that the icon has the right format (this does not check whether
|
||||
# it is a *real* icon - currently that would require a massive cross reference
|
||||
# with the "font-awesome" and the "glyphicon" libraries)
|
||||
# Throw an informative warning if icon isn't html-ish
|
||||
validateIcon <- function(icon) {
|
||||
if (is.null(icon) || identical(icon, character(0))) {
|
||||
if (length(icon) == 0) {
|
||||
return(icon)
|
||||
} else if (inherits(icon, "shiny.tag") && icon$name == "i") {
|
||||
return(icon)
|
||||
} else {
|
||||
stop("Invalid icon. Use Shiny's 'icon()' function to generate a valid icon")
|
||||
}
|
||||
if (!isTagLike(icon)) {
|
||||
rlang::warn(
|
||||
c(
|
||||
"It appears that a non-HTML value was provided to `icon`.",
|
||||
i = "Try using a `shiny::icon()` (or an equivalent) to get an icon."
|
||||
),
|
||||
class = "shiny-validate-icon"
|
||||
)
|
||||
}
|
||||
icon
|
||||
}
|
||||
|
||||
@@ -38,8 +38,8 @@ updateTextInput <- function(session = getDefaultReactiveDomain(), inputId, label
|
||||
validate_session_object(session)
|
||||
|
||||
message <- dropNulls(list(
|
||||
label = processDeps(label, session),
|
||||
value = value,
|
||||
label = if (!is.null(label)) processDeps(label, session),
|
||||
value = value,
|
||||
placeholder = placeholder
|
||||
))
|
||||
session$sendInputMessage(inputId, message)
|
||||
@@ -116,7 +116,7 @@ updateCheckboxInput <- function(session = getDefaultReactiveDomain(), inputId, l
|
||||
validate_session_object(session)
|
||||
|
||||
message <- dropNulls(list(
|
||||
label = processDeps(label, session),
|
||||
label = if (!is.null(label)) processDeps(label, session),
|
||||
value = value
|
||||
))
|
||||
session$sendInputMessage(inputId, message)
|
||||
@@ -181,10 +181,9 @@ updateCheckboxInput <- function(session = getDefaultReactiveDomain(), inputId, l
|
||||
updateActionButton <- function(session = getDefaultReactiveDomain(), inputId, label = NULL, icon = NULL, disabled = NULL) {
|
||||
validate_session_object(session)
|
||||
|
||||
if (!is.null(icon)) icon <- as.character(validateIcon(icon))
|
||||
message <- dropNulls(list(
|
||||
label = processDeps(label, session),
|
||||
icon = icon,
|
||||
label = if (!is.null(label)) processDeps(label, session),
|
||||
icon = if (!is.null(icon)) processDeps(validateIcon(icon), session),
|
||||
disabled = disabled
|
||||
))
|
||||
session$sendInputMessage(inputId, message)
|
||||
@@ -192,7 +191,7 @@ updateActionButton <- function(session = getDefaultReactiveDomain(), inputId, la
|
||||
#' @rdname updateActionButton
|
||||
#' @export
|
||||
updateActionLink <- function(session = getDefaultReactiveDomain(), inputId, label = NULL, icon = NULL) {
|
||||
updateActionButton(session, inputId = inputId, label = processDeps(label, session), icon = icon)
|
||||
updateActionButton(session, inputId=inputId, label=label, icon=icon)
|
||||
}
|
||||
|
||||
|
||||
@@ -237,7 +236,7 @@ updateDateInput <- function(session = getDefaultReactiveDomain(), inputId, label
|
||||
max <- dateYMD(max, "max")
|
||||
|
||||
message <- dropNulls(list(
|
||||
label = processDeps(label, session),
|
||||
label = if (!is.null(label)) processDeps(label, session),
|
||||
value = value,
|
||||
min = min,
|
||||
max = max
|
||||
@@ -291,7 +290,7 @@ updateDateRangeInput <- function(session = getDefaultReactiveDomain(), inputId,
|
||||
max <- dateYMD(max, "max")
|
||||
|
||||
message <- dropNulls(list(
|
||||
label = processDeps(label, session),
|
||||
label = if (!is.null(label)) processDeps(label, session),
|
||||
value = dropNulls(list(start = start, end = end)),
|
||||
min = min,
|
||||
max = max
|
||||
@@ -395,7 +394,7 @@ updateNumericInput <- function(session = getDefaultReactiveDomain(), inputId, la
|
||||
validate_session_object(session)
|
||||
|
||||
message <- dropNulls(list(
|
||||
label = processDeps(label, session),
|
||||
label = if (!is.null(label)) processDeps(label, session),
|
||||
value = formatNoSci(value),
|
||||
min = formatNoSci(min),
|
||||
max = formatNoSci(max),
|
||||
@@ -479,7 +478,7 @@ updateSliderInput <- function(session = getDefaultReactiveDomain(), inputId, lab
|
||||
}
|
||||
|
||||
message <- dropNulls(list(
|
||||
label = processDeps(label, session),
|
||||
label = if (!is.null(label)) processDeps(label, session),
|
||||
value = formatNoSci(value),
|
||||
min = formatNoSci(min),
|
||||
max = formatNoSci(max),
|
||||
@@ -511,8 +510,8 @@ updateInputOptions <- function(session, inputId, label = NULL, choices = NULL,
|
||||
}
|
||||
|
||||
message <- dropNulls(list(
|
||||
label = processDeps(label, session),
|
||||
options = options,
|
||||
label = if (!is.null(label)) processDeps(label, session),
|
||||
options = options,
|
||||
value = selected
|
||||
))
|
||||
|
||||
@@ -668,8 +667,8 @@ updateSelectInput <- function(session = getDefaultReactiveDomain(), inputId, lab
|
||||
if (!is.null(selected)) selected <- as.character(selected)
|
||||
options <- if (!is.null(choices)) selectOptions(choices, selected, inputId, FALSE)
|
||||
message <- dropNulls(list(
|
||||
label = processDeps(label, session),
|
||||
options = options,
|
||||
label = if (!is.null(label)) processDeps(label, session),
|
||||
options = options,
|
||||
value = selected
|
||||
))
|
||||
session$sendInputMessage(inputId, message)
|
||||
|
||||
21
R/utils-tags.R
Normal file
21
R/utils-tags.R
Normal file
@@ -0,0 +1,21 @@
|
||||
# Check if `x` is a tag(), tagList(), or HTML()
|
||||
# @param strict If `FALSE`, also consider a normal list() of 'tags' to be a tag list.
|
||||
isTagLike <- function(x, strict = FALSE) {
|
||||
isTag(x) || isTagList(x, strict = strict) || isTRUE(attr(x, "html"))
|
||||
}
|
||||
|
||||
isTag <- function(x) {
|
||||
inherits(x, "shiny.tag")
|
||||
}
|
||||
|
||||
isTagList <- function(x, strict = TRUE) {
|
||||
if (strict) {
|
||||
return(inherits(x, "shiny.tag.list"))
|
||||
}
|
||||
|
||||
if (!is.list(x)) {
|
||||
return(FALSE)
|
||||
}
|
||||
|
||||
all(vapply(x, isTagLike, logical(1)))
|
||||
}
|
||||
@@ -1,2 +1,2 @@
|
||||
/*! shiny 1.10.0.9001 | (c) 2012-2025 Posit Software, PBC. | License: GPL-3 | file LICENSE */
|
||||
/*! shiny 1.11.1.9000 | (c) 2012-2025 Posit Software, PBC. | License: GPL-3 | file LICENSE */
|
||||
:where([data-shiny-busy-spinners] .recalculating){position:relative}[data-shiny-busy-spinners] .recalculating{min-height:var(--shiny-spinner-size, 32px)}[data-shiny-busy-spinners] .recalculating:after{position:absolute;content:"";--_shiny-spinner-url: var(--shiny-spinner-url, url(spinners/ring.svg));--_shiny-spinner-color: var(--shiny-spinner-color, var(--bs-primary, #007bc2));--_shiny-spinner-size: var(--shiny-spinner-size, 32px);--_shiny-spinner-delay: var(--shiny-spinner-delay, 1s);background:var(--_shiny-spinner-color);width:var(--_shiny-spinner-size);height:var(--_shiny-spinner-size);inset:calc(50% - var(--_shiny-spinner-size) / 2);mask-image:var(--_shiny-spinner-url);-webkit-mask-image:var(--_shiny-spinner-url);opacity:0;animation-delay:var(--_shiny-spinner-delay);animation-name:fade-in;animation-duration:.25s;animation-fill-mode:forwards}[data-shiny-busy-spinners] .recalculating:has(>*),[data-shiny-busy-spinners] .recalculating:empty{opacity:1}[data-shiny-busy-spinners] .recalculating>*:not(.recalculating){opacity:var(--_shiny-fade-opacity);transition:opacity .25s ease var(--shiny-spinner-delay, 1s)}[data-shiny-busy-spinners] .recalculating.html-widget-output{visibility:inherit!important}[data-shiny-busy-spinners] .recalculating.html-widget-output>*{visibility:hidden}[data-shiny-busy-spinners] .recalculating.html-widget-output :after{visibility:visible}[data-shiny-busy-spinners] .recalculating.shiny-html-output:not(.shiny-table-output):after{display:none}[data-shiny-busy-spinners][data-shiny-busy-pulse].shiny-busy:after{--_shiny-pulse-background: var( --shiny-pulse-background, linear-gradient( 120deg, transparent, var(--bs-indigo, #4b00c1), var(--bs-purple, #74149c), var(--bs-pink, #bf007f), transparent ) );--_shiny-pulse-height: var(--shiny-pulse-height, 3px);--_shiny-pulse-speed: var(--shiny-pulse-speed, 1.2s);position:fixed;top:0;left:0;height:var(--_shiny-pulse-height);background:var(--_shiny-pulse-background);z-index:9999;animation-name:busy-page-pulse;animation-duration:var(--_shiny-pulse-speed);animation-direction:alternate;animation-iteration-count:infinite;animation-timing-function:ease-in-out;content:""}[data-shiny-busy-spinners][data-shiny-busy-pulse].shiny-busy:has(.recalculating:not(.shiny-html-output)):after{display:none}[data-shiny-busy-spinners][data-shiny-busy-pulse].shiny-busy:has(.recalculating.shiny-table-output):after{display:none}[data-shiny-busy-spinners][data-shiny-busy-pulse].shiny-busy:has(#shiny-disconnected-overlay):after{display:none}[data-shiny-busy-pulse]:not([data-shiny-busy-spinners]).shiny-busy:after{--_shiny-pulse-background: var( --shiny-pulse-background, linear-gradient( 120deg, transparent, var(--bs-indigo, #4b00c1), var(--bs-purple, #74149c), var(--bs-pink, #bf007f), transparent ) );--_shiny-pulse-height: var(--shiny-pulse-height, 3px);--_shiny-pulse-speed: var(--shiny-pulse-speed, 1.2s);position:fixed;top:0;left:0;height:var(--_shiny-pulse-height);background:var(--_shiny-pulse-background);z-index:9999;animation-name:busy-page-pulse;animation-duration:var(--_shiny-pulse-speed);animation-direction:alternate;animation-iteration-count:infinite;animation-timing-function:ease-in-out;content:""}[data-shiny-busy-pulse]:not([data-shiny-busy-spinners]).shiny-busy:has(#shiny-disconnected-overlay):after{display:none}@keyframes fade-in{0%{opacity:0}to{opacity:1}}@keyframes busy-page-pulse{0%{left:-14%;right:97%}45%{left:0%;right:14%}55%{left:14%;right:0%}to{left:97%;right:-14%}}.shiny-spinner-output-container{--shiny-spinner-size: 0px}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
/*! shiny 1.10.0.9001 | (c) 2012-2025 Posit Software, PBC. | License: GPL-3 | file LICENSE */
|
||||
/*! shiny 1.11.1.9000 | (c) 2012-2025 Posit Software, PBC. | License: GPL-3 | file LICENSE */
|
||||
"use strict";(()=>{document.documentElement.classList.add("autoreload-enabled");var c=window.location.protocol==="https:"?"wss:":"ws:",s=window.location.pathname.replace(/\/?$/,"/")+"autoreload/",i=`${c}//${window.location.host}${s}`,l=document.currentScript?.dataset?.wsUrl||i;async function u(o){let e=new WebSocket(o),n=!1;return new Promise((a,r)=>{e.onopen=()=>{n=!0},e.onerror=t=>{r(t)},e.onclose=()=>{n?a(!1):r(new Error("WebSocket connection failed"))},e.onmessage=function(t){t.data==="autoreload"&&a(!0)}})}async function d(o){return new Promise(e=>setTimeout(e,o))}async function w(){for(;;){try{if(await u(l)){window.location.reload();return}}catch{console.debug("Giving up on autoreload");return}await d(1e3)}}w().catch(o=>{console.error(o)});})();
|
||||
//# sourceMappingURL=shiny-autoreload.js.map
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
/*! shiny 1.10.0.9001 | (c) 2012-2025 Posit Software, PBC. | License: GPL-3 | file LICENSE */
|
||||
/*! shiny 1.11.1.9000 | (c) 2012-2025 Posit Software, 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}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
/*! shiny 1.10.0.9001 | (c) 2012-2025 Posit Software, PBC. | License: GPL-3 | file LICENSE */
|
||||
/*! shiny 1.11.1.9000 | (c) 2012-2025 Posit Software, PBC. | License: GPL-3 | file LICENSE */
|
||||
"use strict";(()=>{var f=400;function c(e,i){let t=0;if(e.nodeType===3){let n=e.nodeValue.replace(/\n/g,"").length;if(n>=i)return{element:e,offset:i};t+=n}else if(e.nodeType===1&&e.firstChild){let n=c(e.firstChild,i);if(n.element!==null)return n;t+=n.offset}return e.nextSibling?c(e.nextSibling,i-t):{element:null,offset:t}}function r(e,i,t){let n=0;for(let s=0;s<e.childNodes.length;s++){let l=e.childNodes[s];if(l.nodeType===3){let o=/\n/g,d;for(;(d=o.exec(l.nodeValue))!==null;)if(n++,n===i)return c(l,d.index+t+1)}else if(l.nodeType===1){let o=r(l,i-n,t);if(o.element!==null)return o;n+=o.offset}}return{element:null,offset:n}}function w(e,i){if(!document.createRange)return;let t=document.getElementById("srcref_"+e);if(!t){t=document.createElement("span"),t.id="srcref_"+e;let n=e,s=document.getElementById(i.replace(/\./g,"_")+"_code");if(!s)return;let l=r(s,n[0],n[4]),o=r(s,n[2],n[5]);if(l.element===null||o.element===null)return;let d=document.createRange();l.element.parentNode.nodeName==="SPAN"&&l.element!==o.element?d.setStartBefore(l.element.parentNode):d.setStart(l.element,l.offset),o.element.parentNode.nodeName==="SPAN"&&l.element!==o.element?d.setEndAfter(o.element.parentNode):d.setEnd(o.element,o.offset),d.surroundContents(t)}$(t).stop(!0,!0).effect("highlight",null,1600)}Shiny&&Shiny.addCustomMessageHandler("showcase-src",function(e){e.srcref&&e.srcfile&&w(e.srcref,e.srcfile)});var a=!1,m=function(e,i){let t=i?f:1,n=e?document.getElementById("showcase-sxs-code"):document.getElementById("showcase-code-inline"),s=e?document.getElementById("showcase-code-inline"):document.getElementById("showcase-sxs-code");if(document.getElementById("showcase-app-metadata")===null){let o=$("#showcase-well");e?o.fadeOut(t):o.fadeIn(t)}$(n).hide(),$(s).fadeOut(t,function(){let o=document.getElementById("showcase-code-tabs");s.removeChild(o),n.appendChild(o),e?h():document.getElementById("showcase-code-content").removeAttribute("style"),$(n).fadeIn(t),e||(document.getElementById("showcase-app-container").removeAttribute("style"),i&&$(document.body).animate({scrollTop:$(n).offset().top}));let d=document.getElementById("readme-md");d!==null&&(d.parentElement.removeChild(d),e?(s.appendChild(d),$(s).fadeIn(t)):document.getElementById("showcase-app-metadata").appendChild(d)),document.getElementById("showcase-code-position-toggle").innerHTML=e?'<i class="fa fa-level-down"></i> show below':'<i class="fa fa-level-up"></i> show with app'}),e&&$(document.body).animate({scrollTop:0},t),a=e,u(e&&i),$(window).trigger("resize")};function u(e){let t=960,n=1,s=document.getElementById("showcase-app-code").offsetWidth;s/2>960?t=s/2:s*.66>960?t=960:(t=s*.66,n=t/960);let l=document.getElementById("showcase-app-container");$(l).animate({width:t+"px",zoom:n*100+"%"},e?f:0)}var g=function(){m(!a,!0)},p=function(){document.body.offsetWidth>1350&&m(!0,!1)};function h(){document.getElementById("showcase-code-content").style.height=$(window).height()+"px"}function y(){let e=document.getElementById("showcase-markdown-content");if(e!==null){let i=e.innerText||e.innerHTML,t=window.Showdown.converter;document.getElementById("readme-md").innerHTML=new t().makeHtml(i)}}$(window).resize(function(){a&&(u(!1),h())});window.toggleCodePosition=g;$(window).on("load",p);$(window).on("load",y);window.hljs&&window.hljs.initHighlightingOnLoad();})();
|
||||
//# sourceMappingURL=shiny-showcase.js.map
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
/*! shiny 1.10.0.9001 | (c) 2012-2025 Posit Software, PBC. | License: GPL-3 | file LICENSE */
|
||||
/*! shiny 1.11.1.9000 | (c) 2012-2025 Posit Software, PBC. | License: GPL-3 | file LICENSE */
|
||||
"use strict";(()=>{var t=eval;window.addEventListener("message",function(a){let e=a.data;e.code&&t(e.code)});})();
|
||||
//# sourceMappingURL=shiny-testmode.js.map
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/*! shiny 1.10.0.9001 | (c) 2012-2025 Posit Software, PBC. | License: GPL-3 | file LICENSE */
|
||||
/*! shiny 1.11.1.9000 | (c) 2012-2025 Posit Software, PBC. | License: GPL-3 | file LICENSE */
|
||||
"use strict";
|
||||
(() => {
|
||||
var __create = Object.create;
|
||||
@@ -1165,30 +1165,34 @@
|
||||
getState(el) {
|
||||
return { value: this.getValue(el) };
|
||||
}
|
||||
receiveMessage(el, data) {
|
||||
const $el = (0, import_jquery7.default)(el);
|
||||
if (hasDefinedProperty(data, "label") || hasDefinedProperty(data, "icon")) {
|
||||
let label = $el.text();
|
||||
let icon = "";
|
||||
if ($el.find("i[class]").length > 0) {
|
||||
const iconHtml = $el.find("i[class]")[0];
|
||||
if (iconHtml === $el.children()[0]) {
|
||||
icon = (0, import_jquery7.default)(iconHtml).prop("outerHTML");
|
||||
}
|
||||
async receiveMessage(el, data) {
|
||||
if (hasDefinedProperty(data, "icon")) {
|
||||
let iconContainer = el.querySelector(
|
||||
":scope > .action-icon"
|
||||
);
|
||||
if (!iconContainer) {
|
||||
iconContainer = document.createElement("span");
|
||||
iconContainer.className = "action-icon";
|
||||
el.prepend(iconContainer);
|
||||
}
|
||||
if (hasDefinedProperty(data, "label")) {
|
||||
label = data.label;
|
||||
await renderContent(iconContainer, data.icon);
|
||||
}
|
||||
if (hasDefinedProperty(data, "label")) {
|
||||
let labelContainer = el.querySelector(
|
||||
":scope > .action-label"
|
||||
);
|
||||
if (!labelContainer) {
|
||||
labelContainer = document.createElement("span");
|
||||
labelContainer.className = "action-label";
|
||||
el.appendChild(labelContainer);
|
||||
}
|
||||
if (hasDefinedProperty(data, "icon")) {
|
||||
icon = Array.isArray(data.icon) ? "" : data.icon ?? "";
|
||||
}
|
||||
$el.html(icon + " " + label);
|
||||
await renderContent(labelContainer, data.label);
|
||||
}
|
||||
if (hasDefinedProperty(data, "disabled")) {
|
||||
if (data.disabled) {
|
||||
$el.attr("disabled", "");
|
||||
el.setAttribute("disabled", "");
|
||||
} else {
|
||||
$el.attr("disabled", null);
|
||||
el.removeAttribute("disabled");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1226,12 +1230,13 @@
|
||||
value: el.checked
|
||||
};
|
||||
}
|
||||
receiveMessage(el, data) {
|
||||
async receiveMessage(el, data) {
|
||||
if (hasDefinedProperty(data, "value")) {
|
||||
el.checked = data.value;
|
||||
}
|
||||
if (hasDefinedProperty(data, "label")) {
|
||||
(0, import_jquery8.default)(el).parent().find("span").text(data.label);
|
||||
const labelSpan = (0, import_jquery8.default)(el).parent().find("span");
|
||||
await renderContent(labelSpan, data.label);
|
||||
}
|
||||
(0, import_jquery8.default)(el).trigger("change");
|
||||
}
|
||||
@@ -5611,21 +5616,29 @@
|
||||
function isJQuery(value) {
|
||||
return Boolean(value && value.jquery);
|
||||
}
|
||||
function valueChangeCallback(inputs, binding, el, allowDeferred) {
|
||||
function valueChangeCallback(inputs, binding, el, priority) {
|
||||
let id = binding.getId(el);
|
||||
if (id) {
|
||||
const value = binding.getValue(el);
|
||||
const type = binding.getType(el);
|
||||
if (type)
|
||||
id = id + ":" + type;
|
||||
const opts = {
|
||||
priority: allowDeferred ? "deferred" : "immediate",
|
||||
binding,
|
||||
el
|
||||
};
|
||||
inputs.setInput(id, value, opts);
|
||||
const normalizedPriority = normalizeEventPriority(priority);
|
||||
inputs.setInput(id, value, { priority: normalizedPriority, binding, el });
|
||||
}
|
||||
}
|
||||
function normalizeEventPriority(priority) {
|
||||
if (priority === false || priority === void 0) {
|
||||
return "immediate";
|
||||
}
|
||||
if (priority === true) {
|
||||
return "deferred";
|
||||
}
|
||||
if (typeof priority === "object" && "priority" in priority) {
|
||||
return priority.priority;
|
||||
}
|
||||
return priority;
|
||||
}
|
||||
var bindingsRegistry = (() => {
|
||||
const bindings = /* @__PURE__ */ new Map();
|
||||
function checkValidity(scope) {
|
||||
@@ -5738,8 +5751,8 @@ ${duplicateIdMsg}`;
|
||||
const thisCallback = function() {
|
||||
const thisBinding = binding;
|
||||
const thisEl = el;
|
||||
return function(allowDeferred) {
|
||||
valueChangeCallback(inputs, thisBinding, thisEl, allowDeferred);
|
||||
return function(priority) {
|
||||
valueChangeCallback(inputs, thisBinding, thisEl, priority);
|
||||
};
|
||||
}();
|
||||
binding.subscribe(el, thisCallback);
|
||||
@@ -6420,10 +6433,10 @@ ${duplicateIdMsg}`;
|
||||
};
|
||||
this._init();
|
||||
}
|
||||
connect(initialInput) {
|
||||
async connect(initialInput) {
|
||||
if (this.$socket)
|
||||
throw "Connect was already called on this application object";
|
||||
this.$socket = this.createSocket();
|
||||
this.$socket = await this.createSocket();
|
||||
this.$initialInput = initialInput;
|
||||
import_jquery38.default.extend(this.$inputValues, initialInput);
|
||||
this.$updateConditionals();
|
||||
@@ -6431,16 +6444,16 @@ ${duplicateIdMsg}`;
|
||||
isConnected() {
|
||||
return !!this.$socket;
|
||||
}
|
||||
reconnect() {
|
||||
async reconnect() {
|
||||
clearTimeout(this.scheduledReconnect);
|
||||
if (this.isConnected())
|
||||
throw "Attempted to reconnect, but already connected.";
|
||||
this.$socket = this.createSocket();
|
||||
this.$socket = await this.createSocket();
|
||||
this.$initialInput = import_jquery38.default.extend({}, this.$inputValues);
|
||||
this.$updateConditionals();
|
||||
}
|
||||
createSocket() {
|
||||
const createSocketFunc = getShinyCreateWebsocket() || (() => {
|
||||
async createSocket() {
|
||||
const createSocketFunc = getShinyCreateWebsocket() || (async () => {
|
||||
let protocol = "ws:";
|
||||
if (window.location.protocol === "https:")
|
||||
protocol = "wss:";
|
||||
@@ -6454,13 +6467,17 @@ ${duplicateIdMsg}`;
|
||||
if (!/\/$/.test(defaultPath))
|
||||
defaultPath += "/";
|
||||
defaultPath += "websocket/";
|
||||
const sessionId = await this.$getSessionId();
|
||||
if (sessionId) {
|
||||
defaultPath += "?session_id=" + sessionId;
|
||||
}
|
||||
const ws = new WebSocket(
|
||||
protocol + "//" + window.location.host + defaultPath
|
||||
);
|
||||
ws.binaryType = "arraybuffer";
|
||||
return ws;
|
||||
});
|
||||
const socket = createSocketFunc();
|
||||
const socket = await createSocketFunc();
|
||||
let hasOpened = false;
|
||||
socket.onopen = () => {
|
||||
hasOpened = true;
|
||||
@@ -6498,6 +6515,26 @@ ${duplicateIdMsg}`;
|
||||
};
|
||||
return socket;
|
||||
}
|
||||
async $getSessionId() {
|
||||
if (this.config) {
|
||||
return this.config.sessionId;
|
||||
}
|
||||
try {
|
||||
const response = await fetch("/new_shiny_session/");
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
return data.session_id;
|
||||
}
|
||||
if (response.status === 404) {
|
||||
return null;
|
||||
}
|
||||
console.error("Error fetching session ID:", response);
|
||||
return null;
|
||||
} catch (error) {
|
||||
console.error("Error fetching session ID:", error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
async startActionQueueLoop() {
|
||||
while (true) {
|
||||
try {
|
||||
@@ -6527,8 +6564,8 @@ ${duplicateIdMsg}`;
|
||||
this.$socket = null;
|
||||
}
|
||||
$scheduleReconnect(delay) {
|
||||
this.scheduledReconnect = window.setTimeout(() => {
|
||||
this.reconnect();
|
||||
this.scheduledReconnect = window.setTimeout(async () => {
|
||||
await this.reconnect();
|
||||
}, delay);
|
||||
}
|
||||
onDisconnected(reloading = false) {
|
||||
@@ -7188,7 +7225,7 @@ ${duplicateIdMsg}`;
|
||||
// srcts/src/shiny/index.ts
|
||||
var ShinyClass = class {
|
||||
constructor() {
|
||||
this.version = "1.10.0.9001";
|
||||
this.version = "1.11.1.9000";
|
||||
const { inputBindings, fileInputBinding: fileInputBinding2 } = initInputBindings();
|
||||
const { outputBindings } = initOutputBindings();
|
||||
setFileInputBinding(fileInputBinding2);
|
||||
@@ -7566,7 +7603,7 @@ ${duplicateIdMsg}`;
|
||||
}
|
||||
});
|
||||
inputsNoResend.reset(initialValues);
|
||||
shinyapp.connect(initialValues);
|
||||
await shinyapp.connect(initialValues);
|
||||
(0, import_jquery39.default)(document).one("shiny:connected", () => {
|
||||
initDeferredIframes();
|
||||
});
|
||||
|
||||
File diff suppressed because one or more lines are too long
4
inst/www/shared/shiny.min.css
vendored
4
inst/www/shared/shiny.min.css
vendored
File diff suppressed because one or more lines are too long
34
inst/www/shared/shiny.min.js
vendored
34
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
@@ -463,6 +463,13 @@ textarea.textarea-autoresize.form-control {
|
||||
}
|
||||
}
|
||||
|
||||
// Add spacing between icon and label for actionButton()
|
||||
.action-button:not(.action-link) {
|
||||
.action-icon + .action-label {
|
||||
padding-left: 0.5ch;
|
||||
}
|
||||
}
|
||||
|
||||
/* Overrides bootstrap-datepicker3.css styling for invalid date ranges.
|
||||
See https://github.com/rstudio/shiny/issues/2042 for details. */
|
||||
.datepicker table tbody tr td.disabled,
|
||||
|
||||
@@ -20,14 +20,15 @@ includes extensive annotated examples.
|
||||
\link{shiny-options} for documentation about global options.
|
||||
}
|
||||
\author{
|
||||
\strong{Maintainer}: Winston Chang \email{winston@posit.co} (\href{https://orcid.org/0000-0002-1576-2126}{ORCID})
|
||||
\strong{Maintainer}: Carson Sievert \email{carson@posit.co} (\href{https://orcid.org/0000-0002-4958-2844}{ORCID})
|
||||
|
||||
Authors:
|
||||
\itemize{
|
||||
\item Winston Chang \email{winston@posit.co} (\href{https://orcid.org/0000-0002-1576-2126}{ORCID})
|
||||
\item Joe Cheng \email{joe@posit.co}
|
||||
\item JJ Allaire \email{jj@posit.co}
|
||||
\item Carson Sievert \email{carson@posit.co} (\href{https://orcid.org/0000-0002-4958-2844}{ORCID})
|
||||
\item Barret Schloerke \email{barret@posit.co} (\href{https://orcid.org/0000-0001-9986-114X}{ORCID})
|
||||
\item Garrick Aden-Buie \email{garrick@adenbuie.com} (\href{https://orcid.org/0000-0002-7111-0077}{ORCID})
|
||||
\item Yihui Xie \email{yihui@posit.co}
|
||||
\item Jeff Allen
|
||||
\item Jonathan McPherson \email{jonathan@posit.co}
|
||||
@@ -37,7 +38,7 @@ Authors:
|
||||
|
||||
Other contributors:
|
||||
\itemize{
|
||||
\item Posit Software, PBC [copyright holder, funder]
|
||||
\item Posit Software, PBC (03wc8by49) [copyright holder, funder]
|
||||
\item jQuery Foundation (jQuery library and jQuery UI library) [copyright holder]
|
||||
\item jQuery contributors (jQuery library; authors listed in inst/www/shared/jquery-AUTHORS.txt) [contributor, copyright holder]
|
||||
\item jQuery UI contributors (jQuery UI library; authors listed in inst/www/shared/jqueryui/AUTHORS.txt) [contributor, copyright holder]
|
||||
|
||||
@@ -72,7 +72,7 @@ be done 100\% correctly.}
|
||||
\code{\link[=runApp]{runApp()}} for more information.}
|
||||
\item{shiny.jquery.version (defaults to \code{3})}{The major version of jQuery to use.
|
||||
Currently only values of \code{3} or \code{1} are supported. If \code{1}, then jQuery 1.12.4 is used. If \code{3},
|
||||
then jQuery 3.6.0 is used.}
|
||||
then jQuery 3.7.1 is used.}
|
||||
\item{shiny.json.digits (defaults to \code{I(16)})}{Max number of digits to use when converting
|
||||
numbers to JSON format to send to the client web browser. Use \code{\link[=I]{I()}} to specify significant digits.
|
||||
Use \code{NA} for max precision.}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"homepage": "https://shiny.rstudio.com",
|
||||
"repository": "github:rstudio/shiny",
|
||||
"name": "@types/rstudio-shiny",
|
||||
"version": "1.10.0-alpha.9001",
|
||||
"version": "1.11.1-alpha.9000",
|
||||
"license": "GPL-3.0-only",
|
||||
"main": "",
|
||||
"browser": "",
|
||||
|
||||
@@ -1,34 +1,28 @@
|
||||
# Revdeps
|
||||
|
||||
## Failed to check (20)
|
||||
|
||||
|package |version |error |warning |note |
|
||||
|:------------------|:-------|:-----|:-------|:----|
|
||||
|animalEKF |1.2 |1 | | |
|
||||
|AovBay |0.1.0 |1 | | |
|
||||
|Certara.VPCResults |3.0.2 |1 | | |
|
||||
|chipPCR |1.0-2 |1 | | |
|
||||
|ctsem |3.10.1 |1 | | |
|
||||
|dartR.sim |? | | | |
|
||||
|diveR |? | | | |
|
||||
|gap |? | | | |
|
||||
|jsmodule |? | | | |
|
||||
|loon.shiny |? | | | |
|
||||
|robmedExtra |0.1.1 |1 | | |
|
||||
|rstanarm |2.32.1 |1 | | |
|
||||
|SensMap |0.7 |1 | | |
|
||||
|Seurat |5.1.0 |1 | |1 |
|
||||
|shinyTempSignal |0.0.8 |1 | | |
|
||||
|Signac |1.14.0 |1 | | |
|
||||
|statsr |0.3.0 |1 | | |
|
||||
|TestAnaAPP |1.1.2 |1 | | |
|
||||
|tidyvpc |1.5.2 |1 | | |
|
||||
|visR |? | | | |
|
||||
|
||||
## New problems (2)
|
||||
|
||||
|package |version |error |warning |note |
|
||||
|:-------|:-------|:-----|:-------|:------|
|
||||
|[HH](problems.md#hh)|3.1-52 | | |__+1__ |
|
||||
|[PopED](problems.md#poped)|0.7.0 | | |__+1__ |
|
||||
## Failed to check (22)
|
||||
|
||||
|package |version |error |warning |note |
|
||||
|:--------------------|:-------|:-----|:-------|:----|
|
||||
|bigPint |? | | | |
|
||||
|bioCancer |? | | | |
|
||||
|EBImage |? | | | |
|
||||
|FAfA |0.3 |1 | | |
|
||||
|fio |0.1.6 |1 | | |
|
||||
|GeneNetworkBuilder |? | | | |
|
||||
|gradientPickerD3 |? | | | |
|
||||
|InterCellar |? | | | |
|
||||
|LACE |? | | | |
|
||||
|lavaan.shiny |1.2 |1 | | |
|
||||
|LDABiplots |? | | | |
|
||||
|LDAShiny |? | | | |
|
||||
|loon.shiny |? | | | |
|
||||
|MatrixQCvis |? | | | |
|
||||
|metricsgraphics |? | | | |
|
||||
|modchart |? | | | |
|
||||
|omicsViewer |? | | | |
|
||||
|RSP |0.4 |1 | | |
|
||||
|rstanarm |2.32.1 |1 | | |
|
||||
|sphereML |0.1.1 |1 | | |
|
||||
|StatTeacherAssistant |? | | | |
|
||||
|TestAnaAPP |1.1.2 |1 | | |
|
||||
|
||||
@@ -1,39 +1,19 @@
|
||||
## revdepcheck results
|
||||
|
||||
We checked 1278 reverse dependencies (1277 from CRAN + 1 from Bioconductor), comparing R CMD check results across CRAN and dev versions of this package.
|
||||
We checked 1344 reverse dependencies (1330 from CRAN + 14 from Bioconductor), comparing R CMD check results across CRAN and dev versions of this package.
|
||||
|
||||
* We saw 2 new problems
|
||||
* We failed to check 19 packages
|
||||
* We saw 0 new problems
|
||||
* We failed to check 8 packages
|
||||
|
||||
Issues with CRAN packages are summarised below.
|
||||
|
||||
### New problems
|
||||
(This reports the first line of each new failure)
|
||||
|
||||
* HH
|
||||
checking installed package size ... NOTE
|
||||
|
||||
* PopED
|
||||
checking installed package size ... NOTE
|
||||
|
||||
### Failed to check
|
||||
|
||||
* animalEKF (NA)
|
||||
* AovBay (NA)
|
||||
* Certara.VPCResults (NA)
|
||||
* chipPCR (NA)
|
||||
* ctsem (NA)
|
||||
* dartR.sim (NA)
|
||||
* diveR (NA)
|
||||
* gap (NA)
|
||||
* jsmodule (NA)
|
||||
* loon.shiny (NA)
|
||||
* robmedExtra (NA)
|
||||
* rstanarm (NA)
|
||||
* SensMap (NA)
|
||||
* Seurat (NA)
|
||||
* shinyTempSignal (NA)
|
||||
* Signac (NA)
|
||||
* statsr (NA)
|
||||
* TestAnaAPP (NA)
|
||||
* tidyvpc (NA)
|
||||
* FAfA (NA)
|
||||
* fio (NA)
|
||||
* lavaan.shiny (NA)
|
||||
* loon.shiny (NA)
|
||||
* RSP (NA)
|
||||
* rstanarm (NA)
|
||||
* sphereML (NA)
|
||||
* TestAnaAPP (NA)
|
||||
|
||||
@@ -1,49 +1 @@
|
||||
# HH
|
||||
|
||||
<details>
|
||||
|
||||
* Version: 3.1-52
|
||||
* GitHub: NA
|
||||
* Source code: https://github.com/cran/HH
|
||||
* Date/Publication: 2024-02-11 00:00:02 UTC
|
||||
* Number of recursive dependencies: 165
|
||||
|
||||
Run `revdepcheck::cloud_details(, "HH")` for more info
|
||||
|
||||
</details>
|
||||
|
||||
## Newly broken
|
||||
|
||||
* checking installed package size ... NOTE
|
||||
```
|
||||
installed size is 5.1Mb
|
||||
sub-directories of 1Mb or more:
|
||||
R 1.5Mb
|
||||
help 1.5Mb
|
||||
```
|
||||
|
||||
# PopED
|
||||
|
||||
<details>
|
||||
|
||||
* Version: 0.7.0
|
||||
* GitHub: https://github.com/andrewhooker/PopED
|
||||
* Source code: https://github.com/cran/PopED
|
||||
* Date/Publication: 2024-10-07 19:30:02 UTC
|
||||
* Number of recursive dependencies: 140
|
||||
|
||||
Run `revdepcheck::cloud_details(, "PopED")` for more info
|
||||
|
||||
</details>
|
||||
|
||||
## Newly broken
|
||||
|
||||
* checking installed package size ... NOTE
|
||||
```
|
||||
installed size is 5.5Mb
|
||||
sub-directories of 1Mb or more:
|
||||
R 1.5Mb
|
||||
doc 1.4Mb
|
||||
test 1.1Mb
|
||||
```
|
||||
|
||||
*Wow, no problems at all. :)*
|
||||
@@ -1,14 +1,22 @@
|
||||
# Using Shiny TypeScript Definitions
|
||||
|
||||
When developing TypeScript projects that use `window.Shiny`, we recommend installing the Shiny TypeScript definitions to your package. To install the latest stable definitions, call
|
||||
When developing TypeScript projects that use `window.Shiny`, we recommend installing the Shiny TypeScript definitions to your package. To install the latest stable definitions, run one of the following (depending on if you're using `npm` or `yarn`):
|
||||
|
||||
```bash
|
||||
yarn add https://github.com/rstudio/shiny\#v1.7.0
|
||||
npm install https://github.com/rstudio/shiny\#v1.10.0 --save-dev
|
||||
# or
|
||||
yarn add https://github.com/rstudio/shiny\#v1.10.0 --dev
|
||||
```
|
||||
|
||||
, matching the GitHub tag to your current the Shiny CRAN release (ex: `v1.7.0`). If you are asked to select a version of `@types/jquery`, please select the closest matching version.
|
||||
, matching the GitHub tag to your current the Shiny CRAN release (ex: `v1.10.0`). If you are asked to select a version of `@types/jquery`, please select the closest matching version.
|
||||
|
||||
This will provide a global type definition of `Shiny`, let your IDE know that `window.Shiny` is of type `Shiny`, and declare a globally available variable `Shiny` within your project. You **should not** need to import anything. Similar to `jQuery`, it should _Just Work_<sup>TM</sup>.
|
||||
This will provide a global type definition of `window.Shiny`. In your code, you can access the Shiny object via `window.Shiny` or just `Shiny`. However, note that if you are using TypeScript, it will be OK with `window.Shiny` but it will flag uses of `Shiny` (without the `window.` prefix), because TypeScript won't know that it's a global variable. We consider it better practice to use `window.Shiny` instead of `Shiny`, but if you want TypeScript to know that `Shiny` is available as a global variable, you can add the following to a TypeScript file in your code base.
|
||||
|
||||
```ts
|
||||
declare global {
|
||||
const Shiny: ShinyClass;
|
||||
}
|
||||
```
|
||||
|
||||
When loading your compiled file, it should be loaded after `shiny.js` is loaded. If you are using an `htmlDependency()` to add your code to the page, your script will automatically be loaded after has been loaded.
|
||||
|
||||
@@ -186,7 +194,7 @@ Both JavaScript files will produce a sourcemap (`**.js.map`) that the browser wi
|
||||
|
||||
### Exported types
|
||||
|
||||
`./extras/globalShiny.ts` contains global declarations to define `window.Shiny`, a globally available `Shiny` variable, and a globally available `Shiny` type. This file is in a parallel folder to `./src` to avoid `Shiny` from being globally accessable within the source code. However, this file is the default type definition when the Type definitions are installed by external developers.
|
||||
`./extras/globalShiny.ts` contains global declarations to define `window.Shiny`, a globally available `Shiny` variable, and a globally available `ShinyClass` type. This file is in a parallel folder to `./src` to avoid `Shiny` from being globally accessable within the source code. However, this file is the default type definition when the Type definitions are installed by external developers.
|
||||
|
||||
### GitHub Actions
|
||||
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import $ from "jquery";
|
||||
import type { HtmlDep } from "../../shiny/render";
|
||||
import { renderContent } from "../../shiny/render";
|
||||
import { hasDefinedProperty } from "../../utils";
|
||||
import { InputBinding } from "./inputBinding";
|
||||
|
||||
type ActionButtonReceiveMessageData = {
|
||||
label?: string;
|
||||
icon?: string | [];
|
||||
label?: { html: string; deps: HtmlDep[] };
|
||||
icon?: { html: string; deps: HtmlDep[] };
|
||||
disabled?: boolean;
|
||||
};
|
||||
|
||||
@@ -39,45 +41,40 @@ class ActionButtonInputBinding extends InputBinding {
|
||||
getState(el: HTMLElement): { value: number } {
|
||||
return { value: this.getValue(el) };
|
||||
}
|
||||
receiveMessage(el: HTMLElement, data: ActionButtonReceiveMessageData): void {
|
||||
const $el = $(el);
|
||||
|
||||
if (hasDefinedProperty(data, "label") || hasDefinedProperty(data, "icon")) {
|
||||
// retrieve current label and icon
|
||||
let label: string = $el.text();
|
||||
let icon = "";
|
||||
|
||||
// to check (and store) the previous icon, we look for a $el child
|
||||
// object that has an i tag, and some (any) class (this prevents
|
||||
// italicized text - which has an i tag but, usually, no class -
|
||||
// from being mistakenly selected)
|
||||
if ($el.find("i[class]").length > 0) {
|
||||
const iconHtml = $el.find("i[class]")[0];
|
||||
|
||||
if (iconHtml === $el.children()[0]) {
|
||||
// another check for robustness
|
||||
icon = $(iconHtml).prop("outerHTML");
|
||||
}
|
||||
async receiveMessage(
|
||||
el: HTMLElement,
|
||||
data: ActionButtonReceiveMessageData
|
||||
): Promise<void> {
|
||||
if (hasDefinedProperty(data, "icon")) {
|
||||
let iconContainer = el.querySelector<HTMLElement>(
|
||||
":scope > .action-icon"
|
||||
);
|
||||
// If no container exists yet, create one
|
||||
if (!iconContainer) {
|
||||
iconContainer = document.createElement("span");
|
||||
iconContainer.className = "action-icon";
|
||||
el.prepend(iconContainer);
|
||||
}
|
||||
await renderContent(iconContainer, data.icon);
|
||||
}
|
||||
|
||||
// update the requested properties
|
||||
if (hasDefinedProperty(data, "label")) {
|
||||
label = data.label;
|
||||
if (hasDefinedProperty(data, "label")) {
|
||||
let labelContainer = el.querySelector<HTMLElement>(
|
||||
":scope > .action-label"
|
||||
);
|
||||
if (!labelContainer) {
|
||||
labelContainer = document.createElement("span");
|
||||
labelContainer.className = "action-label";
|
||||
el.appendChild(labelContainer);
|
||||
}
|
||||
if (hasDefinedProperty(data, "icon")) {
|
||||
// `data.icon` can be an [] if user gave `character(0)`.
|
||||
icon = Array.isArray(data.icon) ? "" : data.icon ?? "";
|
||||
}
|
||||
|
||||
// produce new html
|
||||
$el.html(icon + " " + label);
|
||||
await renderContent(labelContainer, data.label);
|
||||
}
|
||||
|
||||
if (hasDefinedProperty(data, "disabled")) {
|
||||
if (data.disabled) {
|
||||
$el.attr("disabled", "");
|
||||
el.setAttribute("disabled", "");
|
||||
} else {
|
||||
$el.attr("disabled", null);
|
||||
el.removeAttribute("disabled");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
import $ from "jquery";
|
||||
import type { HtmlDep } from "../../shiny/render";
|
||||
import { renderContent } from "../../shiny/render";
|
||||
import { hasDefinedProperty } from "../../utils";
|
||||
import { InputBinding } from "./inputBinding";
|
||||
|
||||
type CheckedHTMLElement = HTMLInputElement;
|
||||
|
||||
type CheckboxChecked = CheckedHTMLElement["checked"];
|
||||
type CheckboxReceiveMessageData = { value?: CheckboxChecked; label?: string };
|
||||
type CheckboxReceiveMessageData = {
|
||||
value?: CheckboxChecked;
|
||||
label?: { html: string; deps: HtmlDep[] };
|
||||
};
|
||||
|
||||
class CheckboxInputBinding extends InputBinding {
|
||||
find(scope: HTMLElement): JQuery<HTMLElement> {
|
||||
@@ -32,10 +37,10 @@ class CheckboxInputBinding extends InputBinding {
|
||||
value: el.checked,
|
||||
};
|
||||
}
|
||||
receiveMessage(
|
||||
async receiveMessage(
|
||||
el: CheckedHTMLElement,
|
||||
data: CheckboxReceiveMessageData
|
||||
): void {
|
||||
): Promise<void> {
|
||||
if (hasDefinedProperty(data, "value")) {
|
||||
el.checked = data.value;
|
||||
}
|
||||
@@ -43,7 +48,8 @@ class CheckboxInputBinding extends InputBinding {
|
||||
// checkboxInput()'s label works different from other
|
||||
// input labels...the label container should always exist
|
||||
if (hasDefinedProperty(data, "label")) {
|
||||
$(el).parent().find("span").text(data.label);
|
||||
const labelSpan = $(el).parent().find("span");
|
||||
await renderContent(labelSpan, data.label);
|
||||
}
|
||||
|
||||
$(el).trigger("change");
|
||||
|
||||
@@ -1,6 +1,22 @@
|
||||
import type { EventPriority } from "../../inputPolicies/inputPolicy";
|
||||
import type { RatePolicyModes } from "../../inputPolicies/inputRateDecorator";
|
||||
import type { BindScope } from "../../shiny/bind";
|
||||
|
||||
type SubscribeEventPriority =
|
||||
| EventPriority
|
||||
| boolean
|
||||
| { priority: EventPriority };
|
||||
// Historically, the .subscribe()'s callback value only took a boolean. In this
|
||||
// case:
|
||||
// * false: send value immediately (i.e., priority = "immediate")
|
||||
// * true: send value later (i.e., priority = "deferred")
|
||||
// * The input rate policy is also consulted on whether to debounce or
|
||||
// throttle
|
||||
// In recent versions, the value can also be "event", meaning that the
|
||||
// value should be sent immediately regardless of whether it has changed.
|
||||
|
||||
type InputSubscribeCallback = (value: SubscribeEventPriority) => void;
|
||||
|
||||
class InputBinding {
|
||||
name!: string;
|
||||
|
||||
@@ -26,10 +42,7 @@ class InputBinding {
|
||||
el; // unused var
|
||||
}
|
||||
|
||||
// The callback method takes one argument, whose value is boolean. If true,
|
||||
// allow deferred (debounce or throttle) sending depending on the value of
|
||||
// getRatePolicy. If false, send value immediately. Default behavior is `false`
|
||||
subscribe(el: HTMLElement, callback: (value: boolean) => void): void {
|
||||
subscribe(el: HTMLElement, callback: InputSubscribeCallback): void {
|
||||
// empty
|
||||
el; // unused var
|
||||
callback; // unused var
|
||||
@@ -102,3 +115,4 @@ class InputBinding {
|
||||
//// END NOTES FOR FUTURE DEV
|
||||
|
||||
export { InputBinding };
|
||||
export type { InputSubscribeCallback, SubscribeEventPriority };
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import $ from "jquery";
|
||||
import { Shiny } from "..";
|
||||
import type { InputBinding, OutputBinding } from "../bindings";
|
||||
import type { SubscribeEventPriority } from "../bindings/input/inputBinding";
|
||||
import { OutputBindingAdapter } from "../bindings/outputAdapter";
|
||||
import type { BindingRegistry } from "../bindings/registry";
|
||||
import { ShinyClientMessageEvent } from "../components/errorConsole";
|
||||
@@ -8,6 +9,7 @@ import type {
|
||||
InputRateDecorator,
|
||||
InputValidateDecorator,
|
||||
} from "../inputPolicies";
|
||||
import type { EventPriority } from "../inputPolicies/inputPolicy";
|
||||
import { shinyAppBindOutput, shinyAppUnbindOutput } from "./initedMethods";
|
||||
import { sendImageSizeFns } from "./sendImageSize";
|
||||
|
||||
@@ -27,7 +29,7 @@ function valueChangeCallback(
|
||||
inputs: InputValidateDecorator,
|
||||
binding: InputBinding,
|
||||
el: HTMLElement,
|
||||
allowDeferred: boolean
|
||||
priority?: SubscribeEventPriority
|
||||
) {
|
||||
let id = binding.getId(el);
|
||||
|
||||
@@ -37,20 +39,32 @@ function valueChangeCallback(
|
||||
|
||||
if (type) id = id + ":" + type;
|
||||
|
||||
const opts: {
|
||||
priority: "deferred" | "immediate";
|
||||
binding: typeof binding;
|
||||
el: typeof el;
|
||||
} = {
|
||||
priority: allowDeferred ? "deferred" : "immediate",
|
||||
binding: binding,
|
||||
el: el,
|
||||
};
|
||||
// Normalize the priority to a valid EventPriority
|
||||
const normalizedPriority = normalizeEventPriority(priority);
|
||||
|
||||
inputs.setInput(id, value, opts);
|
||||
inputs.setInput(id, value, { priority: normalizedPriority, binding, el });
|
||||
}
|
||||
}
|
||||
|
||||
// Narrow the type of the subscribe callback value to EventPriority
|
||||
function normalizeEventPriority(
|
||||
priority?: SubscribeEventPriority
|
||||
): EventPriority {
|
||||
if (priority === false || priority === undefined) {
|
||||
return "immediate";
|
||||
}
|
||||
|
||||
if (priority === true) {
|
||||
return "deferred";
|
||||
}
|
||||
|
||||
if (typeof priority === "object" && "priority" in priority) {
|
||||
return priority.priority;
|
||||
}
|
||||
|
||||
return priority;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registry for input and output binding IDs. Used to check for duplicate IDs
|
||||
* and to keep track of which IDs have already been added to the app. Use an
|
||||
@@ -272,8 +286,8 @@ function bindInputs(
|
||||
const thisBinding = binding;
|
||||
const thisEl = el;
|
||||
|
||||
return function (allowDeferred: boolean) {
|
||||
valueChangeCallback(inputs, thisBinding, thisEl, allowDeferred);
|
||||
return function (priority?: SubscribeEventPriority) {
|
||||
valueChangeCallback(inputs, thisBinding, thisEl, priority);
|
||||
};
|
||||
})();
|
||||
|
||||
|
||||
@@ -82,7 +82,7 @@ class ShinyClass {
|
||||
addCustomMessageHandler: typeof addCustomMessageHandler;
|
||||
|
||||
// The following are added in the initialization, by initShiny()
|
||||
createSocket?: () => WebSocket;
|
||||
createSocket?: () => Promise<WebSocket>;
|
||||
user?: string;
|
||||
progressHandlers?: ShinyApp["progressHandlers"];
|
||||
shinyapp?: ShinyApp;
|
||||
@@ -669,7 +669,7 @@ class ShinyClass {
|
||||
|
||||
// We've collected all the initial values--start the server process!
|
||||
inputsNoResend.reset(initialValues);
|
||||
shinyapp.connect(initialValues);
|
||||
await shinyapp.connect(initialValues);
|
||||
$(document).one("shiny:connected", () => {
|
||||
initDeferredIframes();
|
||||
});
|
||||
|
||||
@@ -92,7 +92,7 @@ function setFileInputBinding(fileInputBinding_: FileInputBinding): void {
|
||||
fileInputBinding = fileInputBinding_;
|
||||
}
|
||||
|
||||
function getShinyCreateWebsocket(): (() => WebSocket) | void {
|
||||
function getShinyCreateWebsocket(): (() => Promise<WebSocket>) | void {
|
||||
return validateShinyHasBeenSet().createSocket;
|
||||
}
|
||||
|
||||
|
||||
@@ -159,11 +159,11 @@ class ShinyApp {
|
||||
this._init();
|
||||
}
|
||||
|
||||
connect(initialInput: InputValues): void {
|
||||
async connect(initialInput: InputValues): Promise<void> {
|
||||
if (this.$socket)
|
||||
throw "Connect was already called on this application object";
|
||||
|
||||
this.$socket = this.createSocket();
|
||||
this.$socket = await this.createSocket();
|
||||
this.$initialInput = initialInput;
|
||||
$.extend(this.$inputValues, initialInput);
|
||||
|
||||
@@ -176,7 +176,7 @@ class ShinyApp {
|
||||
|
||||
private scheduledReconnect: number | undefined = undefined;
|
||||
|
||||
reconnect(): void {
|
||||
async reconnect(): Promise<void> {
|
||||
// This function can be invoked directly even if there's a scheduled
|
||||
// reconnect, so be sure to clear any such scheduled reconnects.
|
||||
clearTimeout(this.scheduledReconnect);
|
||||
@@ -184,15 +184,15 @@ class ShinyApp {
|
||||
if (this.isConnected())
|
||||
throw "Attempted to reconnect, but already connected.";
|
||||
|
||||
this.$socket = this.createSocket();
|
||||
this.$socket = await this.createSocket();
|
||||
this.$initialInput = $.extend({}, this.$inputValues);
|
||||
this.$updateConditionals();
|
||||
}
|
||||
|
||||
createSocket(): ShinyWebSocket {
|
||||
const createSocketFunc: () => ShinyWebSocket =
|
||||
async createSocket(): Promise<ShinyWebSocket> {
|
||||
const createSocketFunc: () => Promise<ShinyWebSocket> =
|
||||
getShinyCreateWebsocket() ||
|
||||
(() => {
|
||||
(async () => {
|
||||
let protocol = "ws:";
|
||||
|
||||
if (window.location.protocol === "https:") protocol = "wss:";
|
||||
@@ -211,6 +211,12 @@ class ShinyApp {
|
||||
if (!/\/$/.test(defaultPath)) defaultPath += "/";
|
||||
defaultPath += "websocket/";
|
||||
|
||||
const sessionId = await this.$getSessionId();
|
||||
if (sessionId) {
|
||||
defaultPath += "?session_id=" + sessionId;
|
||||
}
|
||||
|
||||
// Fail gracefully if the server does not support this endpoint
|
||||
const ws: ShinyWebSocket = new WebSocket(
|
||||
protocol + "//" + window.location.host + defaultPath
|
||||
);
|
||||
@@ -220,7 +226,7 @@ class ShinyApp {
|
||||
return ws;
|
||||
});
|
||||
|
||||
const socket = createSocketFunc();
|
||||
const socket = await createSocketFunc();
|
||||
let hasOpened = false;
|
||||
|
||||
socket.onopen = () => {
|
||||
@@ -277,6 +283,39 @@ class ShinyApp {
|
||||
return socket;
|
||||
}
|
||||
|
||||
async $getSessionId(): Promise<string | null> {
|
||||
// If the session ID has been received through a websocket message,
|
||||
// no need to create a new one.
|
||||
if (this.config) {
|
||||
return this.config.sessionId;
|
||||
}
|
||||
|
||||
// Fetch this client's session ID from the server. When successful, the
|
||||
// server will require a (valid) session ID when establishing a websocket
|
||||
// connection.
|
||||
try {
|
||||
const response = await fetch("/new_shiny_session/");
|
||||
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
return data.session_id;
|
||||
}
|
||||
|
||||
// If the endpoint doesn't exist, then a session ID isn't required
|
||||
// to establish a websocket connection.
|
||||
if (response.status === 404) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// If other problems happen, we probably want to know about it.
|
||||
console.error("Error fetching session ID:", response);
|
||||
return null;
|
||||
} catch (error) {
|
||||
console.error("Error fetching session ID:", error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
async startActionQueueLoop(): Promise<void> {
|
||||
// eslint-disable-next-line no-constant-condition
|
||||
while (true) {
|
||||
@@ -314,8 +353,8 @@ class ShinyApp {
|
||||
}
|
||||
|
||||
$scheduleReconnect(delay: Parameters<typeof setTimeout>[1]): void {
|
||||
this.scheduledReconnect = window.setTimeout(() => {
|
||||
this.reconnect();
|
||||
this.scheduledReconnect = window.setTimeout(async () => {
|
||||
await this.reconnect();
|
||||
}, delay);
|
||||
}
|
||||
|
||||
|
||||
13
srcts/types/src/bindings/input/actionbutton.d.ts
vendored
13
srcts/types/src/bindings/input/actionbutton.d.ts
vendored
@@ -1,7 +1,14 @@
|
||||
import type { HtmlDep } from "../../shiny/render";
|
||||
import { InputBinding } from "./inputBinding";
|
||||
type ActionButtonReceiveMessageData = {
|
||||
label?: string;
|
||||
icon?: string | [];
|
||||
label?: {
|
||||
html: string;
|
||||
deps: HtmlDep[];
|
||||
};
|
||||
icon?: {
|
||||
html: string;
|
||||
deps: HtmlDep[];
|
||||
};
|
||||
disabled?: boolean;
|
||||
};
|
||||
declare class ActionButtonInputBinding extends InputBinding {
|
||||
@@ -13,7 +20,7 @@ declare class ActionButtonInputBinding extends InputBinding {
|
||||
getState(el: HTMLElement): {
|
||||
value: number;
|
||||
};
|
||||
receiveMessage(el: HTMLElement, data: ActionButtonReceiveMessageData): void;
|
||||
receiveMessage(el: HTMLElement, data: ActionButtonReceiveMessageData): Promise<void>;
|
||||
unsubscribe(el: HTMLElement): void;
|
||||
}
|
||||
export { ActionButtonInputBinding };
|
||||
|
||||
8
srcts/types/src/bindings/input/checkbox.d.ts
vendored
8
srcts/types/src/bindings/input/checkbox.d.ts
vendored
@@ -1,9 +1,13 @@
|
||||
import type { HtmlDep } from "../../shiny/render";
|
||||
import { InputBinding } from "./inputBinding";
|
||||
type CheckedHTMLElement = HTMLInputElement;
|
||||
type CheckboxChecked = CheckedHTMLElement["checked"];
|
||||
type CheckboxReceiveMessageData = {
|
||||
value?: CheckboxChecked;
|
||||
label?: string;
|
||||
label?: {
|
||||
html: string;
|
||||
deps: HtmlDep[];
|
||||
};
|
||||
};
|
||||
declare class CheckboxInputBinding extends InputBinding {
|
||||
find(scope: HTMLElement): JQuery<HTMLElement>;
|
||||
@@ -15,7 +19,7 @@ declare class CheckboxInputBinding extends InputBinding {
|
||||
label: string;
|
||||
value: CheckboxChecked;
|
||||
};
|
||||
receiveMessage(el: CheckedHTMLElement, data: CheckboxReceiveMessageData): void;
|
||||
receiveMessage(el: CheckedHTMLElement, data: CheckboxReceiveMessageData): Promise<void>;
|
||||
}
|
||||
export { CheckboxInputBinding };
|
||||
export type { CheckedHTMLElement, CheckboxReceiveMessageData };
|
||||
|
||||
@@ -1,12 +1,17 @@
|
||||
import type { EventPriority } from "../../inputPolicies/inputPolicy";
|
||||
import type { RatePolicyModes } from "../../inputPolicies/inputRateDecorator";
|
||||
import type { BindScope } from "../../shiny/bind";
|
||||
type SubscribeEventPriority = EventPriority | boolean | {
|
||||
priority: EventPriority;
|
||||
};
|
||||
type InputSubscribeCallback = (value: SubscribeEventPriority) => void;
|
||||
declare class InputBinding {
|
||||
name: string;
|
||||
find(scope: BindScope): JQuery<HTMLElement>;
|
||||
getId(el: HTMLElement): string;
|
||||
getType(el: HTMLElement): string | null;
|
||||
getValue(el: HTMLElement): any;
|
||||
subscribe(el: HTMLElement, callback: (value: boolean) => void): void;
|
||||
subscribe(el: HTMLElement, callback: InputSubscribeCallback): void;
|
||||
unsubscribe(el: HTMLElement): void;
|
||||
receiveMessage(el: HTMLElement, data: unknown): Promise<void> | void;
|
||||
getState(el: HTMLElement): unknown;
|
||||
@@ -18,3 +23,4 @@ declare class InputBinding {
|
||||
dispose(el: HTMLElement): void;
|
||||
}
|
||||
export { InputBinding };
|
||||
export type { InputSubscribeCallback, SubscribeEventPriority };
|
||||
|
||||
2
srcts/types/src/shiny/index.d.ts
vendored
2
srcts/types/src/shiny/index.d.ts
vendored
@@ -35,7 +35,7 @@ declare class ShinyClass {
|
||||
renderHtmlAsync: typeof renderHtmlAsync;
|
||||
renderHtml: typeof renderHtml;
|
||||
addCustomMessageHandler: typeof addCustomMessageHandler;
|
||||
createSocket?: () => WebSocket;
|
||||
createSocket?: () => Promise<WebSocket>;
|
||||
user?: string;
|
||||
progressHandlers?: ShinyApp["progressHandlers"];
|
||||
shinyapp?: ShinyApp;
|
||||
|
||||
2
srcts/types/src/shiny/initedMethods.d.ts
vendored
2
srcts/types/src/shiny/initedMethods.d.ts
vendored
@@ -19,5 +19,5 @@ declare function shinyAppUnbindOutput(id: string, binding: OutputBindingAdapter)
|
||||
declare function getShinyOnCustomMessage(): Handler | null;
|
||||
declare function getFileInputBinding(): FileInputBinding;
|
||||
declare function setFileInputBinding(fileInputBinding_: FileInputBinding): void;
|
||||
declare function getShinyCreateWebsocket(): (() => WebSocket) | void;
|
||||
declare function getShinyCreateWebsocket(): (() => Promise<WebSocket>) | void;
|
||||
export { setShinyObj, shinySetInputValue, shinyShinyApp, setShinyUser, shinyForgetLastInputValue, shinyBindAll, shinyUnbindAll, shinyInitializeInputs, shinyAppBindOutput, shinyAppUnbindOutput, getShinyOnCustomMessage, getFileInputBinding, setFileInputBinding, getShinyCreateWebsocket, };
|
||||
|
||||
7
srcts/types/src/shiny/shinyapp.d.ts
vendored
7
srcts/types/src/shiny/shinyapp.d.ts
vendored
@@ -52,11 +52,12 @@ declare class ShinyApp {
|
||||
$nextRequestId: number;
|
||||
$allowReconnect: boolean | "force";
|
||||
constructor();
|
||||
connect(initialInput: InputValues): void;
|
||||
connect(initialInput: InputValues): Promise<void>;
|
||||
isConnected(): boolean;
|
||||
private scheduledReconnect;
|
||||
reconnect(): void;
|
||||
createSocket(): ShinyWebSocket;
|
||||
reconnect(): Promise<void>;
|
||||
createSocket(): Promise<ShinyWebSocket>;
|
||||
$getSessionId(): Promise<string | null>;
|
||||
startActionQueueLoop(): Promise<void>;
|
||||
sendInput(values: InputValues): void;
|
||||
$notifyDisconnected(): void;
|
||||
|
||||
21
tests/testthat/_snaps/actionButton.md
Normal file
21
tests/testthat/_snaps/actionButton.md
Normal file
@@ -0,0 +1,21 @@
|
||||
# Action button allows icon customization
|
||||
|
||||
Code
|
||||
actionButton("foo", "Click me")
|
||||
Output
|
||||
<button id="foo" type="button" class="btn btn-default action-button">
|
||||
<span class="action-label">Click me</span>
|
||||
</button>
|
||||
|
||||
---
|
||||
|
||||
Code
|
||||
actionButton("foo", "Click me", icon = icon("star"))
|
||||
Output
|
||||
<button id="foo" type="button" class="btn btn-default action-button">
|
||||
<span class="action-icon">
|
||||
<i class="far fa-star" role="presentation" aria-label="star icon"></i>
|
||||
</span>
|
||||
<span class="action-label">Click me</span>
|
||||
</button>
|
||||
|
||||
@@ -55,3 +55,42 @@ test_that("Action link accepts class arguments", {
|
||||
get_class(make_link("extra extra2")), sub("\"$", " extra extra2\"", act_class)
|
||||
)
|
||||
})
|
||||
|
||||
test_that("Action button allows icon customization", {
|
||||
# No separator between icon and label
|
||||
expect_snapshot(actionButton("foo", "Click me"))
|
||||
|
||||
# Should include separator between icon and label
|
||||
expect_snapshot(
|
||||
actionButton("foo", "Click me", icon = icon("star"))
|
||||
)
|
||||
|
||||
# Warn on a non-HTML icon
|
||||
expect_warning(
|
||||
actionButton("foo", "Click me", icon = "not an icon"),
|
||||
"non-HTML value was provided"
|
||||
)
|
||||
|
||||
# Allows for arbitrary HTML as icon
|
||||
btn <- expect_no_warning(
|
||||
actionButton("foo", "Click me", icon = tags$svg())
|
||||
)
|
||||
btn2 <- expect_no_warning(
|
||||
actionButton("foo", "Click me", icon = tagList(tags$svg()))
|
||||
)
|
||||
btn3 <- expect_no_warning(
|
||||
actionButton("foo", "Click me", icon = list(tags$svg()))
|
||||
)
|
||||
btn4 <- expect_no_warning(
|
||||
actionButton("foo", "Click me", icon = HTML("<svg></svg>"))
|
||||
)
|
||||
|
||||
# Ignore newlines+indentation for comparison
|
||||
as_character <- function(x) {
|
||||
gsub("\\n\\s*", "", as.character(x))
|
||||
}
|
||||
|
||||
expect_equal(as_character(btn), as_character(btn2))
|
||||
expect_equal(as_character(btn2), as_character(btn3))
|
||||
expect_equal(as_character(btn3), as_character(btn4))
|
||||
})
|
||||
|
||||
@@ -921,7 +921,7 @@ __metadata:
|
||||
peerDependenciesMeta:
|
||||
jquery-ui:
|
||||
optional: true
|
||||
checksum: 8718ebda1068894fc1267459b603f492045723ed1000fdbe798f2fab78fed8536b1906f56c53e9bd0ff9dce24aed176045618d0a1eddcf48f7d0313ad4ad67e9
|
||||
checksum: ba260ba5804c16b1455ff79f9d00ce860e12ae36e29d7a5f702da6b384c9454497421b8e06fe683d10fac53e2dc6ec008da4fa129a153cbbfe5396e027eb4247
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
||||
Reference in New Issue
Block a user