Compare commits
79 Commits
webr-next
...
fix-routin
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
13b4612f1c | ||
|
|
8c9d101100 | ||
|
|
bfd5b1470e | ||
|
|
62ea24aa68 | ||
|
|
9b53251b09 | ||
|
|
942bdd8c40 | ||
|
|
d762865753 | ||
|
|
992b967095 | ||
|
|
9a39cea0cc | ||
|
|
db9f210257 | ||
|
|
e8b7c08a19 | ||
|
|
b596245571 | ||
|
|
57bb3a12d3 | ||
|
|
219fbc6819 | ||
|
|
a660093fa5 | ||
|
|
eac0eea886 | ||
|
|
6df0bb9423 | ||
|
|
159e771ac7 | ||
|
|
ca41c0831b | ||
|
|
316c3c8409 | ||
|
|
f79a22b987 | ||
|
|
83219e3551 | ||
|
|
f55c26af4a | ||
|
|
9fbb2c5829 | ||
|
|
531f31b66f | ||
|
|
58e152154a | ||
|
|
55b37fdeb3 | ||
|
|
b8a5aef53a | ||
|
|
d764ea9b4e | ||
|
|
8ad779f949 | ||
|
|
7642fc84b7 | ||
|
|
0952f3e0a7 | ||
|
|
13ca8dfc57 | ||
|
|
79f42f5846 | ||
|
|
9a35b01e23 | ||
|
|
5bf0701939 | ||
|
|
e5083f4938 | ||
|
|
ce6a562a3c | ||
|
|
b6bcfc8683 | ||
|
|
d37beeece7 | ||
|
|
79ee25620f | ||
|
|
82c678a1eb | ||
|
|
458924569a | ||
|
|
501b012b2b | ||
|
|
ee1aac847a | ||
|
|
7785a76a67 | ||
|
|
79af1d6c92 | ||
|
|
a145add5d4 | ||
|
|
abf71389be | ||
|
|
2e2114f99d | ||
|
|
09d415502f | ||
|
|
c489fef4ff | ||
|
|
9d12b0fca7 | ||
|
|
cc9b9d4e6a | ||
|
|
34f9e4484d | ||
|
|
03a3f8f886 | ||
|
|
b900db0c74 | ||
|
|
5fb3ebc2d9 | ||
|
|
fbc6b2df57 | ||
|
|
6208225354 | ||
|
|
e22b693418 | ||
|
|
c7ca49c634 | ||
|
|
d84aa94762 | ||
|
|
89e2c18531 | ||
|
|
43d36c08dc | ||
|
|
4bc330e5dd | ||
|
|
56ab530d87 | ||
|
|
599209a036 | ||
|
|
15b5fa6c01 | ||
|
|
3f4676d9a6 | ||
|
|
bb89cf9235 | ||
|
|
25c40967da | ||
|
|
068b232e75 | ||
|
|
0b7fda707e | ||
|
|
9fd4ba199e | ||
|
|
43e40c7969 | ||
|
|
248f19333c | ||
|
|
306c4f847b | ||
|
|
e689cdc522 |
@@ -26,7 +26,6 @@
|
||||
^\.vscode$
|
||||
^\.madgerc$
|
||||
^\.prettierrc\.yml$
|
||||
^babel\.config\.json$
|
||||
^jest\.config\.js$
|
||||
^package\.json$
|
||||
^tsconfig\.json$
|
||||
@@ -37,3 +36,4 @@
|
||||
^\.browserslistrc$
|
||||
^\.eslintrc\.yml$
|
||||
^\.yarnrc\.yml$
|
||||
^_dev$
|
||||
|
||||
@@ -35,10 +35,6 @@ rules:
|
||||
|
||||
default-case:
|
||||
- error
|
||||
indent:
|
||||
- error
|
||||
- 2
|
||||
- SwitchCase: 1
|
||||
linebreak-style:
|
||||
- error
|
||||
- unix
|
||||
|
||||
4
.github/ISSUE_TEMPLATE/question.md
vendored
@@ -1,7 +1,7 @@
|
||||
---
|
||||
name : Ask a Question
|
||||
about : The issue tracker is not for questions -- please ask questions at https://community.rstudio.com/c/shiny.
|
||||
about : The issue tracker is not for questions -- please ask questions at https://forum.posit.co/tags/shiny.
|
||||
---
|
||||
|
||||
The issue tracker is not for questions. If you have a question, please feel free to ask it on our community site, at https://community.rstudio.com/c/shiny.
|
||||
The issue tracker is not for questions. If you have a question, please feel free to ask it on our community site, at https://forum.posit.co/c/shiny.
|
||||
|
||||
|
||||
1
.vscode/extensions.json
vendored
@@ -1,6 +1,5 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"arcanis.vscode-zipfs",
|
||||
"dbaeumer.vscode-eslint",
|
||||
"esbenp.prettier-vscode"
|
||||
]
|
||||
|
||||
22
DESCRIPTION
@@ -1,7 +1,7 @@
|
||||
Package: shiny
|
||||
Type: Package
|
||||
Title: Web Application Framework for R
|
||||
Version: 1.8.1.9001
|
||||
Version: 1.10.0.9001
|
||||
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"),
|
||||
@@ -60,7 +60,7 @@ Authors@R: c(
|
||||
comment = "showdown.js library"),
|
||||
person("Ivan", "Sagalaev", role = c("ctb", "cph"),
|
||||
comment = "highlight.js library"),
|
||||
person(family = "R Core Team", role = c("ctb", "cph"),
|
||||
person(given = "R Core Team", role = c("ctb", "cph"),
|
||||
comment = "tar implementation from R")
|
||||
)
|
||||
Description: Makes it incredibly easy to build interactive web
|
||||
@@ -83,22 +83,23 @@ Imports:
|
||||
R6 (>= 2.0),
|
||||
sourcetools,
|
||||
later (>= 1.0.0),
|
||||
promises (>= 1.1.0),
|
||||
promises (>= 1.3.2),
|
||||
tools,
|
||||
crayon,
|
||||
cli,
|
||||
rlang (>= 0.4.10),
|
||||
fastmap (>= 1.1.1),
|
||||
withr,
|
||||
commonmark (>= 1.7),
|
||||
glue (>= 1.3.2),
|
||||
bslib (>= 0.3.0),
|
||||
cachem,
|
||||
bslib (>= 0.6.0),
|
||||
cachem (>= 1.1.0),
|
||||
lifecycle (>= 0.2.0)
|
||||
Suggests:
|
||||
coro (>= 1.1.0),
|
||||
datasets,
|
||||
DT,
|
||||
Cairo (>= 1.5-5),
|
||||
testthat (>= 3.0.0),
|
||||
testthat (>= 3.2.1),
|
||||
knitr (>= 1.6),
|
||||
markdown,
|
||||
rmarkdown,
|
||||
@@ -106,11 +107,13 @@ Suggests:
|
||||
reactlog (>= 1.0.0),
|
||||
magrittr,
|
||||
yaml,
|
||||
mirai,
|
||||
future,
|
||||
dygraphs,
|
||||
ragg,
|
||||
showtext,
|
||||
sass
|
||||
sass,
|
||||
watcher
|
||||
URL: https://shiny.posit.co/,
|
||||
https://github.com/rstudio/shiny
|
||||
BugReports: https://github.com/rstudio/shiny/issues
|
||||
@@ -206,10 +209,9 @@ Collate:
|
||||
'version_selectize.R'
|
||||
'version_strftime.R'
|
||||
'viewer.R'
|
||||
RoxygenNote: 7.3.1
|
||||
RoxygenNote: 7.3.2
|
||||
Encoding: UTF-8
|
||||
Roxygen: list(markdown = TRUE)
|
||||
RdMacros: lifecycle
|
||||
Config/testthat/edition: 3
|
||||
Config/Needs/check:
|
||||
shinytest2
|
||||
|
||||
@@ -216,6 +216,7 @@ export(reactiveVal)
|
||||
export(reactiveValues)
|
||||
export(reactiveValuesToList)
|
||||
export(reactlog)
|
||||
export(reactlogAddMark)
|
||||
export(reactlogReset)
|
||||
export(reactlogShow)
|
||||
export(registerInputHandler)
|
||||
|
||||
121
NEWS.md
@@ -1,16 +1,122 @@
|
||||
# shiny (development version)
|
||||
|
||||
## 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)
|
||||
|
||||
## 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)
|
||||
|
||||
## Improvements
|
||||
|
||||
* When auto-reload is enabled, Shiny now reloads the entire app when support files, like Shiny modules, additional script files, or web assets, change. To enable auto-reload, call `devmode(TRUE)` to enable Shiny's developer mode, or set `options(shiny.autoreload = TRUE)` to specifically enable auto-reload. You can choose which files are watched for changes with the `shiny.autoreload.pattern` option. (#4184)
|
||||
|
||||
* When busy indicators are enabled (i.e., `useBusyIndicators()`), Shiny now:
|
||||
* Shows a spinner on recalculating htmlwidgets that have previously rendered an error (including `req()` and `validate()`). (#4172)
|
||||
* 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's Typescript assets are now compiled to ES2021 instead of ES5. (#4066)
|
||||
|
||||
* `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)
|
||||
|
||||
* Upgraded jQuery from 3.6.0 to 3.7.1. (#3969)
|
||||
|
||||
* Updated jQuery UI from 1.13.2 to 1.14.1. (#4175)
|
||||
|
||||
## 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)
|
||||
|
||||
* Fixed an issue with `ExtendedTask` where synchronous errors would cause an error that would stop the current session. (#4225)
|
||||
|
||||
* `shiny::shinyAppTemplate()` no longer errors without a call to `library(shiny)`. (#3870)
|
||||
|
||||
# shiny 1.10.0
|
||||
|
||||
## New features and improvements
|
||||
|
||||
* Added new functions, `useBusyIndicators()` and `busyIndicatorOptions()`, for enabling and customizing busy indication. Busy indicators provide a visual cue to users when the server is busy calculating outputs or otherwise serving requests to the client. When enabled, a spinner is shown on each calculating/recalculating output, and a pulsing banner is shown at the top of the page when the app is otherwise busy. (#4040)
|
||||
* When busy indicators are enabled (i.e., `useBusyIndicators()` is in the UI), Shiny now:
|
||||
* Shows the pulse indicator when dynamic UI elements are recalculating and no other spinners are visible in the app. (#4137)
|
||||
* Makes the pulse indicator slightly smaller by default and improves its appearance to better blend with any background. (#4122)
|
||||
|
||||
* Improve collection of deep stack traces (stack traces that are tracked across steps in an async promise chain) with `{coro}` async generators such as `{elmer}` chat streams. Previously, Shiny treated each iteration of an async generator as a distinct deep stack, leading to pathologically long stack traces; now, Shiny only keeps/prints unique deep stack trace, discarding duplicates. (#4156)
|
||||
|
||||
* Added an example to the `ExtendedTask` documentation. (@daattali #4087)
|
||||
|
||||
## Bug fixes
|
||||
|
||||
* Fixed a bug in `conditionalPanel()` that would cause the panel to repeatedly show/hide itself when the provided condition was not boolean. (@kamilzyla, #4127)
|
||||
|
||||
* Fixed a bug with `sliderInput()` when used as a range slider that made it impossible to change the slider value when both handles were at the maximum value. (#4131)
|
||||
|
||||
* `dateInput()` and `dateRangeInput()` no longer send immediate updates to the server when the user is typing a date input. Instead, it waits until the user presses Enter or clicks out of the field to send the update, avoiding spurious and incorrect date values. Note that an update is still sent immediately when the field is cleared. (#3664)
|
||||
|
||||
* Fixed a bug in `onBookmark()` hook that caused elements to not be excluded from URL bookmarking. (#3762)
|
||||
|
||||
* Fixed a bug with stack trace capturing that caused reactives with very long async promise chains (hundreds/thousands of steps) to become extremely slow. Chains this long are unlikely to be written by hand, but `{coro}` async generators and `{elmer}` async streaming were easily creating problematically long chains. (#4155)
|
||||
|
||||
* Duplicate input and output IDs -- e.g. using `"debug"` for two inputs or two outputs -- or shared IDs -- e.g. using `"debug"` as the `inputId` for an input and an output -- now result in a console warning message, but not an error. When `devmode()` is enabled, an informative message is shown in the Shiny Client Console. We recommend all Shiny devs enable `devmode()` when developing Shiny apps locally. (#4101)
|
||||
|
||||
* Updating the choices of a `selectizeInput()` via `updateSelectizeInput()` with `server = TRUE` no longer retains the selected choice as a deselected option if the current value is not part of the new choices. (@dvg-p4 #4142)
|
||||
|
||||
* Fixed a bug where stack traces from `observeEvent()` were being stripped of stack frames too aggressively. (#4163)
|
||||
|
||||
# shiny 1.9.1
|
||||
|
||||
## Bug fixes
|
||||
|
||||
* Fixed a bug introduced in v1.9.0 where the boundaries of hover/click/brush regions on plots were being incorrectly scaled when browser zoom was used. (#4111)
|
||||
|
||||
# shiny 1.9.0
|
||||
|
||||
## New busy indication feature
|
||||
|
||||
Add the new `useBusyIndicators()` function to any UI definition to:
|
||||
1. Add a spinner overlay on calculating/recalculating outputs.
|
||||
2. Show a page-level pulsing banner when Shiny is busy calculating something (e.g., a download, side-effect, etc), but no calculating/recalculating outputs are visible.
|
||||
|
||||
In a future version of Shiny, busy indication will be enabled by default, so we encourage you to try it out now, provide feedback, and report any issues.
|
||||
|
||||
In addition, various properties of the spinners and pulse can be customized with `busyIndicatorOptions()`. For more details, see `?busyIndicatorOptions`. (#4040, #4104)
|
||||
|
||||
## New features and improvements
|
||||
|
||||
* The client-side TypeScript code for Shiny has been refactored so that the `Shiny` object is now an instance of class `ShinyClass`. (#4063)
|
||||
|
||||
* In TypeScript, the `Shiny` object has a new property `initializedPromise`, which is a Promise-like object that can be `await`ed or chained with `.then()`. This Promise-like object corresponds to the `shiny:sessioninitialized` JavaScript event, but is easier to use because it can be used both before and after the events have occurred. (#4063)
|
||||
|
||||
* Output bindings now include the `.recalculating` CSS class when they are first bound, up until the first render. This makes it possible/easier to show progress indication when the output is calculating for the first time. (#4039)
|
||||
|
||||
* A new `shiny.client_devmode` option controls client-side devmode features, in particular the client-side error console introduced in shiny 1.8.1, independently of the R-side features of `shiny::devmode()`. This usage is primarily intended for automatic use in Shinylive. (#4073)
|
||||
|
||||
* Added function `reactlogAddMark()` to programmatically add _mark_ed locations in the reactlog log without the requirement of keyboard bindings during an idle reactive moment. (#4103)
|
||||
|
||||
## Bug fixes
|
||||
|
||||
* `downloadButton()` and `downloadLink()` are now disabled up until they are fully initialized. This prevents the user from clicking the button/link before the download is ready. (#4041)
|
||||
|
||||
* Output bindings that are removed, invalidated, then inserted again (while invalidated) now correctly include the `.recalculating` CSS class. (#4039)
|
||||
|
||||
* Fixed a recent issue with `uiOutput()` and `conditionalPanel()` not properly lower opacity when recalculation (in a Bootstrap 5 context). (#4027)
|
||||
|
||||
* Image outputs that were scaled by CSS had certain regions that were unresponsive to hover/click/brush handlers. (#3234)
|
||||
|
||||
# shiny 1.8.1.1
|
||||
|
||||
* In v1.8.1, shiny.js starting throwing an error when input/output bindings have duplicate IDs. This error is now only thrown when `shiny::devmode(TRUE)` is enabled, so the issue is still made discoverable through the JS error console, but avoids unnecessarily breaking apps that happen to work with duplicate IDs. (#4019)
|
||||
@@ -23,7 +129,7 @@
|
||||
|
||||
* Added a JavaScript error dialog, reporting errors that previously were only discoverable by opening the browser's devtools open. Since this dialog is mainly useful for debugging and development, it must be enabled with `shiny::devmode()`. (#3931)
|
||||
|
||||
* `runExamples()` now uses the `{bslib}` package to generate a better looking result. It also gains a `package` argument so that other packages can leverage this same function to run Shiny app examples. For more, see `?runExamples`. (#3963, #4005)
|
||||
* `runExample()` now uses the `{bslib}` package to generate a better looking result. It also gains a `package` argument so that other packages can leverage this same function to run Shiny app examples. For more, see `?runExample`. (#3963, #4005)
|
||||
|
||||
* Added `onUnhandledError()` to register a function that will be called when an unhandled error occurs in a Shiny app. Note that this handler doesn't stop the error or prevent the session from closing, but it can be used to log the error or to clean up session-specific resources. (thanks @JohnCoene, #3993)
|
||||
|
||||
@@ -100,7 +206,6 @@
|
||||
|
||||
* Fixed #3833: When `width` is provided to `textAreaInput()`, we now correctly set the width of the `<textarea>` element. (#3838)
|
||||
|
||||
|
||||
# shiny 1.7.4.1
|
||||
|
||||
## Full changelog
|
||||
@@ -993,7 +1098,7 @@ Shiny can now display notifications on the client browser by using the `showNoti
|
||||
<img src="http://shiny.rstudio.com/images/notification.png" alt="notification" width="50%"/>
|
||||
</p>
|
||||
|
||||
[Here](https://shiny.rstudio.com/articles/notifications.html)'s our article about it, and the [reference documentation](https://shiny.rstudio.com/reference/shiny/latest/showNotification.html).
|
||||
[Here](https://shiny.rstudio.com/articles/notifications.html)'s our article about it, and the [reference documentation](https://shiny.posit.co/r/reference/shiny/latest/shownotification.html).
|
||||
|
||||
## Progress indicators
|
||||
|
||||
@@ -1002,7 +1107,7 @@ If your Shiny app contains computations that take a long time to complete, a pro
|
||||
**_Important note_:**
|
||||
> If you were already using progress bars and had customized them with your own CSS, you can add the `style = "old"` argument to your `withProgress()` call (or `Progress$new()`). This will result in the same appearance as before. You can also call `shinyOptions(progress.style = "old")` in your app's server function to make all progress indicators use the old styling.
|
||||
|
||||
To see new progress bars in action, see [this app](https://gallery.shinyapps.io/085-progress/) in the gallery. You can also learn more about this in [our article](https://shiny.rstudio.com/articles/progress.html) and in the reference documentation (either for the easier [`withProgress` functional API](https://shiny.rstudio.com/reference/shiny/latest/withProgress.html) or the more complicated, but more powerful, [`Progress` object-oriented API](https://shiny.rstudio.com/reference/shiny/latest/Progress.html).
|
||||
To see new progress bars in action, see [this app](https://gallery.shinyapps.io/085-progress/) in the gallery. You can also learn more about this in [our article](https://shiny.rstudio.com/articles/progress.html) and in the reference documentation (either for the easier [`withProgress` functional API](https://shiny.posit.co/r/reference/shiny/latest/withprogress.html) or the more complicated, but more powerful, [`Progress` object-oriented API](https://shiny.posit.co/r/reference/shiny/latest/progress.html).
|
||||
|
||||
## Reconnection
|
||||
|
||||
@@ -1016,7 +1121,7 @@ Shiny has now built-in support for displaying modal dialogs like the one below (
|
||||
<img src="http://shiny.rstudio.com/images/modal-dialog.png" alt="modal-dialog" width="50%"/>
|
||||
</p>
|
||||
|
||||
To learn more about this, read [our article](https://shiny.rstudio.com/articles/modal-dialogs.html) and the [reference documentation](https://shiny.rstudio.com/reference/shiny/latest/modalDialog.html).
|
||||
To learn more about this, read [our article](https://shiny.rstudio.com/articles/modal-dialogs.html) and the [reference documentation](https://shiny.posit.co/r/reference/shiny/latest/modaldialog.html).
|
||||
|
||||
## `insertUI` and `removeUI`
|
||||
|
||||
@@ -1024,7 +1129,7 @@ Sometimes in a Shiny app, arbitrary HTML UI may need to be created on-the-fly in
|
||||
|
||||
See [this simple demo app](https://gallery.shinyapps.io/111-insert-ui/) of how one could use `insertUI` and `removeUI` to insert and remove text elements using a queue. Also see [this other app](https://gallery.shinyapps.io/insertUI/) that demonstrates how to insert and remove a few common Shiny input objects. Finally, [this app](https://gallery.shinyapps.io/insertUI-modules/) shows how to dynamically insert modules using `insertUI`.
|
||||
|
||||
For more, read [our article](https://shiny.rstudio.com/articles/dynamic-ui.html) about dynamic UI generation and the reference documentation about [`insertUI`](https://shiny.rstudio.com/reference/shiny/latest/insertUI.html) and [`removeUI`](https://shiny.rstudio.com/reference/shiny/latest/insertUI.html).
|
||||
For more, read [our article](https://shiny.rstudio.com/articles/dynamic-ui.html) about dynamic UI generation and the reference documentation about [`insertUI`](https://shiny.posit.co/r/reference/shiny/latest/insertui.html) and [`removeUI`](https://shiny.posit.co/r/reference/shiny/latest/insertui.html).
|
||||
|
||||
## Documentation for connecting to an external database
|
||||
|
||||
@@ -1058,7 +1163,7 @@ There are many more minor features, small improvements, and bug fixes than we ca
|
||||
<img src="http://shiny.rstudio.com/images/render-table.png" alt="render-table" width="75%"/>
|
||||
</p>
|
||||
|
||||
For more, read our [short article](https://shiny.rstudio.com/articles/render-table.html) about this update, experiment with all the new features in this [demo app](https://gallery.shinyapps.io/109-render-table/), or check out the [reference documentation](https://shiny.rstudio.com/reference/shiny/latest/renderTable.html).
|
||||
For more, read our [short article](https://shiny.rstudio.com/articles/render-table.html) about this update, experiment with all the new features in this [demo app](https://gallery.shinyapps.io/109-render-table/), or check out the [reference documentation](https://shiny.posit.co/r/reference/shiny/latest/rendertable.html).
|
||||
|
||||
## Full changelog
|
||||
|
||||
|
||||
@@ -177,7 +177,7 @@ utils::globalVariables(".GenericCallEnv", add = TRUE)
|
||||
#' cache by putting this at the top of your app.R, server.R, or global.R:
|
||||
#'
|
||||
#' ```
|
||||
#' shinyOptions(cache = cachem::cache_disk(file.path(dirname(tempdir()), "myapp-cache"))
|
||||
#' shinyOptions(cache = cachem::cache_disk(file.path(dirname(tempdir()), "myapp-cache")))
|
||||
#' ```
|
||||
#'
|
||||
#' This will create a subdirectory in your system temp directory named
|
||||
@@ -231,8 +231,8 @@ utils::globalVariables(".GenericCallEnv", add = TRUE)
|
||||
#' promises, but rather objects provided by the
|
||||
#' \href{https://rstudio.github.io/promises/}{\pkg{promises}} package, which
|
||||
#' are similar to promises in JavaScript. (See [promises::promise()] for more
|
||||
#' information.) You can also use [future::future()] objects to run code in a
|
||||
#' separate process or even on a remote machine.
|
||||
#' information.) You can also use [mirai::mirai()] or [future::future()]
|
||||
#' objects to run code in a separate process or even on a remote machine.
|
||||
#'
|
||||
#' If the value returns a promise, then anything that consumes the cached
|
||||
#' reactive must expect it to return a promise.
|
||||
|
||||
@@ -99,13 +99,13 @@ saveShinySaveState <- function(state) {
|
||||
|
||||
# Encode the state to a URL. This does not save to disk.
|
||||
encodeShinySaveState <- function(state) {
|
||||
exclude <- c(state$exclude, "._bookmark_")
|
||||
inputVals <- serializeReactiveValues(state$input, exclude, stateDir = NULL)
|
||||
|
||||
# Allow user-supplied onSave function to do things like add state$values.
|
||||
if (!is.null(state$onSave))
|
||||
isolate(state$onSave(state))
|
||||
|
||||
exclude <- c(state$exclude, "._bookmark_")
|
||||
inputVals <- serializeReactiveValues(state$input, exclude, stateDir = NULL)
|
||||
|
||||
inputVals <- vapply(inputVals,
|
||||
function(x) toJSON(x, strict_atomic = FALSE),
|
||||
character(1),
|
||||
|
||||
@@ -172,9 +172,10 @@ setCurrentTheme <- function(theme) {
|
||||
|
||||
#' Register a theme dependency
|
||||
#'
|
||||
#' This function registers a function that returns an [htmlDependency()] or list
|
||||
#' of such objects. If `session$setCurrentTheme()` is called, the function will
|
||||
#' be re-executed, and the resulting html dependency will be sent to the client.
|
||||
#' This function registers a function that returns an
|
||||
#' [htmltools::htmlDependency()] or list of such objects. If
|
||||
#' `session$setCurrentTheme()` is called, the function will be re-executed, and
|
||||
#' the resulting html dependency will be sent to the client.
|
||||
#'
|
||||
#' Note that `func` should **not** be an anonymous function, or a function which
|
||||
#' is defined within the calling function. This is so that,
|
||||
@@ -1112,7 +1113,7 @@ plotOutput <- function(outputId, width = "100%", height="400px",
|
||||
#' @rdname renderTable
|
||||
#' @export
|
||||
tableOutput <- function(outputId) {
|
||||
div(id = outputId, class="shiny-html-output")
|
||||
div(id = outputId, class="shiny-html-output shiny-table-output")
|
||||
}
|
||||
|
||||
dataTableDependency <- list(
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
#' output.
|
||||
#' @param pulse Whether to show a pulsing banner at the top of the page when the
|
||||
#' app is busy.
|
||||
#' @param fade Whether to fade recalculating outputs. A value of `FALSE` is
|
||||
#' equivalent to `busyIndicatorOptions(fade_opacity=1)`.
|
||||
#'
|
||||
#' @export
|
||||
#' @seealso [busyIndicatorOptions()] for customizing the appearance of the busy
|
||||
@@ -48,7 +50,7 @@
|
||||
#' }
|
||||
#'
|
||||
#' shinyApp(ui, server)
|
||||
useBusyIndicators <- function(..., spinners = TRUE, pulse = TRUE) {
|
||||
useBusyIndicators <- function(..., spinners = TRUE, pulse = TRUE, fade = TRUE) {
|
||||
|
||||
rlang::check_dots_empty()
|
||||
|
||||
@@ -62,20 +64,33 @@ useBusyIndicators <- function(..., spinners = TRUE, pulse = TRUE) {
|
||||
}
|
||||
})
|
||||
|
||||
js <- HTML(paste(js, collapse = "\n"))
|
||||
|
||||
# TODO: it'd be nice if htmltools had something like a page_attrs() that allowed us
|
||||
# to do this without needing to inject JS into the head.
|
||||
tags$script(js)
|
||||
res <- tags$script(HTML(paste(js, collapse = "\n")))
|
||||
|
||||
if (!fade) {
|
||||
res <- tagList(res, fadeOptions(opacity = 1))
|
||||
}
|
||||
|
||||
res
|
||||
}
|
||||
|
||||
#' Customize busy indicator options
|
||||
#'
|
||||
#' When busy indicators are enabled (see [useBusyIndicators()]), a spinner is
|
||||
#' shown on each calculating/recalculating output, and a pulsing banner is shown
|
||||
#' at the top of the page when the app is otherwise busy. This function allows
|
||||
#' you to customize the appearance of those busy indicators. To apply the
|
||||
#' customization, include the result of this function inside the app's UI.
|
||||
#' @description
|
||||
#' Shiny automatically includes busy indicators, which more specifically means:
|
||||
#' 1. Calculating/recalculating outputs have a spinner overlay.
|
||||
#' 2. Outputs fade out/in when recalculating.
|
||||
#' 3. When no outputs are calculating/recalculating, but Shiny is busy
|
||||
#' doing something else (e.g., a download, side-effect, etc), a page-level
|
||||
#' pulsing banner is shown.
|
||||
#'
|
||||
#' This function allows you to customize the appearance of these busy indicators
|
||||
#' by including the result of this function inside the app's UI. Note that,
|
||||
#' unless `spinner_selector` (or `fade_selector`) is specified, the spinner/fade
|
||||
#' customization applies to the parent element. If the customization should
|
||||
#' instead apply to the entire page, set `spinner_selector = 'html'` and
|
||||
#' `fade_selector = 'html'`.
|
||||
#'
|
||||
#' @param ... Currently ignored.
|
||||
#' @param spinner_type The type of spinner. Pre-bundled types include:
|
||||
@@ -97,6 +112,11 @@ useBusyIndicators <- function(..., spinners = TRUE, pulse = TRUE) {
|
||||
#' @param spinner_selector A character string containing a CSS selector for
|
||||
#' scoping the spinner customization. The default (`NULL`) will apply the
|
||||
#' spinner customization to the parent element of the spinner.
|
||||
#' @param fade_opacity The opacity (a number between 0 and 1) for recalculating
|
||||
#' output. Set to 1 to "disable" the fade.
|
||||
#' @param fade_selector A character string containing a CSS selector for
|
||||
#' scoping the spinner customization. The default (`NULL`) will apply the
|
||||
#' spinner customization to the parent element of the spinner.
|
||||
#' @param pulse_background A CSS background definition for the pulse. The
|
||||
#' default uses a
|
||||
#' [linear-gradient](https://developer.mozilla.org/en-US/docs/Web/CSS/gradient/linear-gradient)
|
||||
@@ -107,7 +127,7 @@ useBusyIndicators <- function(..., spinners = TRUE, pulse = TRUE) {
|
||||
#' time.
|
||||
#'
|
||||
#' @export
|
||||
#' @seealso [useBusyIndicators()] for enabling/disabling busy indicators.
|
||||
#' @seealso [useBusyIndicators()] to disable/enable busy indicators.
|
||||
#' @examplesIf rlang::is_interactive()
|
||||
#'
|
||||
#' library(bslib)
|
||||
@@ -162,6 +182,8 @@ busyIndicatorOptions <- function(
|
||||
spinner_size = NULL,
|
||||
spinner_delay = NULL,
|
||||
spinner_selector = NULL,
|
||||
fade_opacity = NULL,
|
||||
fade_selector = NULL,
|
||||
pulse_background = NULL,
|
||||
pulse_height = NULL,
|
||||
pulse_speed = NULL
|
||||
@@ -177,6 +199,7 @@ busyIndicatorOptions <- function(
|
||||
delay = spinner_delay,
|
||||
selector = spinner_selector
|
||||
),
|
||||
fadeOptions(opacity = fade_opacity, selector = fade_selector),
|
||||
pulseOptions(
|
||||
background = pulse_background,
|
||||
height = pulse_height,
|
||||
@@ -224,6 +247,26 @@ spinnerOptions <- function(type = NULL, color = NULL, size = NULL, delay = NULL,
|
||||
tags$style(css, id = id)
|
||||
}
|
||||
|
||||
fadeOptions <- function(opacity = NULL, selector = NULL) {
|
||||
if (is.null(opacity) && is.null(selector)) {
|
||||
return(NULL)
|
||||
}
|
||||
|
||||
css_vars <- htmltools::css(
|
||||
"--shiny-fade-opacity" = opacity
|
||||
)
|
||||
|
||||
id <- NULL
|
||||
if (is.null(selector)) {
|
||||
id <- paste0("fade-options-", p_randomInt(100, 1000000))
|
||||
selector <- sprintf(":has(> #%s)", id)
|
||||
}
|
||||
|
||||
css <- HTML(paste0(selector, " {", css_vars, "}"))
|
||||
|
||||
tags$style(css, id = id)
|
||||
}
|
||||
|
||||
pulseOptions <- function(background = NULL, height = NULL, speed = NULL) {
|
||||
if (is.null(background) && is.null(height) && is.null(speed)) {
|
||||
return(NULL)
|
||||
@@ -244,6 +287,8 @@ busyIndicatorDependency <- function() {
|
||||
version = get_package_version("shiny"),
|
||||
src = "www/shared/busy-indicators",
|
||||
package = "shiny",
|
||||
stylesheet = "busy-indicators.css"
|
||||
stylesheet = "busy-indicators.css",
|
||||
# TODO-future: In next release make spinners and pulse opt-out
|
||||
# head = as.character(useBusyIndicators())
|
||||
)
|
||||
}
|
||||
|
||||
251
R/conditions.R
@@ -75,6 +75,18 @@ getCallNames <- function(calls) {
|
||||
})
|
||||
}
|
||||
|
||||
# A stripped down version of getCallNames() that intentionally avoids deparsing expressions.
|
||||
# Instead, it leaves expressions to be directly `rlang::hash()` (for de-duplication), which
|
||||
# is much faster than deparsing then hashing.
|
||||
getCallNamesForHash <- function(calls) {
|
||||
lapply(calls, function(call) {
|
||||
name <- call[[1L]]
|
||||
if (is.function(name)) return("<Anonymous>")
|
||||
if (typeof(name) == "promise") return("<Promise>")
|
||||
name
|
||||
})
|
||||
}
|
||||
|
||||
getLocs <- function(calls) {
|
||||
vapply(calls, function(call) {
|
||||
srcref <- attr(call, "srcref", exact = TRUE)
|
||||
@@ -130,6 +142,44 @@ captureStackTraces <- function(expr) {
|
||||
#' @include globals.R
|
||||
.globals$deepStack <- NULL
|
||||
|
||||
getCallStackDigest <- function(callStack, warn = FALSE) {
|
||||
dg <- attr(callStack, "shiny.stack.digest", exact = TRUE)
|
||||
if (!is.null(dg)) {
|
||||
return(dg)
|
||||
}
|
||||
|
||||
if (isTRUE(warn)) {
|
||||
rlang::warn(
|
||||
"Call stack doesn't have a cached digest; expensively computing one now",
|
||||
.frequency = "once",
|
||||
.frequency_id = "deepstack-uncached-digest-warning"
|
||||
)
|
||||
}
|
||||
|
||||
rlang::hash(getCallNamesForHash(callStack))
|
||||
}
|
||||
|
||||
saveCallStackDigest <- function(callStack) {
|
||||
attr(callStack, "shiny.stack.digest") <- getCallStackDigest(callStack, warn = FALSE)
|
||||
callStack
|
||||
}
|
||||
|
||||
# Appends a call stack to a list of call stacks, but only if it's not already
|
||||
# in the list. The list is deduplicated by digest; ideally the digests on the
|
||||
# list are cached before calling this function (you will get a warning if not).
|
||||
appendCallStackWithDedupe <- function(lst, x) {
|
||||
digests <- vapply(lst, getCallStackDigest, character(1), warn = TRUE)
|
||||
xdigest <- getCallStackDigest(x, warn = TRUE)
|
||||
stopifnot(all(nzchar(digests)))
|
||||
stopifnot(length(xdigest) == 1)
|
||||
stopifnot(nzchar(xdigest))
|
||||
if (xdigest %in% digests) {
|
||||
return(lst)
|
||||
} else {
|
||||
return(c(lst, list(x)))
|
||||
}
|
||||
}
|
||||
|
||||
createStackTracePromiseDomain <- function() {
|
||||
# These are actually stateless, we wouldn't have to create a new one each time
|
||||
# if we didn't want to. They're pretty cheap though.
|
||||
@@ -142,13 +192,14 @@ createStackTracePromiseDomain <- function() {
|
||||
currentStack <- sys.calls()
|
||||
currentParents <- sys.parents()
|
||||
attr(currentStack, "parents") <- currentParents
|
||||
currentStack <- saveCallStackDigest(currentStack)
|
||||
currentDeepStack <- .globals$deepStack
|
||||
}
|
||||
function(...) {
|
||||
# Fulfill time
|
||||
if (deepStacksEnabled()) {
|
||||
origDeepStack <- .globals$deepStack
|
||||
.globals$deepStack <- c(currentDeepStack, list(currentStack))
|
||||
.globals$deepStack <- appendCallStackWithDedupe(currentDeepStack, currentStack)
|
||||
on.exit(.globals$deepStack <- origDeepStack, add = TRUE)
|
||||
}
|
||||
|
||||
@@ -165,13 +216,14 @@ createStackTracePromiseDomain <- function() {
|
||||
currentStack <- sys.calls()
|
||||
currentParents <- sys.parents()
|
||||
attr(currentStack, "parents") <- currentParents
|
||||
currentStack <- saveCallStackDigest(currentStack)
|
||||
currentDeepStack <- .globals$deepStack
|
||||
}
|
||||
function(...) {
|
||||
# Fulfill time
|
||||
if (deepStacksEnabled()) {
|
||||
origDeepStack <- .globals$deepStack
|
||||
.globals$deepStack <- c(currentDeepStack, list(currentStack))
|
||||
.globals$deepStack <- appendCallStackWithDedupe(currentDeepStack, currentStack)
|
||||
on.exit(.globals$deepStack <- origDeepStack, add = TRUE)
|
||||
}
|
||||
|
||||
@@ -199,6 +251,7 @@ doCaptureStack <- function(e) {
|
||||
calls <- sys.calls()
|
||||
parents <- sys.parents()
|
||||
attr(calls, "parents") <- parents
|
||||
calls <- saveCallStackDigest(calls)
|
||||
attr(e, "stack.trace") <- calls
|
||||
}
|
||||
if (deepStacksEnabled()) {
|
||||
@@ -281,88 +334,115 @@ printStackTrace <- function(cond,
|
||||
full = get_devmode_option("shiny.fullstacktrace", FALSE),
|
||||
offset = getOption("shiny.stacktraceoffset", TRUE)) {
|
||||
|
||||
should_drop <- !full
|
||||
should_strip <- !full
|
||||
should_prune <- !full
|
||||
|
||||
stackTraceCalls <- c(
|
||||
stackTraces <- c(
|
||||
attr(cond, "deep.stack.trace", exact = TRUE),
|
||||
list(attr(cond, "stack.trace", exact = TRUE))
|
||||
)
|
||||
|
||||
stackTraceParents <- lapply(stackTraceCalls, attr, which = "parents", exact = TRUE)
|
||||
stackTraceCallNames <- lapply(stackTraceCalls, getCallNames)
|
||||
stackTraceCalls <- lapply(stackTraceCalls, offsetSrcrefs, offset = offset)
|
||||
|
||||
# Use dropTrivialFrames logic to remove trailing bits (.handleSimpleError, h)
|
||||
if (should_drop) {
|
||||
# toKeep is a list of logical vectors, of which elements (stack frames) to keep
|
||||
toKeep <- lapply(stackTraceCallNames, dropTrivialFrames)
|
||||
# We apply the list of logical vector indices to each data structure
|
||||
stackTraceCalls <- mapply(stackTraceCalls, FUN = `[`, toKeep, SIMPLIFY = FALSE)
|
||||
stackTraceCallNames <- mapply(stackTraceCallNames, FUN = `[`, toKeep, SIMPLIFY = FALSE)
|
||||
stackTraceParents <- mapply(stackTraceParents, FUN = `[`, toKeep, SIMPLIFY = FALSE)
|
||||
# Stripping of stack traces is the one step where the different stack traces
|
||||
# interact. So we need to do this in one go, instead of individually within
|
||||
# printOneStackTrace.
|
||||
if (!full) {
|
||||
stripResults <- stripStackTraces(lapply(stackTraces, getCallNames))
|
||||
} else {
|
||||
# If full is TRUE, we don't want to strip anything
|
||||
stripResults <- rep_len(list(TRUE), length(stackTraces))
|
||||
}
|
||||
|
||||
delayedAssign("all_true", {
|
||||
# List of logical vectors that are all TRUE, the same shape as
|
||||
# stackTraceCallNames. Delay the evaluation so we don't create it unless
|
||||
# we need it, but if we need it twice then we don't pay to create it twice.
|
||||
lapply(stackTraceCallNames, function(st) {
|
||||
rep_len(TRUE, length(st))
|
||||
})
|
||||
})
|
||||
|
||||
# stripStackTraces and lapply(stackTraceParents, pruneStackTrace) return lists
|
||||
# of logical vectors. Use mapply(FUN = `&`) to boolean-and each pair of the
|
||||
# logical vectors.
|
||||
toShow <- mapply(
|
||||
if (should_strip) stripStackTraces(stackTraceCallNames) else all_true,
|
||||
if (should_prune) lapply(stackTraceParents, pruneStackTrace) else all_true,
|
||||
FUN = `&`,
|
||||
mapply(
|
||||
seq_along(stackTraces),
|
||||
rev(stackTraces),
|
||||
rev(stripResults),
|
||||
FUN = function(i, trace, stripResult) {
|
||||
if (is.integer(trace)) {
|
||||
noun <- if (trace > 1L) "traces" else "trace"
|
||||
message("[ reached getOption(\"shiny.deepstacktrace\") -- omitted ", trace, " more stack ", noun, " ]")
|
||||
} else {
|
||||
if (i != 1) {
|
||||
message("From earlier call:")
|
||||
}
|
||||
printOneStackTrace(
|
||||
stackTrace = trace,
|
||||
stripResult = stripResult,
|
||||
full = full,
|
||||
offset = offset
|
||||
)
|
||||
}
|
||||
# No mapply return value--we're just printing
|
||||
NULL
|
||||
},
|
||||
SIMPLIFY = FALSE
|
||||
)
|
||||
|
||||
dfs <- mapply(seq_along(stackTraceCalls), rev(stackTraceCalls), rev(stackTraceCallNames), rev(toShow), FUN = function(i, calls, nms, index) {
|
||||
st <- data.frame(
|
||||
num = rev(which(index)),
|
||||
call = rev(nms[index]),
|
||||
loc = rev(getLocs(calls[index])),
|
||||
category = rev(getCallCategories(calls[index])),
|
||||
stringsAsFactors = FALSE
|
||||
)
|
||||
|
||||
if (i != 1) {
|
||||
message("From earlier call:")
|
||||
}
|
||||
|
||||
if (nrow(st) == 0) {
|
||||
message(" [No stack trace available]")
|
||||
} else {
|
||||
width <- floor(log10(max(st$num))) + 1
|
||||
formatted <- paste0(
|
||||
" ",
|
||||
formatC(st$num, width = width),
|
||||
": ",
|
||||
mapply(paste0(st$call, st$loc), st$category, FUN = function(name, category) {
|
||||
if (category == "pkg")
|
||||
crayon::silver(name)
|
||||
else if (category == "user")
|
||||
crayon::blue$bold(name)
|
||||
else
|
||||
crayon::white(name)
|
||||
}),
|
||||
"\n"
|
||||
)
|
||||
cat(file = stderr(), formatted, sep = "")
|
||||
}
|
||||
|
||||
st
|
||||
}, SIMPLIFY = FALSE)
|
||||
|
||||
invisible()
|
||||
}
|
||||
|
||||
printOneStackTrace <- function(stackTrace, stripResult, full, offset) {
|
||||
calls <- offsetSrcrefs(stackTrace, offset = offset)
|
||||
callNames <- getCallNames(stackTrace)
|
||||
parents <- attr(stackTrace, "parents", exact = TRUE)
|
||||
|
||||
should_drop <- !full
|
||||
should_strip <- !full
|
||||
should_prune <- !full
|
||||
|
||||
if (should_drop) {
|
||||
toKeep <- dropTrivialFrames(callNames)
|
||||
calls <- calls[toKeep]
|
||||
callNames <- callNames[toKeep]
|
||||
parents <- parents[toKeep]
|
||||
stripResult <- stripResult[toKeep]
|
||||
}
|
||||
|
||||
toShow <- rep(TRUE, length(callNames))
|
||||
if (should_prune) {
|
||||
toShow <- toShow & pruneStackTrace(parents)
|
||||
}
|
||||
if (should_strip) {
|
||||
toShow <- toShow & stripResult
|
||||
}
|
||||
|
||||
# If we're running in testthat, hide the parts of the stack trace that can
|
||||
# vary based on how testthat was launched. It's critical that this is not
|
||||
# happen at the same time as dropTrivialFrames, which happens before
|
||||
# pruneStackTrace; because dropTrivialTestFrames removes calls from the top
|
||||
# (or bottom? whichever is the oldest?) of the stack, it breaks `parents`
|
||||
# which is based on absolute indices of calls. dropTrivialFrames gets away
|
||||
# with this because it only removes calls from the opposite side of the stack.
|
||||
toShow <- toShow & dropTrivialTestFrames(callNames)
|
||||
|
||||
st <- data.frame(
|
||||
num = rev(which(toShow)),
|
||||
call = rev(callNames[toShow]),
|
||||
loc = rev(getLocs(calls[toShow])),
|
||||
category = rev(getCallCategories(calls[toShow])),
|
||||
stringsAsFactors = FALSE
|
||||
)
|
||||
|
||||
if (nrow(st) == 0) {
|
||||
message(" [No stack trace available]")
|
||||
} else {
|
||||
width <- floor(log10(max(st$num))) + 1
|
||||
formatted <- paste0(
|
||||
" ",
|
||||
formatC(st$num, width = width),
|
||||
": ",
|
||||
mapply(paste0(st$call, st$loc), st$category, FUN = function(name, category) {
|
||||
if (category == "pkg")
|
||||
cli::col_silver(name)
|
||||
else if (category == "user")
|
||||
cli::style_bold(cli::col_blue(name))
|
||||
else
|
||||
cli::col_white(name)
|
||||
}),
|
||||
"\n"
|
||||
)
|
||||
cat(file = stderr(), formatted, sep = "")
|
||||
}
|
||||
|
||||
invisible(st)
|
||||
}
|
||||
|
||||
stripStackTraces <- function(stackTraces, values = FALSE) {
|
||||
score <- 1L # >=1: show, <=0: hide
|
||||
lapply(seq_along(stackTraces), function(i) {
|
||||
@@ -458,6 +538,33 @@ dropTrivialFrames <- function(callnames) {
|
||||
)
|
||||
}
|
||||
|
||||
dropTrivialTestFrames <- function(callnames) {
|
||||
if (!identical(Sys.getenv("TESTTHAT_IS_SNAPSHOT"), "true")) {
|
||||
return(rep_len(TRUE, length(callnames)))
|
||||
}
|
||||
|
||||
hideable <- callnames %in% c(
|
||||
"test",
|
||||
"devtools::test",
|
||||
"test_check",
|
||||
"testthat::test_check",
|
||||
"test_dir",
|
||||
"testthat::test_dir",
|
||||
"test_file",
|
||||
"testthat::test_file",
|
||||
"test_local",
|
||||
"testthat::test_local"
|
||||
)
|
||||
|
||||
firstGoodCall <- min(which(!hideable))
|
||||
toRemove <- firstGoodCall - 1L
|
||||
|
||||
c(
|
||||
rep_len(FALSE, toRemove),
|
||||
rep_len(TRUE, length(callnames) - toRemove)
|
||||
)
|
||||
}
|
||||
|
||||
offsetSrcrefs <- function(calls, offset = TRUE) {
|
||||
if (offset) {
|
||||
srcrefs <- getSrcRefs(calls)
|
||||
|
||||
@@ -128,6 +128,12 @@ in_devmode <- function() {
|
||||
!identical(Sys.getenv("TESTTHAT"), "true")
|
||||
}
|
||||
|
||||
in_client_devmode <- function() {
|
||||
# Client-side devmode enables client-side only dev features without local
|
||||
# devmode. Currently, the main feature is the client-side error console.
|
||||
isTRUE(getOption("shiny.client_devmode", FALSE))
|
||||
}
|
||||
|
||||
#' @describeIn devmode Temporarily set Shiny Developer Mode and Developer
|
||||
#' message verbosity
|
||||
#' @param code Code to execute with the temporary Dev Mode options set
|
||||
|
||||
@@ -41,6 +41,56 @@
|
||||
#' is, a function that quickly returns a promise) and allows even that very
|
||||
#' session to immediately unblock and carry on with other user interactions.
|
||||
#'
|
||||
#' @examplesIf rlang::is_interactive() && rlang::is_installed("mirai")
|
||||
#' library(shiny)
|
||||
#' library(bslib)
|
||||
#' library(mirai)
|
||||
#'
|
||||
#' # Set background processes for running tasks
|
||||
#' daemons(1)
|
||||
#' # Reset when the app is stopped
|
||||
#' onStop(function() daemons(0))
|
||||
#'
|
||||
#' ui <- page_fluid(
|
||||
#' titlePanel("Extended Task Demo"),
|
||||
#' p(
|
||||
#' 'Click the button below to perform a "calculation"',
|
||||
#' "that takes a while to perform."
|
||||
#' ),
|
||||
#' input_task_button("recalculate", "Recalculate"),
|
||||
#' p(textOutput("result"))
|
||||
#' )
|
||||
#'
|
||||
#' server <- function(input, output) {
|
||||
#' rand_task <- ExtendedTask$new(function() {
|
||||
#' mirai(
|
||||
#' {
|
||||
#' # Slow operation goes here
|
||||
#' Sys.sleep(2)
|
||||
#' sample(1:100, 1)
|
||||
#' }
|
||||
#' )
|
||||
#' })
|
||||
#'
|
||||
#' # Make button state reflect task.
|
||||
#' # If using R >=4.1, you can do this instead:
|
||||
#' # rand_task <- ExtendedTask$new(...) |> bind_task_button("recalculate")
|
||||
#' bind_task_button(rand_task, "recalculate")
|
||||
#'
|
||||
#' observeEvent(input$recalculate, {
|
||||
#' # Invoke the extended in an observer
|
||||
#' rand_task$invoke()
|
||||
#' })
|
||||
#'
|
||||
#' output$result <- renderText({
|
||||
#' # React to updated results when the task completes
|
||||
#' number <- rand_task$result()
|
||||
#' paste0("Your number is ", number, ".")
|
||||
#' })
|
||||
#' }
|
||||
#'
|
||||
#' shinyApp(ui, server)
|
||||
#'
|
||||
#' @export
|
||||
ExtendedTask <- R6Class("ExtendedTask", portable = TRUE, cloneable = FALSE,
|
||||
public = list(
|
||||
@@ -52,11 +102,12 @@ ExtendedTask <- R6Class("ExtendedTask", portable = TRUE, cloneable = FALSE,
|
||||
#' @param func The long-running operation to execute. This should be an
|
||||
#' asynchronous function, meaning, it should use the
|
||||
#' [\{promises\}](https://rstudio.github.io/promises/) package, most
|
||||
#' likely in conjuction with the
|
||||
#' likely in conjunction with the
|
||||
#' [\{mirai\}](https://mirai.r-lib.org) or
|
||||
#' [\{future\}](https://rstudio.github.io/promises/articles/promises_04_futures.html)
|
||||
#' package. (In short, the return value of `func` should be a
|
||||
#' [`Future`][future::future()] object, or a `promise`, or something else
|
||||
#' that [promises::as.promise()] understands.)
|
||||
#' [`mirai`][mirai::mirai()], [`Future`][future::future()], `promise`,
|
||||
#' or something else that [promises::as.promise()] understands.)
|
||||
#'
|
||||
#' It's also important that this logic does not read from any
|
||||
#' reactive inputs/sources, as inputs may change after the function is
|
||||
@@ -82,14 +133,15 @@ ExtendedTask <- R6Class("ExtendedTask", portable = TRUE, cloneable = FALSE,
|
||||
#' arguments.
|
||||
invoke = function(...) {
|
||||
args <- rlang::dots_list(..., .ignore_empty = "none")
|
||||
call <- rlang::caller_call(n = 0)
|
||||
|
||||
if (
|
||||
isolate(private$rv_status()) == "running" ||
|
||||
private$invocation_queue$size() > 0
|
||||
) {
|
||||
private$invocation_queue$add(args)
|
||||
private$invocation_queue$add(list(args = args, call = call))
|
||||
} else {
|
||||
private$do_invoke(args)
|
||||
private$do_invoke(args, call = call)
|
||||
}
|
||||
invisible(NULL)
|
||||
},
|
||||
@@ -156,44 +208,41 @@ ExtendedTask <- R6Class("ExtendedTask", portable = TRUE, cloneable = FALSE,
|
||||
rv_error = NULL,
|
||||
invocation_queue = NULL,
|
||||
|
||||
do_invoke = function(args) {
|
||||
do_invoke = function(args, call = NULL) {
|
||||
private$rv_status("running")
|
||||
private$rv_value(NULL)
|
||||
private$rv_error(NULL)
|
||||
|
||||
p <- NULL
|
||||
tryCatch({
|
||||
maskReactiveContext({
|
||||
# TODO: Bounce the do.call off of a promise_resolve(), so that the
|
||||
# call to invoke() always returns immediately?
|
||||
result <- do.call(private$func, args)
|
||||
p <- promises::as.promise(result)
|
||||
})
|
||||
}, error = function(e) {
|
||||
private$on_error(e)
|
||||
})
|
||||
p <- promises::promise_resolve(
|
||||
maskReactiveContext(do.call(private$func, args))
|
||||
)
|
||||
|
||||
promises::finally(
|
||||
promises::then(p,
|
||||
onFulfilled = function(value, .visible) {
|
||||
private$on_success(list(value=value, visible=.visible))
|
||||
},
|
||||
onRejected = function(error) {
|
||||
private$on_error(error)
|
||||
}
|
||||
),
|
||||
onFinally = function() {
|
||||
if (private$invocation_queue$size() > 0) {
|
||||
private$do_invoke(private$invocation_queue$remove())
|
||||
}
|
||||
p <- promises::then(
|
||||
p,
|
||||
onFulfilled = function(value, .visible) {
|
||||
private$on_success(list(value = value, visible = .visible))
|
||||
},
|
||||
onRejected = function(error) {
|
||||
private$on_error(error, call = call)
|
||||
}
|
||||
)
|
||||
|
||||
promises::finally(p, onFinally = function() {
|
||||
if (private$invocation_queue$size() > 0) {
|
||||
next_call <- private$invocation_queue$remove()
|
||||
private$do_invoke(next_call$args, next_call$call)
|
||||
}
|
||||
})
|
||||
|
||||
invisible(NULL)
|
||||
},
|
||||
|
||||
on_error = function(err) {
|
||||
on_error = function(err, call = NULL) {
|
||||
cli::cli_warn(
|
||||
"ERROR: An error occurred when invoking the ExtendedTask.",
|
||||
parent = err,
|
||||
call = call
|
||||
)
|
||||
private$rv_status("error")
|
||||
private$rv_error(err)
|
||||
},
|
||||
|
||||
122
R/graph.R
@@ -1,4 +1,3 @@
|
||||
|
||||
# domain is like session
|
||||
|
||||
|
||||
@@ -20,7 +19,7 @@ reactIdStr <- function(num) {
|
||||
#' dependencies and execution in your application.
|
||||
#'
|
||||
#' To use the reactive log visualizer, start with a fresh R session and
|
||||
#' run the command `options(shiny.reactlog=TRUE)`; then launch your
|
||||
#' run the command `reactlog::reactlog_enable()`; then launch your
|
||||
#' application in the usual way (e.g. using [runApp()]). At
|
||||
#' any time you can hit Ctrl+F3 (or for Mac users, Command+F3) in your
|
||||
#' web browser to launch the reactive log visualization.
|
||||
@@ -43,16 +42,20 @@ reactIdStr <- function(num) {
|
||||
#' call `reactlogShow()` explicitly.
|
||||
#'
|
||||
#' For security and performance reasons, do not enable
|
||||
#' `shiny.reactlog` in production environments. When the option is
|
||||
#' enabled, it's possible for any user of your app to see at least some
|
||||
#' of the source code of your reactive expressions and observers.
|
||||
#' `options(shiny.reactlog=TRUE)` (or `reactlog::reactlog_enable()`) in
|
||||
#' production environments. When the option is enabled, it's possible
|
||||
#' for any user of your app to see at least some of the source code of
|
||||
#' your reactive expressions and observers. In addition, reactlog
|
||||
#' should be considered a memory leak as it will constantly grow and
|
||||
#' will never reset until the R session is restarted.
|
||||
#'
|
||||
#' @name reactlog
|
||||
NULL
|
||||
|
||||
|
||||
#' @describeIn reactlog Return a list of reactive information. Can be used in conjunction with
|
||||
#' [reactlog::reactlog_show] to later display the reactlog graph.
|
||||
#' @describeIn reactlog Return a list of reactive information. Can be used in
|
||||
#' conjunction with [reactlog::reactlog_show] to later display the reactlog
|
||||
#' graph.
|
||||
#' @export
|
||||
reactlog <- function() {
|
||||
rLog$asList()
|
||||
@@ -67,12 +70,34 @@ reactlogShow <- function(time = TRUE) {
|
||||
reactlog::reactlog_show(reactlog(), time = time)
|
||||
}
|
||||
|
||||
#' @describeIn reactlog Resets the entire reactlog stack. Useful for debugging and removing all prior reactive history.
|
||||
#' @describeIn reactlog Resets the entire reactlog stack. Useful for debugging
|
||||
#' and removing all prior reactive history.
|
||||
#' @export
|
||||
reactlogReset <- function() {
|
||||
rLog$reset()
|
||||
}
|
||||
|
||||
#' @describeIn reactlog Adds "mark" entry into the reactlog stack. This is
|
||||
#' useful for programmatically adding a marked entry in the reactlog, rather
|
||||
#' than using your keyboard's key combination.
|
||||
#'
|
||||
#' For example, we can _mark_ the reactlog at the beginning of an
|
||||
#' `observeEvent`'s calculation:
|
||||
#' ```r
|
||||
#' observeEvent(input$my_event_trigger, {
|
||||
#' # Add a mark in the reactlog
|
||||
#' reactlogAddMark()
|
||||
#' # Run your regular event reaction code here...
|
||||
#' ....
|
||||
#' })
|
||||
#' ```
|
||||
#' @param session The Shiny session to assign the mark to. Defaults to the
|
||||
#' current session.
|
||||
#' @export
|
||||
reactlogAddMark <- function(session = getDefaultReactiveDomain()) {
|
||||
rLog$userMark(session)
|
||||
}
|
||||
|
||||
# called in "/reactlog" middleware
|
||||
renderReactlog <- function(sessionToken = NULL, time = TRUE) {
|
||||
check_reactlog()
|
||||
@@ -98,7 +123,6 @@ RLog <- R6Class(
|
||||
private = list(
|
||||
option = "shiny.reactlog",
|
||||
msgOption = "shiny.reactlog.console",
|
||||
|
||||
appendEntry = function(domain, logEntry) {
|
||||
if (self$isLogging()) {
|
||||
sessionToken <- if (is.null(domain)) NULL else domain$token
|
||||
@@ -113,20 +137,19 @@ RLog <- R6Class(
|
||||
public = list(
|
||||
msg = "<MessageLogger>",
|
||||
logStack = "<Stack>",
|
||||
|
||||
noReactIdLabel = "NoCtxReactId",
|
||||
noReactId = reactIdStr("NoCtxReactId"),
|
||||
dummyReactIdLabel = "DummyReactId",
|
||||
dummyReactId = reactIdStr("DummyReactId"),
|
||||
|
||||
asList = function() {
|
||||
ret <- self$logStack$as_list()
|
||||
attr(ret, "version") <- "1"
|
||||
ret
|
||||
},
|
||||
|
||||
ctxIdStr = function(ctxId) {
|
||||
if (is.null(ctxId) || identical(ctxId, "")) return(NULL)
|
||||
if (is.null(ctxId) || identical(ctxId, "")) {
|
||||
return(NULL)
|
||||
}
|
||||
paste0("ctx", ctxId)
|
||||
},
|
||||
namesIdStr = function(reactId) {
|
||||
@@ -141,7 +164,6 @@ RLog <- R6Class(
|
||||
keyIdStr = function(reactId, key) {
|
||||
paste0(reactId, "$", key)
|
||||
},
|
||||
|
||||
valueStr = function(value, n = 200) {
|
||||
if (!self$isLogging()) {
|
||||
# return a placeholder string to avoid calling str
|
||||
@@ -151,10 +173,9 @@ RLog <- R6Class(
|
||||
# only capture the first level of the object
|
||||
utils::capture.output(utils::str(value, max.level = 1))
|
||||
})
|
||||
outputTxt <- paste0(output, collapse="\n")
|
||||
outputTxt <- paste0(output, collapse = "\n")
|
||||
msg$shortenString(outputTxt, n = n)
|
||||
},
|
||||
|
||||
initialize = function(rlogOption = "shiny.reactlog", msgOption = "shiny.reactlog.console") {
|
||||
private$option <- rlogOption
|
||||
private$msgOption <- msgOption
|
||||
@@ -174,7 +195,6 @@ RLog <- R6Class(
|
||||
isLogging = function() {
|
||||
isTRUE(getOption(private$option, FALSE))
|
||||
},
|
||||
|
||||
define = function(reactId, value, label, type, domain) {
|
||||
valueStr <- self$valueStr(value)
|
||||
if (msg$hasReact(reactId)) {
|
||||
@@ -205,9 +225,10 @@ RLog <- R6Class(
|
||||
defineObserver = function(reactId, label, domain) {
|
||||
self$define(reactId, value = NULL, label, "observer", domain)
|
||||
},
|
||||
|
||||
dependsOn = function(reactId, depOnReactId, ctxId, domain) {
|
||||
if (is.null(reactId)) return()
|
||||
if (is.null(reactId)) {
|
||||
return()
|
||||
}
|
||||
ctxId <- ctxIdStr(ctxId)
|
||||
msg$log("dependsOn:", msg$reactStr(reactId), " on", msg$reactStr(depOnReactId), msg$ctxStr(ctxId))
|
||||
private$appendEntry(domain, list(
|
||||
@@ -220,7 +241,6 @@ RLog <- R6Class(
|
||||
dependsOnKey = function(reactId, depOnReactId, key, ctxId, domain) {
|
||||
self$dependsOn(reactId, self$keyIdStr(depOnReactId, key), ctxId, domain)
|
||||
},
|
||||
|
||||
dependsOnRemove = function(reactId, depOnReactId, ctxId, domain) {
|
||||
ctxId <- self$ctxIdStr(ctxId)
|
||||
msg$log("dependsOnRemove:", msg$reactStr(reactId), " on", msg$reactStr(depOnReactId), msg$ctxStr(ctxId))
|
||||
@@ -234,7 +254,6 @@ RLog <- R6Class(
|
||||
dependsOnKeyRemove = function(reactId, depOnReactId, key, ctxId, domain) {
|
||||
self$dependsOnRemove(reactId, self$keyIdStr(depOnReactId, key), ctxId, domain)
|
||||
},
|
||||
|
||||
createContext = function(ctxId, label, type, prevCtxId, domain) {
|
||||
ctxId <- self$ctxIdStr(ctxId)
|
||||
prevCtxId <- self$ctxIdStr(prevCtxId)
|
||||
@@ -245,10 +264,9 @@ RLog <- R6Class(
|
||||
label = msg$shortenString(label),
|
||||
type = type,
|
||||
prevCtxId = prevCtxId,
|
||||
srcref = as.vector(attr(label, "srcref")), srcfile=attr(label, "srcfile")
|
||||
srcref = as.vector(attr(label, "srcref")), srcfile = attr(label, "srcfile")
|
||||
))
|
||||
},
|
||||
|
||||
enter = function(reactId, ctxId, type, domain) {
|
||||
ctxId <- self$ctxIdStr(ctxId)
|
||||
if (identical(type, "isolate")) {
|
||||
@@ -291,7 +309,6 @@ RLog <- R6Class(
|
||||
))
|
||||
}
|
||||
},
|
||||
|
||||
valueChange = function(reactId, value, domain) {
|
||||
valueStr <- self$valueStr(value)
|
||||
msg$log("valueChange:", msg$reactStr(reactId), msg$valueStr(valueStr))
|
||||
@@ -313,8 +330,6 @@ RLog <- R6Class(
|
||||
valueChangeKey = function(reactId, key, value, domain) {
|
||||
self$valueChange(self$keyIdStr(reactId, key), value, domain)
|
||||
},
|
||||
|
||||
|
||||
invalidateStart = function(reactId, ctxId, type, domain) {
|
||||
ctxId <- self$ctxIdStr(ctxId)
|
||||
if (identical(type, "isolate")) {
|
||||
@@ -357,7 +372,6 @@ RLog <- R6Class(
|
||||
))
|
||||
}
|
||||
},
|
||||
|
||||
invalidateLater = function(reactId, runningCtx, millis, domain) {
|
||||
msg$log("invalidateLater: ", millis, "ms", msg$reactStr(reactId), msg$ctxStr(runningCtx))
|
||||
private$appendEntry(domain, list(
|
||||
@@ -367,14 +381,12 @@ RLog <- R6Class(
|
||||
millis = millis
|
||||
))
|
||||
},
|
||||
|
||||
idle = function(domain = NULL) {
|
||||
msg$log("idle")
|
||||
private$appendEntry(domain, list(
|
||||
action = "idle"
|
||||
))
|
||||
},
|
||||
|
||||
asyncStart = function(domain = NULL) {
|
||||
msg$log("asyncStart")
|
||||
private$appendEntry(domain, list(
|
||||
@@ -387,7 +399,6 @@ RLog <- R6Class(
|
||||
action = "asyncStop"
|
||||
))
|
||||
},
|
||||
|
||||
freezeReactiveVal = function(reactId, domain) {
|
||||
msg$log("freeze:", msg$reactStr(reactId))
|
||||
private$appendEntry(domain, list(
|
||||
@@ -398,7 +409,6 @@ RLog <- R6Class(
|
||||
freezeReactiveKey = function(reactId, key, domain) {
|
||||
self$freezeReactiveVal(self$keyIdStr(reactId, key), domain)
|
||||
},
|
||||
|
||||
thawReactiveVal = function(reactId, domain) {
|
||||
msg$log("thaw:", msg$reactStr(reactId))
|
||||
private$appendEntry(domain, list(
|
||||
@@ -409,54 +419,60 @@ RLog <- R6Class(
|
||||
thawReactiveKey = function(reactId, key, domain) {
|
||||
self$thawReactiveVal(self$keyIdStr(reactId, key), domain)
|
||||
},
|
||||
|
||||
userMark = function(domain = NULL) {
|
||||
msg$log("userMark")
|
||||
private$appendEntry(domain, list(
|
||||
action = "userMark"
|
||||
))
|
||||
}
|
||||
|
||||
)
|
||||
)
|
||||
|
||||
MessageLogger = R6Class(
|
||||
MessageLogger <- R6Class(
|
||||
"MessageLogger",
|
||||
portable = FALSE,
|
||||
public = list(
|
||||
depth = 0L,
|
||||
reactCache = list(),
|
||||
option = "shiny.reactlog.console",
|
||||
|
||||
initialize = function(option = "shiny.reactlog.console", depth = 0L) {
|
||||
if (!missing(depth)) self$depth <- depth
|
||||
if (!missing(option)) self$option <- option
|
||||
},
|
||||
|
||||
isLogging = function() {
|
||||
isTRUE(getOption(self$option))
|
||||
},
|
||||
isNotLogging = function() {
|
||||
! isTRUE(getOption(self$option))
|
||||
!isTRUE(getOption(self$option))
|
||||
},
|
||||
depthIncrement = function() {
|
||||
if (self$isNotLogging()) return(NULL)
|
||||
if (self$isNotLogging()) {
|
||||
return(NULL)
|
||||
}
|
||||
self$depth <- self$depth + 1L
|
||||
},
|
||||
depthDecrement = function() {
|
||||
if (self$isNotLogging()) return(NULL)
|
||||
if (self$isNotLogging()) {
|
||||
return(NULL)
|
||||
}
|
||||
self$depth <- self$depth - 1L
|
||||
},
|
||||
hasReact = function(reactId) {
|
||||
if (self$isNotLogging()) return(FALSE)
|
||||
if (self$isNotLogging()) {
|
||||
return(FALSE)
|
||||
}
|
||||
!is.null(self$getReact(reactId))
|
||||
},
|
||||
getReact = function(reactId, force = FALSE) {
|
||||
if (identical(force, FALSE) && self$isNotLogging()) return(NULL)
|
||||
if (identical(force, FALSE) && self$isNotLogging()) {
|
||||
return(NULL)
|
||||
}
|
||||
self$reactCache[[reactId]]
|
||||
},
|
||||
setReact = function(reactObj, force = FALSE) {
|
||||
if (identical(force, FALSE) && self$isNotLogging()) return(NULL)
|
||||
if (identical(force, FALSE) && self$isNotLogging()) {
|
||||
return(NULL)
|
||||
}
|
||||
self$reactCache[[reactObj$reactId]] <- reactObj
|
||||
},
|
||||
shortenString = function(txt, n = 250) {
|
||||
@@ -475,13 +491,17 @@ MessageLogger = R6Class(
|
||||
},
|
||||
valueStr = function(valueStr) {
|
||||
paste0(
|
||||
" '", self$shortenString(self$singleLine(valueStr)), "'"
|
||||
" '", self$shortenString(self$singleLine(valueStr)), "'"
|
||||
)
|
||||
},
|
||||
reactStr = function(reactId) {
|
||||
if (self$isNotLogging()) return(NULL)
|
||||
if (self$isNotLogging()) {
|
||||
return(NULL)
|
||||
}
|
||||
reactInfo <- self$getReact(reactId)
|
||||
if (is.null(reactInfo)) return(" <UNKNOWN_REACTID>")
|
||||
if (is.null(reactInfo)) {
|
||||
return(" <UNKNOWN_REACTID>")
|
||||
}
|
||||
paste0(
|
||||
" ", reactInfo$reactId, ":'", self$shortenString(self$singleLine(reactInfo$label)), "'"
|
||||
)
|
||||
@@ -490,11 +510,15 @@ MessageLogger = R6Class(
|
||||
self$ctxStr(ctxId = NULL, type = type)
|
||||
},
|
||||
ctxStr = function(ctxId = NULL, type = NULL) {
|
||||
if (self$isNotLogging()) return(NULL)
|
||||
if (self$isNotLogging()) {
|
||||
return(NULL)
|
||||
}
|
||||
self$ctxPrevCtxStr(ctxId = ctxId, prevCtxId = NULL, type = type)
|
||||
},
|
||||
ctxPrevCtxStr = function(ctxId = NULL, prevCtxId = NULL, type = NULL, preCtxIdTxt = " in ") {
|
||||
if (self$isNotLogging()) return(NULL)
|
||||
if (self$isNotLogging()) {
|
||||
return(NULL)
|
||||
}
|
||||
paste0(
|
||||
if (!is.null(ctxId)) paste0(preCtxIdTxt, ctxId),
|
||||
if (!is.null(prevCtxId)) paste0(" from ", prevCtxId),
|
||||
@@ -502,7 +526,9 @@ MessageLogger = R6Class(
|
||||
)
|
||||
},
|
||||
log = function(...) {
|
||||
if (self$isNotLogging()) return(NULL)
|
||||
if (self$isNotLogging()) {
|
||||
return(NULL)
|
||||
}
|
||||
msg <- paste0(
|
||||
paste0(rep("= ", depth), collapse = ""), "- ", paste0(..., collapse = ""),
|
||||
collapse = ""
|
||||
|
||||
@@ -153,6 +153,12 @@ datePickerDependency <- function(theme) {
|
||||
)
|
||||
}
|
||||
|
||||
datePickerSass <- function() {
|
||||
sass::sass_file(
|
||||
system_file(package = "shiny", "www/shared/datepicker/scss/build3.scss")
|
||||
)
|
||||
}
|
||||
|
||||
datePickerCSS <- function(theme) {
|
||||
if (!is_bs_theme(theme)) {
|
||||
return(htmlDependency(
|
||||
@@ -164,10 +170,8 @@ datePickerCSS <- function(theme) {
|
||||
))
|
||||
}
|
||||
|
||||
scss_file <- system_file(package = "shiny", "www/shared/datepicker/scss/build3.scss")
|
||||
|
||||
bslib::bs_dependency(
|
||||
input = sass::sass_file(scss_file),
|
||||
input = datePickerSass(),
|
||||
theme = theme,
|
||||
name = "bootstrap-datepicker",
|
||||
version = version_bs_date_picker,
|
||||
|
||||
@@ -29,22 +29,36 @@
|
||||
#' A numeric vector of length 1.
|
||||
#'
|
||||
#' @export
|
||||
numericInput <- function(inputId, label, value, min = NA, max = NA, step = NA,
|
||||
width = NULL) {
|
||||
numericInput <- function(
|
||||
inputId,
|
||||
label,
|
||||
value,
|
||||
min = NA,
|
||||
max = NA,
|
||||
step = NA,
|
||||
width = NULL,
|
||||
...,
|
||||
updateOn = c("change", "blur")
|
||||
) {
|
||||
rlang::check_dots_empty()
|
||||
updateOn <- rlang::arg_match(updateOn)
|
||||
|
||||
value <- restoreInput(id = inputId, default = value)
|
||||
|
||||
# build input tag
|
||||
inputTag <- tags$input(id = inputId, type = "number", class="shiny-input-number form-control",
|
||||
value = formatNoSci(value))
|
||||
if (!is.na(min))
|
||||
inputTag$attribs$min = min
|
||||
if (!is.na(max))
|
||||
inputTag$attribs$max = max
|
||||
if (!is.na(step))
|
||||
inputTag$attribs$step = step
|
||||
inputTag <- tags$input(
|
||||
id = inputId,
|
||||
type = "number",
|
||||
class = "shiny-input-number form-control",
|
||||
value = formatNoSci(value),
|
||||
`data-update-on` = updateOn
|
||||
)
|
||||
if (!is.na(min)) inputTag$attribs$min = min
|
||||
if (!is.na(max)) inputTag$attribs$max = max
|
||||
if (!is.na(step)) inputTag$attribs$step = step
|
||||
|
||||
div(class = "form-group shiny-input-container",
|
||||
div(
|
||||
class = "form-group shiny-input-container",
|
||||
style = css(width = validateCssUnit(width)),
|
||||
shinyInputLabel(inputId, label),
|
||||
inputTag
|
||||
|
||||
@@ -30,12 +30,29 @@
|
||||
#' shinyApp(ui, server)
|
||||
#' }
|
||||
#' @export
|
||||
passwordInput <- function(inputId, label, value = "", width = NULL,
|
||||
placeholder = NULL) {
|
||||
div(class = "form-group shiny-input-container",
|
||||
passwordInput <- function(
|
||||
inputId,
|
||||
label,
|
||||
value = "",
|
||||
width = NULL,
|
||||
placeholder = NULL,
|
||||
...,
|
||||
updateOn = c("change", "blur")
|
||||
) {
|
||||
rlang::check_dots_empty()
|
||||
updateOn <- rlang::arg_match(updateOn)
|
||||
|
||||
div(
|
||||
class = "form-group shiny-input-container",
|
||||
style = css(width = validateCssUnit(width)),
|
||||
shinyInputLabel(inputId, label),
|
||||
tags$input(id = inputId, type="password", class="shiny-input-password form-control", value=value,
|
||||
placeholder = placeholder)
|
||||
tags$input(
|
||||
id = inputId,
|
||||
type = "password",
|
||||
class = "shiny-input-password form-control",
|
||||
value = value,
|
||||
placeholder = placeholder,
|
||||
`data-update-on` = updateOn
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -241,11 +241,8 @@ selectizeDependencyFunc <- function(theme) {
|
||||
return(selectizeStaticDependency(version_selectize))
|
||||
}
|
||||
|
||||
selectizeDir <- system_file(package = "shiny", "www/shared/selectize/")
|
||||
bs_version <- bslib::theme_version(theme)
|
||||
stylesheet <- file.path(
|
||||
selectizeDir, "scss", paste0("selectize.bootstrap", bs_version, ".scss")
|
||||
)
|
||||
|
||||
# It'd be cleaner to ship the JS in a separate, href-based,
|
||||
# HTML dependency (which we currently do for other themable widgets),
|
||||
# but DT, crosstalk, and maybe other pkgs include selectize JS/CSS
|
||||
@@ -253,10 +250,11 @@ selectizeDependencyFunc <- function(theme) {
|
||||
# name, the JS/CSS would be loaded/included twice, which leads to
|
||||
# strange issues, especially since we now include a 3rd party
|
||||
# accessibility plugin https://github.com/rstudio/shiny/pull/3153
|
||||
selectizeDir <- system_file(package = "shiny", "www/shared/selectize/")
|
||||
script <- file.path(selectizeDir, selectizeScripts())
|
||||
|
||||
bslib::bs_dependency(
|
||||
input = sass::sass_file(stylesheet),
|
||||
input = selectizeSass(bs_version),
|
||||
theme = theme,
|
||||
name = "selectize",
|
||||
version = version_selectize,
|
||||
@@ -265,6 +263,14 @@ selectizeDependencyFunc <- function(theme) {
|
||||
)
|
||||
}
|
||||
|
||||
selectizeSass <- function(bs_version) {
|
||||
selectizeDir <- system_file(package = "shiny", "www/shared/selectize/")
|
||||
stylesheet <- file.path(
|
||||
selectizeDir, "scss", paste0("selectize.bootstrap", bs_version, ".scss")
|
||||
)
|
||||
sass::sass_file(stylesheet)
|
||||
}
|
||||
|
||||
selectizeStaticDependency <- function(version) {
|
||||
htmlDependency(
|
||||
"selectize",
|
||||
|
||||
@@ -222,6 +222,15 @@ ionRangeSliderDependency <- function() {
|
||||
)
|
||||
}
|
||||
|
||||
ionRangeSliderDependencySass <- function() {
|
||||
list(
|
||||
list(accent = "$component-active-bg"),
|
||||
sass::sass_file(
|
||||
system_file(package = "shiny", "www/shared/ionrangeslider/scss/shiny.scss")
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
ionRangeSliderDependencyCSS <- function(theme) {
|
||||
if (!is_bs_theme(theme)) {
|
||||
return(htmlDependency(
|
||||
@@ -234,12 +243,7 @@ ionRangeSliderDependencyCSS <- function(theme) {
|
||||
}
|
||||
|
||||
bslib::bs_dependency(
|
||||
input = list(
|
||||
list(accent = "$component-active-bg"),
|
||||
sass::sass_file(
|
||||
system_file(package = "shiny", "www/shared/ionrangeslider/scss/shiny.scss")
|
||||
)
|
||||
),
|
||||
input = ionRangeSliderDependencySass(),
|
||||
theme = theme,
|
||||
name = "ionRangeSlider",
|
||||
version = version_ion_range_slider,
|
||||
|
||||
@@ -57,7 +57,7 @@ submitButton <- function(text = "Apply Changes", icon = NULL, width = NULL) {
|
||||
div(
|
||||
tags$button(
|
||||
type="submit",
|
||||
class="btn btn-primary",
|
||||
class="btn btn-primary shiny-submit-button",
|
||||
style = css(width = validateCssUnit(width)),
|
||||
list(icon, text)
|
||||
)
|
||||
|
||||
@@ -10,6 +10,14 @@
|
||||
#' @param placeholder A character string giving the user a hint as to what can
|
||||
#' be entered into the control. Internet Explorer 8 and 9 do not support this
|
||||
#' option.
|
||||
#' @param ... Ignored, included to require named arguments and for future
|
||||
#' feature expansion.
|
||||
#' @param updateOn A character vector specifying when the input should be
|
||||
#' updated. Options are `"change"` (default) and `"blur"`. Use `"change"` to
|
||||
#' update the input immediately whenever the value changes. Use `"blur"`to
|
||||
#' delay the input update until the input loses focus (the user moves away
|
||||
#' from the input), or when Enter is pressed (or Cmd/Ctrl + Enter for
|
||||
#' [textAreaInput()]).
|
||||
#' @return A text input control that can be added to a UI definition.
|
||||
#'
|
||||
#' @family input elements
|
||||
@@ -34,15 +42,31 @@
|
||||
#' unless `value` is provided.
|
||||
#'
|
||||
#' @export
|
||||
textInput <- function(inputId, label, value = "", width = NULL,
|
||||
placeholder = NULL) {
|
||||
textInput <- function(
|
||||
inputId,
|
||||
label,
|
||||
value = "",
|
||||
width = NULL,
|
||||
placeholder = NULL,
|
||||
...,
|
||||
updateOn = c("change", "blur")
|
||||
) {
|
||||
rlang::check_dots_empty()
|
||||
updateOn <- rlang::arg_match(updateOn)
|
||||
|
||||
value <- restoreInput(id = inputId, default = value)
|
||||
|
||||
div(class = "form-group shiny-input-container",
|
||||
div(
|
||||
class = "form-group shiny-input-container",
|
||||
style = css(width = validateCssUnit(width)),
|
||||
shinyInputLabel(inputId, label),
|
||||
tags$input(id = inputId, type="text", class="shiny-input-text form-control", value=value,
|
||||
placeholder = placeholder)
|
||||
tags$input(
|
||||
id = inputId,
|
||||
type = "text",
|
||||
class = "shiny-input-text form-control",
|
||||
value = value,
|
||||
placeholder = placeholder,
|
||||
`data-update-on` = updateOn
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
#' @param resize Which directions the textarea box can be resized. Can be one of
|
||||
#' `"both"`, `"none"`, `"vertical"`, and `"horizontal"`. The default, `NULL`,
|
||||
#' will use the client browser's default setting for resizing textareas.
|
||||
#' @param autoresize If `TRUE`, the textarea will automatically resize to fit
|
||||
#' the input text.
|
||||
#' @return A textarea input control that can be added to a UI definition.
|
||||
#'
|
||||
#' @family input elements
|
||||
@@ -41,8 +43,22 @@
|
||||
#' unless `value` is provided.
|
||||
#'
|
||||
#' @export
|
||||
textAreaInput <- function(inputId, label, value = "", width = NULL, height = NULL,
|
||||
cols = NULL, rows = NULL, placeholder = NULL, resize = NULL) {
|
||||
textAreaInput <- function(
|
||||
inputId,
|
||||
label,
|
||||
value = "",
|
||||
width = NULL,
|
||||
height = NULL,
|
||||
cols = NULL,
|
||||
rows = NULL,
|
||||
placeholder = NULL,
|
||||
resize = NULL,
|
||||
...,
|
||||
autoresize = FALSE,
|
||||
updateOn = c("change", "blur")
|
||||
) {
|
||||
rlang::check_dots_empty()
|
||||
updateOn <- rlang::arg_match(updateOn)
|
||||
|
||||
value <- restoreInput(id = inputId, default = value)
|
||||
|
||||
@@ -50,23 +66,30 @@ textAreaInput <- function(inputId, label, value = "", width = NULL, height = NUL
|
||||
resize <- match.arg(resize, c("both", "none", "vertical", "horizontal"))
|
||||
}
|
||||
|
||||
style <- css(
|
||||
# The width is specified on the parent div.
|
||||
width = if (!is.null(width)) "100%",
|
||||
height = validateCssUnit(height),
|
||||
resize = resize
|
||||
)
|
||||
classes <- "form-control"
|
||||
if (autoresize) {
|
||||
classes <- c(classes, "textarea-autoresize")
|
||||
if (is.null(rows)) {
|
||||
rows <- 1
|
||||
}
|
||||
}
|
||||
|
||||
div(class = "form-group shiny-input-container",
|
||||
div(
|
||||
class = "shiny-input-textarea form-group shiny-input-container",
|
||||
style = css(width = validateCssUnit(width)),
|
||||
shinyInputLabel(inputId, label),
|
||||
style = if (!is.null(width)) paste0("width: ", validateCssUnit(width), ";"),
|
||||
tags$textarea(
|
||||
id = inputId,
|
||||
class = "shiny-input-textarea form-control",
|
||||
class = classes,
|
||||
placeholder = placeholder,
|
||||
style = style,
|
||||
style = css(
|
||||
width = if (!is.null(width)) "100%",
|
||||
height = validateCssUnit(height),
|
||||
resize = resize
|
||||
),
|
||||
rows = rows,
|
||||
cols = cols,
|
||||
`data-update-on` = updateOn,
|
||||
value
|
||||
)
|
||||
)
|
||||
|
||||
14
R/react.R
@@ -53,10 +53,12 @@ Context <- R6Class(
|
||||
|
||||
promises::with_promise_domain(reactivePromiseDomain(), {
|
||||
withReactiveDomain(.domain, {
|
||||
env <- .getReactiveEnvironment()
|
||||
rLog$enter(.reactId, id, .reactType, .domain)
|
||||
on.exit(rLog$exit(.reactId, id, .reactType, .domain), add = TRUE)
|
||||
env$runWith(self, func)
|
||||
captureStackTraces({
|
||||
env <- .getReactiveEnvironment()
|
||||
rLog$enter(.reactId, id, .reactType, .domain)
|
||||
on.exit(rLog$exit(.reactId, id, .reactType, .domain), add = TRUE)
|
||||
env$runWith(self, func)
|
||||
})
|
||||
})
|
||||
})
|
||||
},
|
||||
@@ -223,9 +225,7 @@ wrapForContext <- function(func, ctx) {
|
||||
|
||||
function(...) {
|
||||
.getReactiveEnvironment()$runWith(ctx, function() {
|
||||
captureStackTraces(
|
||||
func(...)
|
||||
)
|
||||
func(...)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -951,7 +951,10 @@ Observable <- R6Class(
|
||||
#' See the [Shiny tutorial](https://shiny.rstudio.com/tutorial/) for
|
||||
#' more information about reactive expressions.
|
||||
#'
|
||||
#' @param x For `is.reactive()`, an object to test. For `reactive()`, an expression. When passing in a [`quo()`]sure with `reactive()`, remember to use [`rlang::inject()`] to distinguish that you are passing in the content of your quosure, not the expression of the quosure.
|
||||
#' @param x For `is.reactive()`, an object to test. For `reactive()`, an
|
||||
#' expression. When passing in a [`rlang::quo()`]sure with `reactive()`,
|
||||
#' remember to use [`rlang::inject()`] to distinguish that you are passing in
|
||||
#' the content of your quosure, not the expression of the quosure.
|
||||
#' @template param-env
|
||||
#' @templateVar x x
|
||||
#' @templateVar env env
|
||||
@@ -2301,7 +2304,7 @@ observeEvent <- function(eventExpr, handlerExpr,
|
||||
priority = priority,
|
||||
domain = domain,
|
||||
autoDestroy = TRUE,
|
||||
..stacktraceon = FALSE # TODO: Does this go in the bindEvent?
|
||||
..stacktraceon = TRUE
|
||||
))
|
||||
|
||||
o <- inject(bindEvent(
|
||||
|
||||
@@ -266,6 +266,8 @@ drawPlot <- function(name, session, func, width, height, alt, pixelratio, res, .
|
||||
# addition to ggplot, and there's a print method for that class, that we
|
||||
# won't override that method. https://github.com/rstudio/shiny/issues/841
|
||||
print.ggplot <- custom_print.ggplot
|
||||
# For compatibility with ggplot2 >v4.0.0
|
||||
`print.ggplot2::ggplot` <- custom_print.ggplot
|
||||
|
||||
# Use capture.output to squelch printing to the actual console; we
|
||||
# are only interested in plot output
|
||||
|
||||
@@ -445,7 +445,9 @@ stopApp <- function(returnValue = invisible()) {
|
||||
#' @param host The IPv4 address that the application should listen on. Defaults
|
||||
#' to the `shiny.host` option, if set, or `"127.0.0.1"` if not.
|
||||
#' @param display.mode The mode in which to display the example. Defaults to
|
||||
#' `showcase`, but may be set to `normal` to see the example without
|
||||
#' `"auto"`, which uses the value of `DisplayMode` in the example's
|
||||
#' `DESCRIPTION` file. Set to `"showcase"` to show the app code and
|
||||
#' description with the running app, or `"normal"` to see the example without
|
||||
#' code or commentary.
|
||||
#' @param package The package in which to find the example (defaults to
|
||||
#' `"shiny"`).
|
||||
|
||||
@@ -65,16 +65,20 @@ getShinyOption <- function(name, default = NULL) {
|
||||
#' changes are detected, all connected Shiny sessions are reloaded. This
|
||||
#' allows for fast feedback loops when tweaking Shiny UI.
|
||||
#'
|
||||
#' Since monitoring for changes is expensive (we simply poll for last
|
||||
#' modified times), this feature is intended only for development.
|
||||
#' Monitoring for changes is no longer expensive, thanks to the \pkg{watcher}
|
||||
#' package, but this feature is still intended only for development.
|
||||
#'
|
||||
#' You can customize the file patterns Shiny will monitor by setting the
|
||||
#' shiny.autoreload.pattern option. For example, to monitor only ui.R:
|
||||
#' `options(shiny.autoreload.pattern = glob2rx("ui.R"))`
|
||||
#' shiny.autoreload.pattern option. For example, to monitor only `ui.R`:
|
||||
#' `options(shiny.autoreload.pattern = glob2rx("ui.R"))`.
|
||||
#'
|
||||
#' The default polling interval is 500 milliseconds. You can change this
|
||||
#' by setting e.g. `options(shiny.autoreload.interval = 2000)` (every
|
||||
#' two seconds).}
|
||||
#' As mentioned above, Shiny no longer polls watched files for changes.
|
||||
#' Instead, using \pkg{watcher}, Shiny is notified of file changes as they
|
||||
#' occur. These changes are batched together within a customizable latency
|
||||
#' period. You can adjust this period by setting
|
||||
#' `options(shiny.autoreload.interval = 2000)` (in milliseconds). This value
|
||||
#' converted to seconds and passed to the `latency` argument of
|
||||
#' [watcher::watcher()]. The default latency is 250ms.}
|
||||
#' \item{shiny.deprecation.messages (defaults to `TRUE`)}{This controls whether messages for
|
||||
#' deprecated functions in Shiny will be printed. See
|
||||
#' [shinyDeprecated()] for more information.}
|
||||
@@ -151,6 +155,11 @@ getShinyOption <- function(name, default = NULL) {
|
||||
# ' \item{shiny.devmode.verbose (defaults to `TRUE`)}{If `TRUE`, will display messages printed
|
||||
# ' about which options are being set. See [devmode()] for more details. }
|
||||
### (end not documenting 'shiny.devmode.verbose')
|
||||
### start shiny.client_devmode is primarily for niche, internal shinylive usage
|
||||
# ' \item{shiny.client_devmode (defaults to `FALSE`)}{If `TRUE`, enables client-
|
||||
# ' side devmode features. Currently the primary feature is the client-side
|
||||
# ' error console.}
|
||||
### end shiny.client_devmode
|
||||
#' }
|
||||
#'
|
||||
#'
|
||||
|
||||
@@ -2024,7 +2024,7 @@ ShinySession <- R6Class(
|
||||
tmpdata <- tempfile(fileext = ext)
|
||||
return(Context$new(getDefaultReactiveDomain(), '[download]')$run(function() {
|
||||
promises::with_promise_domain(reactivePromiseDomain(), {
|
||||
promises::with_promise_domain(createStackTracePromiseDomain(), {
|
||||
captureStackTraces({
|
||||
self$incrementBusyCount()
|
||||
hybrid_chain(
|
||||
# ..stacktraceon matches with the top-level ..stacktraceoff..
|
||||
|
||||
114
R/shinyapp.R
@@ -162,11 +162,29 @@ shinyAppDir_serverR <- function(appDir, options=list()) {
|
||||
sharedEnv <- globalenv()
|
||||
}
|
||||
|
||||
# To enable hot-reloading of support files, this function is called
|
||||
# whenever the UI or Server func source is updated. To avoid loading
|
||||
# support files 2x, we follow the last cache update trigger timestamp.
|
||||
autoload_r_support_if_needed <- local({
|
||||
autoload_last_loaded <- -1
|
||||
function() {
|
||||
if (!isTRUE(getOption("shiny.autoload.r", TRUE))) return()
|
||||
|
||||
last_cache_trigger <- cachedAutoReloadLastChanged$get()
|
||||
if (identical(autoload_last_loaded, last_cache_trigger)) return()
|
||||
|
||||
loadSupport(appDir, renv = sharedEnv, globalrenv = globalenv())
|
||||
|
||||
autoload_last_loaded <<- last_cache_trigger
|
||||
}
|
||||
})
|
||||
|
||||
# uiHandlerSource is a function that returns an HTTP handler for serving up
|
||||
# ui.R as a webpage. The "cachedFuncWithFile" call makes sure that the closure
|
||||
# we're creating here only gets executed when ui.R's contents change.
|
||||
uiHandlerSource <- cachedFuncWithFile(appDir, "ui.R", case.sensitive = FALSE,
|
||||
function(uiR) {
|
||||
autoload_r_support_if_needed()
|
||||
if (file.exists(uiR)) {
|
||||
# If ui.R contains a call to shinyUI (which sets .globals$ui), use that.
|
||||
# If not, then take the last expression that's returned from ui.R.
|
||||
@@ -197,6 +215,7 @@ shinyAppDir_serverR <- function(appDir, options=list()) {
|
||||
|
||||
serverSource <- cachedFuncWithFile(appDir, "server.R", case.sensitive = FALSE,
|
||||
function(serverR) {
|
||||
autoload_r_support_if_needed()
|
||||
# If server.R contains a call to shinyServer (which sets .globals$server),
|
||||
# use that. If not, then take the last expression that's returned from
|
||||
# server.R.
|
||||
@@ -232,10 +251,9 @@ shinyAppDir_serverR <- function(appDir, options=list()) {
|
||||
onStart <- function() {
|
||||
oldwd <<- getwd()
|
||||
setwd(appDir)
|
||||
# TODO: we should support hot reloading on global.R and R/*.R changes.
|
||||
if (getOption("shiny.autoload.r", TRUE)) {
|
||||
loadSupport(appDir, renv=sharedEnv, globalrenv=globalenv())
|
||||
} else {
|
||||
autoload_r_support_if_needed()
|
||||
} else {
|
||||
if (file.exists(file.path.ci(appDir, "global.R")))
|
||||
sourceUTF8(file.path.ci(appDir, "global.R"))
|
||||
}
|
||||
@@ -290,33 +308,77 @@ initAutoReloadMonitor <- function(dir) {
|
||||
return(function(){})
|
||||
}
|
||||
|
||||
filePattern <- getOption("shiny.autoreload.pattern",
|
||||
".*\\.(r|html?|js|css|png|jpe?g|gif)$")
|
||||
filePattern <- getOption(
|
||||
"shiny.autoreload.pattern",
|
||||
".*\\.(r|html?|js|css|png|jpe?g|gif)$"
|
||||
)
|
||||
|
||||
lastValue <- NULL
|
||||
observeLabel <- paste0("File Auto-Reload - '", basename(dir), "'")
|
||||
obs <- observe(label = observeLabel, {
|
||||
files <- sort_c(
|
||||
list.files(dir, pattern = filePattern, recursive = TRUE, ignore.case = TRUE)
|
||||
)
|
||||
times <- file.info(files)$mtime
|
||||
names(times) <- files
|
||||
|
||||
if (is.null(lastValue)) {
|
||||
# First run
|
||||
lastValue <<- times
|
||||
} else if (!identical(lastValue, times)) {
|
||||
# We've changed!
|
||||
lastValue <<- times
|
||||
|
||||
if (is_installed("watcher")) {
|
||||
check_for_update <- function(paths) {
|
||||
paths <- grep(
|
||||
filePattern,
|
||||
paths,
|
||||
ignore.case = TRUE,
|
||||
value = TRUE
|
||||
)
|
||||
|
||||
if (length(paths) == 0) {
|
||||
return()
|
||||
}
|
||||
|
||||
cachedAutoReloadLastChanged$set()
|
||||
autoReloadCallbacks$invoke()
|
||||
}
|
||||
|
||||
# [garrick, 2025-02-20] Shiny <= v1.10.0 used `invalidateLater()` with an
|
||||
# autoreload.interval in ms. {watcher} instead uses a latency parameter in
|
||||
# seconds, which serves a similar purpose and that I'm keeping for backcompat.
|
||||
latency <- getOption("shiny.autoreload.interval", 250) / 1000
|
||||
watcher <- watcher::watcher(dir, check_for_update, latency = latency)
|
||||
watcher$start()
|
||||
onStop(watcher$stop)
|
||||
} else {
|
||||
# Fall back to legacy observer behavior
|
||||
if (!is_false(getOption("shiny.autoreload.legacy_warning", TRUE))) {
|
||||
cli::cli_warn(
|
||||
c(
|
||||
"Using legacy autoreload file watching. Please install {.pkg watcher} for a more performant autoreload file watcher.",
|
||||
"i" = "Set {.run options(shiny.autoreload.legacy_warning = FALSE)} to suppress this warning."
|
||||
),
|
||||
.frequency = "regularly",
|
||||
.frequency_id = "shiny.autoreload.legacy_warning"
|
||||
)
|
||||
}
|
||||
|
||||
invalidateLater(getOption("shiny.autoreload.interval", 500))
|
||||
})
|
||||
lastValue <- NULL
|
||||
observeLabel <- paste0("File Auto-Reload - '", basename(dir), "'")
|
||||
watcher <- observe(label = observeLabel, {
|
||||
files <- sort_c(
|
||||
list.files(dir, pattern = filePattern, recursive = TRUE, ignore.case = TRUE)
|
||||
)
|
||||
times <- file.info(files)$mtime
|
||||
names(times) <- files
|
||||
|
||||
if (is.null(lastValue)) {
|
||||
# First run
|
||||
lastValue <<- times
|
||||
} else if (!identical(lastValue, times)) {
|
||||
# We've changed!
|
||||
lastValue <<- times
|
||||
cachedAutoReloadLastChanged$set()
|
||||
autoReloadCallbacks$invoke()
|
||||
}
|
||||
|
||||
invalidateLater(getOption("shiny.autoreload.interval", 500))
|
||||
})
|
||||
|
||||
onStop(watcher$destroy)
|
||||
|
||||
watcher$destroy
|
||||
}
|
||||
|
||||
onStop(obs$destroy)
|
||||
|
||||
obs$destroy
|
||||
invisible(watcher)
|
||||
}
|
||||
|
||||
#' Load an app's supporting R files
|
||||
@@ -421,8 +483,6 @@ shinyAppDir_appR <- function(fileName, appDir, options=list())
|
||||
wasDir <- setwd(appDir)
|
||||
on.exit(setwd(wasDir))
|
||||
|
||||
# TODO: we should support hot reloading on R/*.R changes.
|
||||
# In an upcoming version of shiny, this option will go away.
|
||||
if (getOption("shiny.autoload.r", TRUE)) {
|
||||
# Create a child env which contains all the helpers and will be the shared parent
|
||||
# of the ui.R and server.R load.
|
||||
|
||||
17
R/shinyui.R
@@ -69,7 +69,7 @@ renderPage <- function(ui, showcase=0, testMode=FALSE) {
|
||||
)
|
||||
}
|
||||
|
||||
if (in_devmode()) {
|
||||
if (in_devmode() || in_client_devmode()) {
|
||||
# If we're in dev mode, add a simple script to the head that injects a
|
||||
# global variable for the client to use to detect dev mode.
|
||||
shiny_deps[[length(shiny_deps) + 1]] <-
|
||||
@@ -135,6 +135,14 @@ shinyDependencies <- function() {
|
||||
)
|
||||
}
|
||||
|
||||
shinyDependencySass <- function(bs_version) {
|
||||
bootstrap_scss <- paste0("shiny.bootstrap", bs_version, ".scss")
|
||||
|
||||
scss_home <- system_file("www/shared/shiny_scss", package = "shiny")
|
||||
scss_files <- file.path(scss_home, c(bootstrap_scss, "shiny.scss"))
|
||||
lapply(scss_files, sass::sass_file)
|
||||
}
|
||||
|
||||
shinyDependencyCSS <- function(theme) {
|
||||
version <- get_package_version("shiny")
|
||||
|
||||
@@ -150,14 +158,9 @@ shinyDependencyCSS <- function(theme) {
|
||||
}
|
||||
|
||||
bs_version <- bslib::theme_version(theme)
|
||||
bootstrap_scss <- paste0("shiny.bootstrap", bs_version, ".scss")
|
||||
|
||||
scss_home <- system_file("www/shared/shiny_scss", package = "shiny")
|
||||
scss_files <- file.path(scss_home, c(bootstrap_scss, "shiny.scss"))
|
||||
scss_files <- lapply(scss_files, sass::sass_file)
|
||||
|
||||
bslib::bs_dependency(
|
||||
input = scss_files,
|
||||
input = shinyDependencySass(bs_version),
|
||||
theme = theme,
|
||||
name = "shiny-sass",
|
||||
version = version,
|
||||
|
||||
@@ -383,8 +383,10 @@ markOutputAttrs <- function(renderFunc, snapshotExclude = NULL,
|
||||
#' The corresponding HTML output tag should be `div` or `img` and have
|
||||
#' the CSS class name `shiny-image-output`.
|
||||
#'
|
||||
#' @seealso For more details on how the images are generated, and how to control
|
||||
#' @seealso
|
||||
#' * For more details on how the images are generated, and how to control
|
||||
#' the output, see [plotPNG()].
|
||||
#' * Use [outputOptions()] to set general output options for an image output.
|
||||
#'
|
||||
#' @param expr An expression that returns a list.
|
||||
#' @inheritParams renderUI
|
||||
@@ -598,6 +600,7 @@ isTemp <- function(path, tempDir = tempdir(), mustExist) {
|
||||
#' used in an interactive RMarkdown document.
|
||||
#'
|
||||
#' @example res/text-example.R
|
||||
#' @seealso [outputOptions()]
|
||||
#' @export
|
||||
renderPrint <- function(expr, env = parent.frame(), quoted = FALSE,
|
||||
width = getOption('width'), outputArgs=list())
|
||||
@@ -719,7 +722,7 @@ renderText <- function(expr, env = parent.frame(), quoted = FALSE,
|
||||
#' call to [uiOutput()] when `renderUI` is used in an
|
||||
#' interactive R Markdown document.
|
||||
#'
|
||||
#' @seealso [uiOutput()]
|
||||
#' @seealso [uiOutput()], [outputOptions()]
|
||||
#' @export
|
||||
#' @examples
|
||||
#' ## Only run examples in interactive R sessions
|
||||
@@ -809,6 +812,13 @@ renderUI <- function(expr, env = parent.frame(), quoted = FALSE,
|
||||
#'
|
||||
#' shinyApp(ui, server)
|
||||
#' }
|
||||
#'
|
||||
#' @seealso
|
||||
#' * The download handler, like other outputs, is suspended (disabled) by
|
||||
#' default for download buttons and links that are hidden. Use
|
||||
#' [outputOptions()] to control this behavior, e.g. to set
|
||||
#' `suspendWhenHidden = FALSE` if the download is initiated by
|
||||
#' programmatically clicking on the download button using JavaScript.
|
||||
#' @export
|
||||
downloadHandler <- function(filename, content, contentType=NULL, outputArgs=list()) {
|
||||
renderFunc <- function(shinysession, name, ...) {
|
||||
|
||||
8
R/test.R
@@ -158,8 +158,7 @@ print.shiny_runtests <- function(x, ..., reporter = "summary") {
|
||||
|
||||
|
||||
if (any(x$pass)) {
|
||||
# TODO in future... use clisymbols::symbol$tick and crayon green
|
||||
cat("* Success\n")
|
||||
cli::cat_bullet("Success", bullet = "tick", bullet_col = "green")
|
||||
mapply(
|
||||
x$file,
|
||||
x$pass,
|
||||
@@ -171,9 +170,8 @@ print.shiny_runtests <- function(x, ..., reporter = "summary") {
|
||||
}
|
||||
)
|
||||
}
|
||||
if (any(!x$pass)) {
|
||||
# TODO in future... use clisymbols::symbol$cross and crayon red
|
||||
cat("* Failure\n")
|
||||
if (!all(x$pass)) {
|
||||
cli::cat_bullet("Failure", bullet = "cross", bullet_col = "red")
|
||||
mapply(
|
||||
x$file,
|
||||
x$pass,
|
||||
|
||||
@@ -37,7 +37,11 @@
|
||||
updateTextInput <- function(session = getDefaultReactiveDomain(), inputId, label = NULL, value = NULL, placeholder = NULL) {
|
||||
validate_session_object(session)
|
||||
|
||||
message <- dropNulls(list(label=label, value=value, placeholder=placeholder))
|
||||
message <- dropNulls(list(
|
||||
label = processDeps(label, session),
|
||||
value = value,
|
||||
placeholder = placeholder
|
||||
))
|
||||
session$sendInputMessage(inputId, message)
|
||||
}
|
||||
|
||||
@@ -111,7 +115,10 @@ updateTextAreaInput <- updateTextInput
|
||||
updateCheckboxInput <- function(session = getDefaultReactiveDomain(), inputId, label = NULL, value = NULL) {
|
||||
validate_session_object(session)
|
||||
|
||||
message <- dropNulls(list(label=label, value=value))
|
||||
message <- dropNulls(list(
|
||||
label = processDeps(label, session),
|
||||
value = value
|
||||
))
|
||||
session$sendInputMessage(inputId, message)
|
||||
}
|
||||
|
||||
@@ -175,13 +182,17 @@ updateActionButton <- function(session = getDefaultReactiveDomain(), inputId, la
|
||||
validate_session_object(session)
|
||||
|
||||
if (!is.null(icon)) icon <- as.character(validateIcon(icon))
|
||||
message <- dropNulls(list(label=label, icon=icon, disabled=disabled))
|
||||
message <- dropNulls(list(
|
||||
label = processDeps(label, session),
|
||||
icon = icon,
|
||||
disabled = disabled
|
||||
))
|
||||
session$sendInputMessage(inputId, message)
|
||||
}
|
||||
#' @rdname updateActionButton
|
||||
#' @export
|
||||
updateActionLink <- function(session = getDefaultReactiveDomain(), inputId, label = NULL, icon = NULL) {
|
||||
updateActionButton(session, inputId=inputId, label=label, icon=icon)
|
||||
updateActionButton(session, inputId = inputId, label = processDeps(label, session), icon = icon)
|
||||
}
|
||||
|
||||
|
||||
@@ -225,7 +236,12 @@ updateDateInput <- function(session = getDefaultReactiveDomain(), inputId, label
|
||||
min <- dateYMD(min, "min")
|
||||
max <- dateYMD(max, "max")
|
||||
|
||||
message <- dropNulls(list(label=label, value=value, min=min, max=max))
|
||||
message <- dropNulls(list(
|
||||
label = processDeps(label, session),
|
||||
value = value,
|
||||
min = min,
|
||||
max = max
|
||||
))
|
||||
session$sendInputMessage(inputId, message)
|
||||
}
|
||||
|
||||
@@ -275,7 +291,7 @@ updateDateRangeInput <- function(session = getDefaultReactiveDomain(), inputId,
|
||||
max <- dateYMD(max, "max")
|
||||
|
||||
message <- dropNulls(list(
|
||||
label = label,
|
||||
label = processDeps(label, session),
|
||||
value = dropNulls(list(start = start, end = end)),
|
||||
min = min,
|
||||
max = max
|
||||
@@ -374,13 +390,16 @@ updateNavlistPanel <- updateTabsetPanel
|
||||
#' }
|
||||
#' @export
|
||||
updateNumericInput <- function(session = getDefaultReactiveDomain(), inputId, label = NULL, value = NULL,
|
||||
min = NULL, max = NULL, step = NULL) {
|
||||
min = NULL, max = NULL, step = NULL) {
|
||||
|
||||
validate_session_object(session)
|
||||
|
||||
message <- dropNulls(list(
|
||||
label = label, value = formatNoSci(value),
|
||||
min = formatNoSci(min), max = formatNoSci(max), step = formatNoSci(step)
|
||||
label = processDeps(label, session),
|
||||
value = formatNoSci(value),
|
||||
min = formatNoSci(min),
|
||||
max = formatNoSci(max),
|
||||
step = formatNoSci(step)
|
||||
))
|
||||
session$sendInputMessage(inputId, message)
|
||||
}
|
||||
@@ -460,7 +479,7 @@ updateSliderInput <- function(session = getDefaultReactiveDomain(), inputId, lab
|
||||
}
|
||||
|
||||
message <- dropNulls(list(
|
||||
label = label,
|
||||
label = processDeps(label, session),
|
||||
value = formatNoSci(value),
|
||||
min = formatNoSci(min),
|
||||
max = formatNoSci(max),
|
||||
@@ -491,7 +510,11 @@ updateInputOptions <- function(session, inputId, label = NULL, choices = NULL,
|
||||
))
|
||||
}
|
||||
|
||||
message <- dropNulls(list(label = label, options = options, value = selected))
|
||||
message <- dropNulls(list(
|
||||
label = processDeps(label, session),
|
||||
options = options,
|
||||
value = selected
|
||||
))
|
||||
|
||||
session$sendInputMessage(inputId, message)
|
||||
}
|
||||
@@ -644,7 +667,11 @@ updateSelectInput <- function(session = getDefaultReactiveDomain(), inputId, lab
|
||||
choices <- if (!is.null(choices)) choicesWithNames(choices)
|
||||
if (!is.null(selected)) selected <- as.character(selected)
|
||||
options <- if (!is.null(choices)) selectOptions(choices, selected, inputId, FALSE)
|
||||
message <- dropNulls(list(label = label, options = options, value = selected))
|
||||
message <- dropNulls(list(
|
||||
label = processDeps(label, session),
|
||||
options = options,
|
||||
value = selected
|
||||
))
|
||||
session$sendInputMessage(inputId, message)
|
||||
}
|
||||
|
||||
|
||||
@@ -53,8 +53,8 @@ formalsAndBody <- function(x) {
|
||||
|
||||
#' @describeIn createRenderFunction convert a quosure to a function.
|
||||
#' @param q Quosure of the expression `x`. When capturing expressions to create
|
||||
#' your quosure, it is recommended to use [`enquo0()`] to not unquote the
|
||||
#' object too early. See [`enquo0()`] for more details.
|
||||
#' your quosure, it is recommended to use [`rlang::enquo0()`] to not unquote
|
||||
#' the object too early. See [`rlang::enquo0()`] for more details.
|
||||
#' @inheritParams installExprFunction
|
||||
#' @export
|
||||
quoToFunction <- function(
|
||||
|
||||
37
R/utils.R
@@ -770,22 +770,45 @@ formatNoSci <- function(x) {
|
||||
format(x, scientific = FALSE, digits = 15)
|
||||
}
|
||||
|
||||
# A simple getter/setting to track the last time the auto-reload process
|
||||
# updated. This value is used by `cachedFuncWithFile()` when auto-reload is
|
||||
# enabled to reload app/ui/server files when watched supporting files change.
|
||||
cachedAutoReloadLastChanged <- local({
|
||||
last_update <- 0
|
||||
|
||||
list(
|
||||
set = function() {
|
||||
last_update <<- as.integer(Sys.time())
|
||||
invisible(last_update)
|
||||
},
|
||||
get = function() {
|
||||
last_update
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
# Returns a function that calls the given func and caches the result for
|
||||
# subsequent calls, unless the given file's mtime changes.
|
||||
cachedFuncWithFile <- function(dir, file, func, case.sensitive = FALSE) {
|
||||
dir <- normalizePath(dir, mustWork=TRUE)
|
||||
mtime <- NA
|
||||
dir <- normalizePath(dir, mustWork = TRUE)
|
||||
|
||||
value <- NULL
|
||||
last_mtime_file <- NA
|
||||
last_autoreload <- 0
|
||||
|
||||
function(...) {
|
||||
fname <- if (case.sensitive)
|
||||
file.path(dir, file)
|
||||
else
|
||||
fname <- if (case.sensitive) {
|
||||
file.path(dir, file)
|
||||
} else {
|
||||
file.path.ci(dir, file)
|
||||
}
|
||||
|
||||
now <- file.info(fname)$mtime
|
||||
if (!identical(mtime, now)) {
|
||||
autoreload <- last_autoreload < cachedAutoReloadLastChanged$get()
|
||||
if (autoreload || !identical(last_mtime_file, now)) {
|
||||
value <<- func(fname, ...)
|
||||
mtime <<- now
|
||||
last_mtime_file <<- now
|
||||
last_autoreload <<- cachedAutoReloadLastChanged$get()
|
||||
}
|
||||
value
|
||||
}
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
# Generated by tools/updatejQuery.R; do not edit by hand
|
||||
version_jquery <- "3.6.0"
|
||||
version_jquery <- "3.7.1"
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
# Generated by tools/updatejQueryUI.R; do not edit by hand
|
||||
version_jqueryui <- "1.13.2"
|
||||
version_jqueryui <- "1.14.1"
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
{
|
||||
"presets": [
|
||||
"@babel/preset-typescript",
|
||||
[
|
||||
"@babel/preset-env",
|
||||
{
|
||||
"useBuiltIns": "usage",
|
||||
"corejs": "3.12"
|
||||
}
|
||||
]
|
||||
],
|
||||
"ignore":[
|
||||
"node_modules/core-js"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
## revdepcheck results
|
||||
|
||||
We checked 1278 reverse dependencies (1277 from CRAN + 1 from Bioconductor), comparing R CMD check results across CRAN and dev versions of shiny.
|
||||
|
||||
* We saw 2 new problems (NOTEs only)
|
||||
* We failed to check 19 packages due to installation issues
|
||||
|
||||
Issues with CRAN packages are summarised below.
|
||||
|
||||
### New problems
|
||||
|
||||
R CMD check displayed NOTEs for two packages, unrelated to changes in shiny.
|
||||
|
||||
* HH
|
||||
checking installed package size ... NOTE
|
||||
|
||||
* PopED
|
||||
checking installed package size ... NOTE
|
||||
|
||||
### Failed to check
|
||||
|
||||
* animalEKF
|
||||
* AovBay
|
||||
* Certara.VPCResults
|
||||
* chipPCR
|
||||
* ctsem
|
||||
* dartR.sim
|
||||
* diveR
|
||||
* gap
|
||||
* jsmodule
|
||||
* loon.shiny
|
||||
* robmedExtra
|
||||
* rstanarm
|
||||
* SensMap
|
||||
* Seurat
|
||||
* shinyTempSignal
|
||||
* Signac
|
||||
* statsr
|
||||
* TestAnaAPP
|
||||
* tidyvpc
|
||||
@@ -5,7 +5,7 @@ test_that("Initial snapshot values are consistent", {
|
||||
app$expect_values()
|
||||
}){{
|
||||
if (isTRUE(module)) {
|
||||
HTML('
|
||||
shiny::HTML('
|
||||
|
||||
|
||||
test_that("Module values are consistent", {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<script src="shared/jquery.js" type="text/javascript"></script>
|
||||
<script src="shared/shiny.js" type="text/javascript"></script>
|
||||
<link rel="stylesheet" type="text/css" href="shared/shiny.css"/>
|
||||
<script src="shared/jquery.min.js" type="text/javascript"></script>
|
||||
<script src="shared/shiny.min.js" type="text/javascript"></script>
|
||||
<link rel="stylesheet" type="text/css" href="shared/shiny.min.css"/>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
/*! shiny 1.8.1.9001 | (c) 2012-2024 RStudio, PBC. | License: GPL-3 | file LICENSE */
|
||||
:where([data-shiny-busy-spinners] .recalculating){position:relative}[data-shiny-busy-spinners] .recalculating{opacity:1}[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}[data-shiny-busy-spinners] .recalculating>*:not(.recalculating){opacity:.2;transition:opacity .25s ease var(--shiny-spinner-delay, 1s)}[data-shiny-busy-spinners] .recalculating.shiny-html-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, var(--bs-body-bg, #fff), var(--bs-indigo, #4b00c1), var(--bs-purple, #74149c), var(--bs-pink, #bf007f), var(--bs-body-bg, #fff) ) );--_shiny-pulse-height: var(--shiny-pulse-height, 5px);--_shiny-pulse-speed: var(--shiny-pulse-speed, 1.85s);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-iteration-count:infinite;animation-timing-function:ease-in-out;content:""}[data-shiny-busy-spinners][data-shiny-busy-pulse].shiny-busy:has(.recalculating):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, var(--bs-body-bg, #fff), var(--bs-indigo, #4b00c1), var(--bs-purple, #74149c), var(--bs-pink, #bf007f), var(--bs-body-bg, #fff) ) );--_shiny-pulse-height: var(--shiny-pulse-height, 5px);--_shiny-pulse-speed: var(--shiny-pulse-speed, 1.85s);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-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:-75%;width:75%}50%{left:100%;width:75%}to{left:-75%;width:75%}}
|
||||
/*! shiny 1.10.0.9001 | (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}
|
||||
|
||||
@@ -235,6 +235,10 @@
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.irs--shiny .irs-handle.type_last {
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
.irs--shiny .irs-handle.state_hover, .irs--shiny .irs-handle:hover {
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
@@ -143,6 +143,11 @@ $font-family: $font-family-base !default;
|
||||
border-radius: $handle_width;
|
||||
z-index: 2;
|
||||
|
||||
&.type_last {
|
||||
// Ensure last-used handle is on top if it overlaps with another handle
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
&.state_hover,
|
||||
&:hover {
|
||||
background: $handle_color_hover;
|
||||
|
||||
2103
inst/www/shared/jquery.js
vendored
4
inst/www/shared/jquery.min.js
vendored
@@ -1,5 +1,5 @@
|
||||
Authors ordered by first contribution
|
||||
A list of current team members is available at http://jqueryui.com/about
|
||||
A list of current team members is available at https://jqueryui.com/about
|
||||
|
||||
Paul Bakaus <paul.bakaus@gmail.com>
|
||||
Richard Worth <rdworth@gmail.com>
|
||||
@@ -42,7 +42,7 @@ Adam Sontag <ajpiano@ajpiano.com>
|
||||
Carl Fürstenberg <carl@excito.com>
|
||||
Kevin Dalman <development@allpro.net>
|
||||
Alberto Fernández Capel <afcapel@gmail.com>
|
||||
Jacek Jędrzejewski (http://jacek.jedrzejewski.name)
|
||||
Jacek Jędrzejewski (https://jacek.jedrzejewski.name)
|
||||
Ting Kuei <ting@kuei.com>
|
||||
Samuel Cormier-Iijima <sam@chide.it>
|
||||
Jon Palmer <jonspalmer@gmail.com>
|
||||
@@ -370,3 +370,15 @@ dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
|
||||
Adam Lidén Hällgren <adamlh92@gmail.com>
|
||||
James Hinderks <hinderks@gmail.com>
|
||||
Denny Septian Panggabean <97607754+ddevsr@users.noreply.github.com>
|
||||
Matías Cánepa <matias.canepa@gmail.com>
|
||||
Ashish Kurmi <100655670+boahc077@users.noreply.github.com>
|
||||
DeerBear <andrea.raimondi@gmail.com>
|
||||
Дилян Палаузов <dpa-github@aegee.org>
|
||||
Kenneth DeBacker <kcdebacker@gmail.com>
|
||||
Timo Tijhof <krinkle@fastmail.com>
|
||||
Timmy Willison <timmywil@users.noreply.github.com>
|
||||
divdeploy <166095818+divdeploy@users.noreply.github.com>
|
||||
mark van tilburg <markvantilburg@gmail.com>
|
||||
Ralf Koller <1665422+rpkoller@users.noreply.github.com>
|
||||
Porter Clevidence <116387727+porterclev@users.noreply.github.com>
|
||||
Daniel García <93217193+Daniel-Garmig@users.noreply.github.com>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Copyright jQuery Foundation and other contributors, https://jquery.org/
|
||||
Copyright OpenJS Foundation and other contributors, https://openjsf.org/
|
||||
|
||||
This software consists of voluntary contributions made by many
|
||||
individuals. For exact contribution history, see the revision history
|
||||
|
||||
|
Before Width: | Height: | Size: 7.0 KiB After Width: | Height: | Size: 6.8 KiB |
|
Before Width: | Height: | Size: 7.0 KiB After Width: | Height: | Size: 6.8 KiB |
|
Before Width: | Height: | Size: 4.6 KiB After Width: | Height: | Size: 4.4 KiB |
|
Before Width: | Height: | Size: 7.0 KiB After Width: | Height: | Size: 6.8 KiB |
|
Before Width: | Height: | Size: 4.6 KiB After Width: | Height: | Size: 4.4 KiB |
|
Before Width: | Height: | Size: 6.4 KiB After Width: | Height: | Size: 6.3 KiB |
@@ -437,7 +437,7 @@ $( "#button-icon" ).button({
|
||||
showLabel: false
|
||||
});
|
||||
|
||||
$( "#radioset" ).buttonset();
|
||||
$( "#radioset" ).controlgroup();
|
||||
|
||||
$( "#controlgroup" ).controlgroup();
|
||||
|
||||
|
||||
42
inst/www/shared/jqueryui/jquery-ui.css
vendored
@@ -1,8 +1,8 @@
|
||||
/*! jQuery UI - v1.13.2 - 2022-07-14
|
||||
* http://jqueryui.com
|
||||
/*! jQuery UI - v1.14.1 - 2024-10-30
|
||||
* https://jqueryui.com
|
||||
* Includes: core.css, accordion.css, autocomplete.css, menu.css, button.css, controlgroup.css, checkboxradio.css, datepicker.css, dialog.css, draggable.css, resizable.css, progressbar.css, selectable.css, selectmenu.css, slider.css, sortable.css, spinner.css, tabs.css, tooltip.css, theme.css
|
||||
* To view and modify this theme, visit http://jqueryui.com/themeroller/?bgShadowXPos=&bgOverlayXPos=&bgErrorXPos=&bgHighlightXPos=&bgContentXPos=&bgHeaderXPos=&bgActiveXPos=&bgHoverXPos=&bgDefaultXPos=&bgShadowYPos=&bgOverlayYPos=&bgErrorYPos=&bgHighlightYPos=&bgContentYPos=&bgHeaderYPos=&bgActiveYPos=&bgHoverYPos=&bgDefaultYPos=&bgShadowRepeat=&bgOverlayRepeat=&bgErrorRepeat=&bgHighlightRepeat=&bgContentRepeat=&bgHeaderRepeat=&bgActiveRepeat=&bgHoverRepeat=&bgDefaultRepeat=&iconsHover=url(%22images%2Fui-icons_555555_256x240.png%22)&iconsHighlight=url(%22images%2Fui-icons_777620_256x240.png%22)&iconsHeader=url(%22images%2Fui-icons_444444_256x240.png%22)&iconsError=url(%22images%2Fui-icons_cc0000_256x240.png%22)&iconsDefault=url(%22images%2Fui-icons_777777_256x240.png%22)&iconsContent=url(%22images%2Fui-icons_444444_256x240.png%22)&iconsActive=url(%22images%2Fui-icons_ffffff_256x240.png%22)&bgImgUrlShadow=&bgImgUrlOverlay=&bgImgUrlHover=&bgImgUrlHighlight=&bgImgUrlHeader=&bgImgUrlError=&bgImgUrlDefault=&bgImgUrlContent=&bgImgUrlActive=&opacityFilterShadow=Alpha(Opacity%3D30)&opacityFilterOverlay=Alpha(Opacity%3D30)&opacityShadowPerc=30&opacityOverlayPerc=30&iconColorHover=%23555555&iconColorHighlight=%23777620&iconColorHeader=%23444444&iconColorError=%23cc0000&iconColorDefault=%23777777&iconColorContent=%23444444&iconColorActive=%23ffffff&bgImgOpacityShadow=0&bgImgOpacityOverlay=0&bgImgOpacityError=95&bgImgOpacityHighlight=55&bgImgOpacityContent=75&bgImgOpacityHeader=75&bgImgOpacityActive=65&bgImgOpacityHover=75&bgImgOpacityDefault=75&bgTextureShadow=flat&bgTextureOverlay=flat&bgTextureError=flat&bgTextureHighlight=flat&bgTextureContent=flat&bgTextureHeader=flat&bgTextureActive=flat&bgTextureHover=flat&bgTextureDefault=flat&cornerRadius=3px&fwDefault=normal&ffDefault=Arial%2CHelvetica%2Csans-serif&fsDefault=1em&cornerRadiusShadow=8px&thicknessShadow=5px&offsetLeftShadow=0px&offsetTopShadow=0px&opacityShadow=.3&bgColorShadow=%23666666&opacityOverlay=.3&bgColorOverlay=%23aaaaaa&fcError=%235f3f3f&borderColorError=%23f1a899&bgColorError=%23fddfdf&fcHighlight=%23777620&borderColorHighlight=%23dad55e&bgColorHighlight=%23fffa90&fcContent=%23333333&borderColorContent=%23dddddd&bgColorContent=%23ffffff&fcHeader=%23333333&borderColorHeader=%23dddddd&bgColorHeader=%23e9e9e9&fcActive=%23ffffff&borderColorActive=%23003eff&bgColorActive=%23007fff&fcHover=%232b2b2b&borderColorHover=%23cccccc&bgColorHover=%23ededed&fcDefault=%23454545&borderColorDefault=%23c5c5c5&bgColorDefault=%23f6f6f6
|
||||
* Copyright jQuery Foundation and other contributors; Licensed MIT */
|
||||
* To view and modify this theme, visit https://jqueryui.com/themeroller/?bgColorDefault=%23f6f6f6&borderColorDefault=%23c5c5c5&fcDefault=%23454545&bgColorHover=%23ededed&borderColorHover=%23cccccc&fcHover=%232b2b2b&bgColorActive=%23007fff&borderColorActive=%23003eff&fcActive=%23ffffff&bgColorHeader=%23e9e9e9&borderColorHeader=%23dddddd&fcHeader=%23333333&bgColorContent=%23ffffff&borderColorContent=%23dddddd&fcContent=%23333333&bgColorHighlight=%23fffa90&borderColorHighlight=%23dad55e&fcHighlight=%23777620&bgColorError=%23fddfdf&borderColorError=%23f1a899&fcError=%235f3f3f&bgColorOverlay=%23aaaaaa&opacityOverlay=.3&bgColorShadow=%23666666&opacityShadow=.3&offsetTopShadow=0px&offsetLeftShadow=0px&thicknessShadow=5px&cornerRadiusShadow=8px&fsDefault=1em&ffDefault=Arial%2CHelvetica%2Csans-serif&fwDefault=normal&cornerRadius=3px&bgTextureDefault=flat&bgTextureHover=flat&bgTextureActive=flat&bgTextureHeader=flat&bgTextureContent=flat&bgTextureHighlight=flat&bgTextureError=flat&bgTextureOverlay=flat&bgTextureShadow=flat&bgImgOpacityDefault=75&bgImgOpacityHover=75&bgImgOpacityActive=65&bgImgOpacityHeader=75&bgImgOpacityContent=75&bgImgOpacityHighlight=55&bgImgOpacityError=95&bgImgOpacityOverlay=0&bgImgOpacityShadow=0&iconColorActive=%23ffffff&iconColorContent=%23444444&iconColorDefault=%23777777&iconColorError=%23cc0000&iconColorHeader=%23444444&iconColorHighlight=%23777620&iconColorHover=%23555555&opacityOverlayPerc=30&opacityShadowPerc=30&bgImgUrlActive=&bgImgUrlContent=&bgImgUrlDefault=&bgImgUrlError=&bgImgUrlHeader=&bgImgUrlHighlight=&bgImgUrlHover=&bgImgUrlOverlay=&bgImgUrlShadow=&iconsActive=url(%22images%2Fui-icons_ffffff_256x240.png%22)&iconsContent=url(%22images%2Fui-icons_444444_256x240.png%22)&iconsDefault=url(%22images%2Fui-icons_777777_256x240.png%22)&iconsError=url(%22images%2Fui-icons_cc0000_256x240.png%22)&iconsHeader=url(%22images%2Fui-icons_444444_256x240.png%22)&iconsHighlight=url(%22images%2Fui-icons_777620_256x240.png%22)&iconsHover=url(%22images%2Fui-icons_555555_256x240.png%22)&bgDefaultRepeat=&bgHoverRepeat=&bgActiveRepeat=&bgHeaderRepeat=&bgContentRepeat=&bgHighlightRepeat=&bgErrorRepeat=&bgOverlayRepeat=&bgShadowRepeat=&bgDefaultYPos=&bgHoverYPos=&bgActiveYPos=&bgHeaderYPos=&bgContentYPos=&bgHighlightYPos=&bgErrorYPos=&bgOverlayYPos=&bgShadowYPos=&bgDefaultXPos=&bgHoverXPos=&bgActiveXPos=&bgHeaderXPos=&bgContentXPos=&bgHighlightXPos=&bgErrorXPos=&bgOverlayXPos=&bgShadowXPos=
|
||||
* Copyright OpenJS Foundation and other contributors; Licensed MIT */
|
||||
|
||||
/* Layout helpers
|
||||
----------------------------------*/
|
||||
@@ -45,7 +45,6 @@
|
||||
left: 0;
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
-ms-filter: "alpha(opacity=0)"; /* support: IE8 */
|
||||
}
|
||||
|
||||
.ui-front {
|
||||
@@ -122,8 +121,6 @@
|
||||
.ui-menu .ui-menu-item {
|
||||
margin: 0;
|
||||
cursor: pointer;
|
||||
/* support: IE10, see #8844 */
|
||||
list-style-image: url("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7");
|
||||
}
|
||||
.ui-menu .ui-menu-item-wrapper {
|
||||
position: relative;
|
||||
@@ -173,12 +170,7 @@
|
||||
vertical-align: middle;
|
||||
text-align: center;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
|
||||
/* Support: IE <= 11 */
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.ui-button,
|
||||
@@ -229,7 +221,7 @@ input.ui-button.ui-icon-notext .ui-icon {
|
||||
}
|
||||
|
||||
/* workarounds */
|
||||
/* Support: Firefox 5 - 40 */
|
||||
/* Support: Firefox 5 - 125+ */
|
||||
input.ui-button::-moz-focus-inner,
|
||||
button.ui-button::-moz-focus-inner {
|
||||
border: 0;
|
||||
@@ -280,9 +272,6 @@ button.ui-button::-moz-focus-inner {
|
||||
|
||||
/* Spinner specific style fixes */
|
||||
.ui-controlgroup-vertical .ui-spinner-input {
|
||||
|
||||
/* Support: IE8 only, Android < 4.4 only */
|
||||
width: 75%;
|
||||
width: calc( 100% - 2.4em );
|
||||
}
|
||||
.ui-controlgroup-vertical .ui-spinner .ui-spinner-up {
|
||||
@@ -579,7 +568,6 @@ button.ui-button::-moz-focus-inner {
|
||||
cursor: move;
|
||||
}
|
||||
.ui-draggable-handle {
|
||||
-ms-touch-action: none;
|
||||
touch-action: none;
|
||||
}
|
||||
.ui-resizable {
|
||||
@@ -589,7 +577,6 @@ button.ui-button::-moz-focus-inner {
|
||||
position: absolute;
|
||||
font-size: 0.1px;
|
||||
display: block;
|
||||
-ms-touch-action: none;
|
||||
touch-action: none;
|
||||
}
|
||||
.ui-resizable-disabled .ui-resizable-handle,
|
||||
@@ -664,14 +651,12 @@ button.ui-button::-moz-focus-inner {
|
||||
.ui-progressbar .ui-progressbar-overlay {
|
||||
background: url("data:image/gif;base64,R0lGODlhKAAoAIABAAAAAP///yH/C05FVFNDQVBFMi4wAwEAAAAh+QQJAQABACwAAAAAKAAoAAACkYwNqXrdC52DS06a7MFZI+4FHBCKoDeWKXqymPqGqxvJrXZbMx7Ttc+w9XgU2FB3lOyQRWET2IFGiU9m1frDVpxZZc6bfHwv4c1YXP6k1Vdy292Fb6UkuvFtXpvWSzA+HycXJHUXiGYIiMg2R6W459gnWGfHNdjIqDWVqemH2ekpObkpOlppWUqZiqr6edqqWQAAIfkECQEAAQAsAAAAACgAKAAAApSMgZnGfaqcg1E2uuzDmmHUBR8Qil95hiPKqWn3aqtLsS18y7G1SzNeowWBENtQd+T1JktP05nzPTdJZlR6vUxNWWjV+vUWhWNkWFwxl9VpZRedYcflIOLafaa28XdsH/ynlcc1uPVDZxQIR0K25+cICCmoqCe5mGhZOfeYSUh5yJcJyrkZWWpaR8doJ2o4NYq62lAAACH5BAkBAAEALAAAAAAoACgAAAKVDI4Yy22ZnINRNqosw0Bv7i1gyHUkFj7oSaWlu3ovC8GxNso5fluz3qLVhBVeT/Lz7ZTHyxL5dDalQWPVOsQWtRnuwXaFTj9jVVh8pma9JjZ4zYSj5ZOyma7uuolffh+IR5aW97cHuBUXKGKXlKjn+DiHWMcYJah4N0lYCMlJOXipGRr5qdgoSTrqWSq6WFl2ypoaUAAAIfkECQEAAQAsAAAAACgAKAAAApaEb6HLgd/iO7FNWtcFWe+ufODGjRfoiJ2akShbueb0wtI50zm02pbvwfWEMWBQ1zKGlLIhskiEPm9R6vRXxV4ZzWT2yHOGpWMyorblKlNp8HmHEb/lCXjcW7bmtXP8Xt229OVWR1fod2eWqNfHuMjXCPkIGNileOiImVmCOEmoSfn3yXlJWmoHGhqp6ilYuWYpmTqKUgAAIfkECQEAAQAsAAAAACgAKAAAApiEH6kb58biQ3FNWtMFWW3eNVcojuFGfqnZqSebuS06w5V80/X02pKe8zFwP6EFWOT1lDFk8rGERh1TTNOocQ61Hm4Xm2VexUHpzjymViHrFbiELsefVrn6XKfnt2Q9G/+Xdie499XHd2g4h7ioOGhXGJboGAnXSBnoBwKYyfioubZJ2Hn0RuRZaflZOil56Zp6iioKSXpUAAAh+QQJAQABACwAAAAAKAAoAAACkoQRqRvnxuI7kU1a1UU5bd5tnSeOZXhmn5lWK3qNTWvRdQxP8qvaC+/yaYQzXO7BMvaUEmJRd3TsiMAgswmNYrSgZdYrTX6tSHGZO73ezuAw2uxuQ+BbeZfMxsexY35+/Qe4J1inV0g4x3WHuMhIl2jXOKT2Q+VU5fgoSUI52VfZyfkJGkha6jmY+aaYdirq+lQAACH5BAkBAAEALAAAAAAoACgAAAKWBIKpYe0L3YNKToqswUlvznigd4wiR4KhZrKt9Upqip61i9E3vMvxRdHlbEFiEXfk9YARYxOZZD6VQ2pUunBmtRXo1Lf8hMVVcNl8JafV38aM2/Fu5V16Bn63r6xt97j09+MXSFi4BniGFae3hzbH9+hYBzkpuUh5aZmHuanZOZgIuvbGiNeomCnaxxap2upaCZsq+1kAACH5BAkBAAEALAAAAAAoACgAAAKXjI8By5zf4kOxTVrXNVlv1X0d8IGZGKLnNpYtm8Lr9cqVeuOSvfOW79D9aDHizNhDJidFZhNydEahOaDH6nomtJjp1tutKoNWkvA6JqfRVLHU/QUfau9l2x7G54d1fl995xcIGAdXqMfBNadoYrhH+Mg2KBlpVpbluCiXmMnZ2Sh4GBqJ+ckIOqqJ6LmKSllZmsoq6wpQAAAh+QQJAQABACwAAAAAKAAoAAAClYx/oLvoxuJDkU1a1YUZbJ59nSd2ZXhWqbRa2/gF8Gu2DY3iqs7yrq+xBYEkYvFSM8aSSObE+ZgRl1BHFZNr7pRCavZ5BW2142hY3AN/zWtsmf12p9XxxFl2lpLn1rseztfXZjdIWIf2s5dItwjYKBgo9yg5pHgzJXTEeGlZuenpyPmpGQoKOWkYmSpaSnqKileI2FAAACH5BAkBAAEALAAAAAAoACgAAAKVjB+gu+jG4kORTVrVhRlsnn2dJ3ZleFaptFrb+CXmO9OozeL5VfP99HvAWhpiUdcwkpBH3825AwYdU8xTqlLGhtCosArKMpvfa1mMRae9VvWZfeB2XfPkeLmm18lUcBj+p5dnN8jXZ3YIGEhYuOUn45aoCDkp16hl5IjYJvjWKcnoGQpqyPlpOhr3aElaqrq56Bq7VAAAOw==");
|
||||
height: 100%;
|
||||
-ms-filter: "alpha(opacity=25)"; /* support: IE8 */
|
||||
opacity: 0.25;
|
||||
}
|
||||
.ui-progressbar-indeterminate .ui-progressbar-value {
|
||||
background-image: none;
|
||||
}
|
||||
.ui-selectable {
|
||||
-ms-touch-action: none;
|
||||
touch-action: none;
|
||||
}
|
||||
.ui-selectable-helper {
|
||||
@@ -729,7 +714,6 @@ button.ui-button::-moz-focus-inner {
|
||||
width: 1.2em;
|
||||
height: 1.2em;
|
||||
cursor: pointer;
|
||||
-ms-touch-action: none;
|
||||
touch-action: none;
|
||||
}
|
||||
.ui-slider .ui-slider-range {
|
||||
@@ -741,12 +725,6 @@ button.ui-button::-moz-focus-inner {
|
||||
background-position: 0 0;
|
||||
}
|
||||
|
||||
/* support: IE8 - See #6727 */
|
||||
.ui-slider.ui-state-disabled .ui-slider-handle,
|
||||
.ui-slider.ui-state-disabled .ui-slider-range {
|
||||
filter: inherit;
|
||||
}
|
||||
|
||||
.ui-slider-horizontal {
|
||||
height: .8em;
|
||||
}
|
||||
@@ -785,7 +763,6 @@ button.ui-button::-moz-focus-inner {
|
||||
top: 0;
|
||||
}
|
||||
.ui-sortable-handle {
|
||||
-ms-touch-action: none;
|
||||
touch-action: none;
|
||||
}
|
||||
.ui-spinner {
|
||||
@@ -1041,19 +1018,14 @@ a.ui-button:active,
|
||||
.ui-widget-content .ui-priority-secondary,
|
||||
.ui-widget-header .ui-priority-secondary {
|
||||
opacity: .7;
|
||||
-ms-filter: "alpha(opacity=70)"; /* support: IE8 */
|
||||
font-weight: normal;
|
||||
}
|
||||
.ui-state-disabled,
|
||||
.ui-widget-content .ui-state-disabled,
|
||||
.ui-widget-header .ui-state-disabled {
|
||||
opacity: .35;
|
||||
-ms-filter: "alpha(opacity=35)"; /* support: IE8 */
|
||||
background-image: none;
|
||||
}
|
||||
.ui-state-disabled .ui-icon {
|
||||
-ms-filter: "alpha(opacity=35)"; /* support: IE8 - See #6059 */
|
||||
}
|
||||
|
||||
/* Icons
|
||||
----------------------------------*/
|
||||
@@ -1306,10 +1278,8 @@ a.ui-button:active,
|
||||
/* Overlays */
|
||||
.ui-widget-overlay {
|
||||
background: #aaaaaa;
|
||||
opacity: .003;
|
||||
-ms-filter: Alpha(Opacity=.3); /* support: IE8 */
|
||||
opacity: .3;
|
||||
}
|
||||
.ui-widget-shadow {
|
||||
-webkit-box-shadow: 0px 0px 5px #666666;
|
||||
box-shadow: 0px 0px 5px #666666;
|
||||
}
|
||||
|
||||
1337
inst/www/shared/jqueryui/jquery-ui.js
vendored
10
inst/www/shared/jqueryui/jquery-ui.min.css
vendored
8
inst/www/shared/jqueryui/jquery-ui.min.js
vendored
35
inst/www/shared/jqueryui/jquery-ui.structure.css
vendored
@@ -1,12 +1,12 @@
|
||||
/*!
|
||||
* jQuery UI CSS Framework 1.13.2
|
||||
* http://jqueryui.com
|
||||
* jQuery UI CSS Framework 1.14.1
|
||||
* https://jqueryui.com
|
||||
*
|
||||
* Copyright jQuery Foundation and other contributors
|
||||
* Copyright OpenJS Foundation and other contributors
|
||||
* Released under the MIT license.
|
||||
* http://jquery.org/license
|
||||
* https://jquery.org/license
|
||||
*
|
||||
* http://api.jqueryui.com/category/theming/
|
||||
* https://api.jqueryui.com/category/theming/
|
||||
*/
|
||||
/* Layout helpers
|
||||
----------------------------------*/
|
||||
@@ -49,7 +49,6 @@
|
||||
left: 0;
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
-ms-filter: "alpha(opacity=0)"; /* support: IE8 */
|
||||
}
|
||||
|
||||
.ui-front {
|
||||
@@ -126,8 +125,6 @@
|
||||
.ui-menu .ui-menu-item {
|
||||
margin: 0;
|
||||
cursor: pointer;
|
||||
/* support: IE10, see #8844 */
|
||||
list-style-image: url("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7");
|
||||
}
|
||||
.ui-menu .ui-menu-item-wrapper {
|
||||
position: relative;
|
||||
@@ -177,12 +174,7 @@
|
||||
vertical-align: middle;
|
||||
text-align: center;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
|
||||
/* Support: IE <= 11 */
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.ui-button,
|
||||
@@ -233,7 +225,7 @@ input.ui-button.ui-icon-notext .ui-icon {
|
||||
}
|
||||
|
||||
/* workarounds */
|
||||
/* Support: Firefox 5 - 40 */
|
||||
/* Support: Firefox 5 - 125+ */
|
||||
input.ui-button::-moz-focus-inner,
|
||||
button.ui-button::-moz-focus-inner {
|
||||
border: 0;
|
||||
@@ -284,9 +276,6 @@ button.ui-button::-moz-focus-inner {
|
||||
|
||||
/* Spinner specific style fixes */
|
||||
.ui-controlgroup-vertical .ui-spinner-input {
|
||||
|
||||
/* Support: IE8 only, Android < 4.4 only */
|
||||
width: 75%;
|
||||
width: calc( 100% - 2.4em );
|
||||
}
|
||||
.ui-controlgroup-vertical .ui-spinner .ui-spinner-up {
|
||||
@@ -583,7 +572,6 @@ button.ui-button::-moz-focus-inner {
|
||||
cursor: move;
|
||||
}
|
||||
.ui-draggable-handle {
|
||||
-ms-touch-action: none;
|
||||
touch-action: none;
|
||||
}
|
||||
.ui-resizable {
|
||||
@@ -593,7 +581,6 @@ button.ui-button::-moz-focus-inner {
|
||||
position: absolute;
|
||||
font-size: 0.1px;
|
||||
display: block;
|
||||
-ms-touch-action: none;
|
||||
touch-action: none;
|
||||
}
|
||||
.ui-resizable-disabled .ui-resizable-handle,
|
||||
@@ -668,14 +655,12 @@ button.ui-button::-moz-focus-inner {
|
||||
.ui-progressbar .ui-progressbar-overlay {
|
||||
background: url("data:image/gif;base64,R0lGODlhKAAoAIABAAAAAP///yH/C05FVFNDQVBFMi4wAwEAAAAh+QQJAQABACwAAAAAKAAoAAACkYwNqXrdC52DS06a7MFZI+4FHBCKoDeWKXqymPqGqxvJrXZbMx7Ttc+w9XgU2FB3lOyQRWET2IFGiU9m1frDVpxZZc6bfHwv4c1YXP6k1Vdy292Fb6UkuvFtXpvWSzA+HycXJHUXiGYIiMg2R6W459gnWGfHNdjIqDWVqemH2ekpObkpOlppWUqZiqr6edqqWQAAIfkECQEAAQAsAAAAACgAKAAAApSMgZnGfaqcg1E2uuzDmmHUBR8Qil95hiPKqWn3aqtLsS18y7G1SzNeowWBENtQd+T1JktP05nzPTdJZlR6vUxNWWjV+vUWhWNkWFwxl9VpZRedYcflIOLafaa28XdsH/ynlcc1uPVDZxQIR0K25+cICCmoqCe5mGhZOfeYSUh5yJcJyrkZWWpaR8doJ2o4NYq62lAAACH5BAkBAAEALAAAAAAoACgAAAKVDI4Yy22ZnINRNqosw0Bv7i1gyHUkFj7oSaWlu3ovC8GxNso5fluz3qLVhBVeT/Lz7ZTHyxL5dDalQWPVOsQWtRnuwXaFTj9jVVh8pma9JjZ4zYSj5ZOyma7uuolffh+IR5aW97cHuBUXKGKXlKjn+DiHWMcYJah4N0lYCMlJOXipGRr5qdgoSTrqWSq6WFl2ypoaUAAAIfkECQEAAQAsAAAAACgAKAAAApaEb6HLgd/iO7FNWtcFWe+ufODGjRfoiJ2akShbueb0wtI50zm02pbvwfWEMWBQ1zKGlLIhskiEPm9R6vRXxV4ZzWT2yHOGpWMyorblKlNp8HmHEb/lCXjcW7bmtXP8Xt229OVWR1fod2eWqNfHuMjXCPkIGNileOiImVmCOEmoSfn3yXlJWmoHGhqp6ilYuWYpmTqKUgAAIfkECQEAAQAsAAAAACgAKAAAApiEH6kb58biQ3FNWtMFWW3eNVcojuFGfqnZqSebuS06w5V80/X02pKe8zFwP6EFWOT1lDFk8rGERh1TTNOocQ61Hm4Xm2VexUHpzjymViHrFbiELsefVrn6XKfnt2Q9G/+Xdie499XHd2g4h7ioOGhXGJboGAnXSBnoBwKYyfioubZJ2Hn0RuRZaflZOil56Zp6iioKSXpUAAAh+QQJAQABACwAAAAAKAAoAAACkoQRqRvnxuI7kU1a1UU5bd5tnSeOZXhmn5lWK3qNTWvRdQxP8qvaC+/yaYQzXO7BMvaUEmJRd3TsiMAgswmNYrSgZdYrTX6tSHGZO73ezuAw2uxuQ+BbeZfMxsexY35+/Qe4J1inV0g4x3WHuMhIl2jXOKT2Q+VU5fgoSUI52VfZyfkJGkha6jmY+aaYdirq+lQAACH5BAkBAAEALAAAAAAoACgAAAKWBIKpYe0L3YNKToqswUlvznigd4wiR4KhZrKt9Upqip61i9E3vMvxRdHlbEFiEXfk9YARYxOZZD6VQ2pUunBmtRXo1Lf8hMVVcNl8JafV38aM2/Fu5V16Bn63r6xt97j09+MXSFi4BniGFae3hzbH9+hYBzkpuUh5aZmHuanZOZgIuvbGiNeomCnaxxap2upaCZsq+1kAACH5BAkBAAEALAAAAAAoACgAAAKXjI8By5zf4kOxTVrXNVlv1X0d8IGZGKLnNpYtm8Lr9cqVeuOSvfOW79D9aDHizNhDJidFZhNydEahOaDH6nomtJjp1tutKoNWkvA6JqfRVLHU/QUfau9l2x7G54d1fl995xcIGAdXqMfBNadoYrhH+Mg2KBlpVpbluCiXmMnZ2Sh4GBqJ+ckIOqqJ6LmKSllZmsoq6wpQAAAh+QQJAQABACwAAAAAKAAoAAAClYx/oLvoxuJDkU1a1YUZbJ59nSd2ZXhWqbRa2/gF8Gu2DY3iqs7yrq+xBYEkYvFSM8aSSObE+ZgRl1BHFZNr7pRCavZ5BW2142hY3AN/zWtsmf12p9XxxFl2lpLn1rseztfXZjdIWIf2s5dItwjYKBgo9yg5pHgzJXTEeGlZuenpyPmpGQoKOWkYmSpaSnqKileI2FAAACH5BAkBAAEALAAAAAAoACgAAAKVjB+gu+jG4kORTVrVhRlsnn2dJ3ZleFaptFrb+CXmO9OozeL5VfP99HvAWhpiUdcwkpBH3825AwYdU8xTqlLGhtCosArKMpvfa1mMRae9VvWZfeB2XfPkeLmm18lUcBj+p5dnN8jXZ3YIGEhYuOUn45aoCDkp16hl5IjYJvjWKcnoGQpqyPlpOhr3aElaqrq56Bq7VAAAOw==");
|
||||
height: 100%;
|
||||
-ms-filter: "alpha(opacity=25)"; /* support: IE8 */
|
||||
opacity: 0.25;
|
||||
}
|
||||
.ui-progressbar-indeterminate .ui-progressbar-value {
|
||||
background-image: none;
|
||||
}
|
||||
.ui-selectable {
|
||||
-ms-touch-action: none;
|
||||
touch-action: none;
|
||||
}
|
||||
.ui-selectable-helper {
|
||||
@@ -733,7 +718,6 @@ button.ui-button::-moz-focus-inner {
|
||||
width: 1.2em;
|
||||
height: 1.2em;
|
||||
cursor: pointer;
|
||||
-ms-touch-action: none;
|
||||
touch-action: none;
|
||||
}
|
||||
.ui-slider .ui-slider-range {
|
||||
@@ -745,12 +729,6 @@ button.ui-button::-moz-focus-inner {
|
||||
background-position: 0 0;
|
||||
}
|
||||
|
||||
/* support: IE8 - See #6727 */
|
||||
.ui-slider.ui-state-disabled .ui-slider-handle,
|
||||
.ui-slider.ui-state-disabled .ui-slider-range {
|
||||
filter: inherit;
|
||||
}
|
||||
|
||||
.ui-slider-horizontal {
|
||||
height: .8em;
|
||||
}
|
||||
@@ -789,7 +767,6 @@ button.ui-button::-moz-focus-inner {
|
||||
top: 0;
|
||||
}
|
||||
.ui-sortable-handle {
|
||||
-ms-touch-action: none;
|
||||
touch-action: none;
|
||||
}
|
||||
.ui-spinner {
|
||||
|
||||
21
inst/www/shared/jqueryui/jquery-ui.theme.css
vendored
@@ -1,14 +1,14 @@
|
||||
/*!
|
||||
* jQuery UI CSS Framework 1.13.2
|
||||
* http://jqueryui.com
|
||||
* jQuery UI CSS Framework 1.14.1
|
||||
* https://jqueryui.com
|
||||
*
|
||||
* Copyright jQuery Foundation and other contributors
|
||||
* Copyright OpenJS Foundation and other contributors
|
||||
* Released under the MIT license.
|
||||
* http://jquery.org/license
|
||||
* https://jquery.org/license
|
||||
*
|
||||
* http://api.jqueryui.com/category/theming/
|
||||
* https://api.jqueryui.com/category/theming/
|
||||
*
|
||||
* To view and modify this theme, visit http://jqueryui.com/themeroller/?bgShadowXPos=&bgOverlayXPos=&bgErrorXPos=&bgHighlightXPos=&bgContentXPos=&bgHeaderXPos=&bgActiveXPos=&bgHoverXPos=&bgDefaultXPos=&bgShadowYPos=&bgOverlayYPos=&bgErrorYPos=&bgHighlightYPos=&bgContentYPos=&bgHeaderYPos=&bgActiveYPos=&bgHoverYPos=&bgDefaultYPos=&bgShadowRepeat=&bgOverlayRepeat=&bgErrorRepeat=&bgHighlightRepeat=&bgContentRepeat=&bgHeaderRepeat=&bgActiveRepeat=&bgHoverRepeat=&bgDefaultRepeat=&iconsHover=url(%22images%2Fui-icons_555555_256x240.png%22)&iconsHighlight=url(%22images%2Fui-icons_777620_256x240.png%22)&iconsHeader=url(%22images%2Fui-icons_444444_256x240.png%22)&iconsError=url(%22images%2Fui-icons_cc0000_256x240.png%22)&iconsDefault=url(%22images%2Fui-icons_777777_256x240.png%22)&iconsContent=url(%22images%2Fui-icons_444444_256x240.png%22)&iconsActive=url(%22images%2Fui-icons_ffffff_256x240.png%22)&bgImgUrlShadow=&bgImgUrlOverlay=&bgImgUrlHover=&bgImgUrlHighlight=&bgImgUrlHeader=&bgImgUrlError=&bgImgUrlDefault=&bgImgUrlContent=&bgImgUrlActive=&opacityFilterShadow=Alpha(Opacity%3D30)&opacityFilterOverlay=Alpha(Opacity%3D30)&opacityShadowPerc=30&opacityOverlayPerc=30&iconColorHover=%23555555&iconColorHighlight=%23777620&iconColorHeader=%23444444&iconColorError=%23cc0000&iconColorDefault=%23777777&iconColorContent=%23444444&iconColorActive=%23ffffff&bgImgOpacityShadow=0&bgImgOpacityOverlay=0&bgImgOpacityError=95&bgImgOpacityHighlight=55&bgImgOpacityContent=75&bgImgOpacityHeader=75&bgImgOpacityActive=65&bgImgOpacityHover=75&bgImgOpacityDefault=75&bgTextureShadow=flat&bgTextureOverlay=flat&bgTextureError=flat&bgTextureHighlight=flat&bgTextureContent=flat&bgTextureHeader=flat&bgTextureActive=flat&bgTextureHover=flat&bgTextureDefault=flat&cornerRadius=3px&fwDefault=normal&ffDefault=Arial%2CHelvetica%2Csans-serif&fsDefault=1em&cornerRadiusShadow=8px&thicknessShadow=5px&offsetLeftShadow=0px&offsetTopShadow=0px&opacityShadow=.3&bgColorShadow=%23666666&opacityOverlay=.3&bgColorOverlay=%23aaaaaa&fcError=%235f3f3f&borderColorError=%23f1a899&bgColorError=%23fddfdf&fcHighlight=%23777620&borderColorHighlight=%23dad55e&bgColorHighlight=%23fffa90&fcContent=%23333333&borderColorContent=%23dddddd&bgColorContent=%23ffffff&fcHeader=%23333333&borderColorHeader=%23dddddd&bgColorHeader=%23e9e9e9&fcActive=%23ffffff&borderColorActive=%23003eff&bgColorActive=%23007fff&fcHover=%232b2b2b&borderColorHover=%23cccccc&bgColorHover=%23ededed&fcDefault=%23454545&borderColorDefault=%23c5c5c5&bgColorDefault=%23f6f6f6
|
||||
* To view and modify this theme, visit https://jqueryui.com/themeroller/?bgColorDefault=%23f6f6f6&borderColorDefault=%23c5c5c5&fcDefault=%23454545&bgColorHover=%23ededed&borderColorHover=%23cccccc&fcHover=%232b2b2b&bgColorActive=%23007fff&borderColorActive=%23003eff&fcActive=%23ffffff&bgColorHeader=%23e9e9e9&borderColorHeader=%23dddddd&fcHeader=%23333333&bgColorContent=%23ffffff&borderColorContent=%23dddddd&fcContent=%23333333&bgColorHighlight=%23fffa90&borderColorHighlight=%23dad55e&fcHighlight=%23777620&bgColorError=%23fddfdf&borderColorError=%23f1a899&fcError=%235f3f3f&bgColorOverlay=%23aaaaaa&opacityOverlay=.3&bgColorShadow=%23666666&opacityShadow=.3&offsetTopShadow=0px&offsetLeftShadow=0px&thicknessShadow=5px&cornerRadiusShadow=8px&fsDefault=1em&ffDefault=Arial%2CHelvetica%2Csans-serif&fwDefault=normal&cornerRadius=3px&bgTextureDefault=flat&bgTextureHover=flat&bgTextureActive=flat&bgTextureHeader=flat&bgTextureContent=flat&bgTextureHighlight=flat&bgTextureError=flat&bgTextureOverlay=flat&bgTextureShadow=flat&bgImgOpacityDefault=75&bgImgOpacityHover=75&bgImgOpacityActive=65&bgImgOpacityHeader=75&bgImgOpacityContent=75&bgImgOpacityHighlight=55&bgImgOpacityError=95&bgImgOpacityOverlay=0&bgImgOpacityShadow=0&iconColorActive=%23ffffff&iconColorContent=%23444444&iconColorDefault=%23777777&iconColorError=%23cc0000&iconColorHeader=%23444444&iconColorHighlight=%23777620&iconColorHover=%23555555&opacityOverlayPerc=30&opacityShadowPerc=30&bgImgUrlActive=&bgImgUrlContent=&bgImgUrlDefault=&bgImgUrlError=&bgImgUrlHeader=&bgImgUrlHighlight=&bgImgUrlHover=&bgImgUrlOverlay=&bgImgUrlShadow=&iconsActive=url(%22images%2Fui-icons_ffffff_256x240.png%22)&iconsContent=url(%22images%2Fui-icons_444444_256x240.png%22)&iconsDefault=url(%22images%2Fui-icons_777777_256x240.png%22)&iconsError=url(%22images%2Fui-icons_cc0000_256x240.png%22)&iconsHeader=url(%22images%2Fui-icons_444444_256x240.png%22)&iconsHighlight=url(%22images%2Fui-icons_777620_256x240.png%22)&iconsHover=url(%22images%2Fui-icons_555555_256x240.png%22)&bgDefaultRepeat=&bgHoverRepeat=&bgActiveRepeat=&bgHeaderRepeat=&bgContentRepeat=&bgHighlightRepeat=&bgErrorRepeat=&bgOverlayRepeat=&bgShadowRepeat=&bgDefaultYPos=&bgHoverYPos=&bgActiveYPos=&bgHeaderYPos=&bgContentYPos=&bgHighlightYPos=&bgErrorYPos=&bgOverlayYPos=&bgShadowYPos=&bgDefaultXPos=&bgHoverXPos=&bgActiveXPos=&bgHeaderXPos=&bgContentXPos=&bgHighlightXPos=&bgErrorXPos=&bgOverlayXPos=&bgShadowXPos=
|
||||
*/
|
||||
|
||||
|
||||
@@ -172,19 +172,14 @@ a.ui-button:active,
|
||||
.ui-widget-content .ui-priority-secondary,
|
||||
.ui-widget-header .ui-priority-secondary {
|
||||
opacity: .7;
|
||||
-ms-filter: "alpha(opacity=70)"; /* support: IE8 */
|
||||
font-weight: normal;
|
||||
}
|
||||
.ui-state-disabled,
|
||||
.ui-widget-content .ui-state-disabled,
|
||||
.ui-widget-header .ui-state-disabled {
|
||||
opacity: .35;
|
||||
-ms-filter: "alpha(opacity=35)"; /* support: IE8 */
|
||||
background-image: none;
|
||||
}
|
||||
.ui-state-disabled .ui-icon {
|
||||
-ms-filter: "alpha(opacity=35)"; /* support: IE8 - See #6059 */
|
||||
}
|
||||
|
||||
/* Icons
|
||||
----------------------------------*/
|
||||
@@ -437,10 +432,8 @@ a.ui-button:active,
|
||||
/* Overlays */
|
||||
.ui-widget-overlay {
|
||||
background: #aaaaaa;
|
||||
opacity: .003;
|
||||
-ms-filter: Alpha(Opacity=.3); /* support: IE8 */
|
||||
opacity: .3;
|
||||
}
|
||||
.ui-widget-shadow {
|
||||
-webkit-box-shadow: 0px 0px 5px #666666;
|
||||
box-shadow: 0px 0px 5px #666666;
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
Selectize.define("selectize-plugin-a11y",function(c){var t=this,l=13;typeof t.accessibility=="undefined"&&(t.accessibility={}),t.accessibility.helpers={randomId:function(e){for(var r="",s=e||10,i="abcdefghijklmnopqrstuvwxyz0123456789",n=i.length,a=0;a<s;a++)r+=i[Math.floor(n*Math.random())];return r}},t.accessibility.liveRegion={$region:"",speak:function(e){var r=$("<div></div>");r.text(e),this.$region.html(r)},domListener:function(){var e=new MutationObserver(function(r){r.forEach(function(s){var i=$(s.target);if(i.hasClass("items"))if(i.hasClass("dropdown-active")){t.$control_input.attr("aria-expanded","true");for(var n=t.$dropdown_content[0].children,a=0;a<n.length;a++){var o=n[a].attributes;o.role||n[a].setAttribute("role","option"),o.id||n[a].setAttribute("id",t.accessibility.helpers.randomId())}}else t.$control_input.attr("aria-expanded","false"),t.$control_input.removeAttr("aria-activedescendant");else i.hasClass("active")&&i.attr("data-value")&&(t.$control_input.attr("aria-activedescendant",i.attr("id")),t.accessibility.liveRegion.speak(i.text(),500))})});e.observe(t.$dropdown[0],{attributes:!0,attributeFilter:["class"],subtree:!0,attributeOldValue:!0}),e.observe(t.$control[0],{attributes:!0,attributeFilter:["class"]}),e.observe(t.$control_input[0],{attributes:!0,attributeFilter:["value"]})},setAttributes:function(){this.$region.attr({"aria-live":"assertive",role:"log","aria-relevant":"additions","aria-atomic":"true"})},setStyles:function(){this.$region.css({position:"absolute",width:"1px",height:"1px","margin-top":"-1px",clip:"rect(1px, 1px, 1px, 1px)",overflow:"hidden"})},init:function(){this.$region=$("<div>"),this.setAttributes(),this.setStyles(),$("body").append(this.$region),this.domListener()}},this.setup=function(){var e=t.setup;return function(){e.apply(this,arguments);var r=t.accessibility.helpers.randomId(),s=t.accessibility.helpers.randomId();t.$control.on("keydown",function(i){i.keyCode===l&&(t.settings.openOnFocus?(t.settings.openOnFocus=!1,t.focus(),setTimeout(function(){t.settings.openOnFocus=!0},0)):t.focus())}),t.$control_input.attr({role:"combobox","aria-expanded":"false",haspopup:"listbox","aria-owns":s,"aria-label":t.$wrapper.closest("[data-accessibility-selectize-label]").attr("data-accessibility-selectize-label")}),t.$dropdown_content.attr({role:"listbox",id:s}),t.accessibility.liveRegion.init()}}(),this.destroy=function(){var e=t.destroy;return function(){return t.accessibility.liveRegion.$region.remove(),e.apply(this,arguments)}}()});
|
||||
Selectize.define("selectize-plugin-a11y",function(c){var t=this,l=13;typeof t.accessibility>"u"&&(t.accessibility={}),t.accessibility.helpers={randomId:function(e){for(var r="",s=e||10,i="abcdefghijklmnopqrstuvwxyz0123456789",n=i.length,a=0;a<s;a++)r+=i[Math.floor(n*Math.random())];return r}},t.accessibility.liveRegion={$region:"",speak:function(e){var r=$("<div></div>");r.text(e),this.$region.html(r)},domListener:function(){var e=new MutationObserver(function(r){r.forEach(function(s){var i=$(s.target);if(i.hasClass("items"))if(i.hasClass("dropdown-active")){t.$control_input.attr("aria-expanded","true");for(var n=t.$dropdown_content[0].children,a=0;a<n.length;a++){var o=n[a].attributes;o.role||n[a].setAttribute("role","option"),o.id||n[a].setAttribute("id",t.accessibility.helpers.randomId())}}else t.$control_input.attr("aria-expanded","false"),t.$control_input.removeAttr("aria-activedescendant");else i.hasClass("active")&&i.attr("data-value")&&(t.$control_input.attr("aria-activedescendant",i.attr("id")),t.accessibility.liveRegion.speak(i.text(),500))})});e.observe(t.$dropdown[0],{attributes:!0,attributeFilter:["class"],subtree:!0,attributeOldValue:!0}),e.observe(t.$control[0],{attributes:!0,attributeFilter:["class"]}),e.observe(t.$control_input[0],{attributes:!0,attributeFilter:["value"]})},setAttributes:function(){this.$region.attr({"aria-live":"assertive",role:"log","aria-relevant":"additions","aria-atomic":"true"})},setStyles:function(){this.$region.css({position:"absolute",width:"1px",height:"1px","margin-top":"-1px",clip:"rect(1px, 1px, 1px, 1px)",overflow:"hidden"})},init:function(){this.$region=$("<div>"),this.setAttributes(),this.setStyles(),$("body").append(this.$region),this.domListener()}},this.setup=function(){var e=t.setup;return function(){e.apply(this,arguments);var r=t.accessibility.helpers.randomId(),s=t.accessibility.helpers.randomId();t.$control.on("keydown",function(i){i.keyCode===l&&(t.settings.openOnFocus?(t.settings.openOnFocus=!1,t.focus(),setTimeout(function(){t.settings.openOnFocus=!0},0)):t.focus())}),t.$control_input.attr({role:"combobox","aria-expanded":"false",haspopup:"listbox","aria-owns":s,"aria-label":t.$wrapper.closest("[data-accessibility-selectize-label]").attr("data-accessibility-selectize-label")}),t.$dropdown_content.attr({role:"listbox",id:s}),t.accessibility.liveRegion.init()}}(),this.destroy=function(){var e=t.destroy;return function(){return t.accessibility.liveRegion.$region.remove(),e.apply(this,arguments)}}()});
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
/*! shiny 1.8.1.9001 | (c) 2012-2024 RStudio, PBC. | License: GPL-3 | file LICENSE */
|
||||
/*! shiny 1.10.0.9001 | (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.8.1.9001 | (c) 2012-2024 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)});})();
|
||||
/*! shiny 1.10.0.9001 | (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,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.\n\nconst indirectEval = eval;\n\nexport { indirectEval };\n", "/* 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: { data: { code: string } }) {\n const message = e.data;\n\n if (message.code) indirectEval(message.code);\n});\n"],
|
||||
"mappings": ";mBAQA,IAAMA,EAAe,KCHrB,OAAO,iBAAiB,UAAW,SAAUC,EAA+B,CAC1E,IAAMC,EAAUD,EAAE,KAEdC,EAAQ,MAAMC,EAAaD,EAAQ,IAAI,CAC7C,CAAC",
|
||||
"names": ["indirectEval", "e", "message", "indirectEval"]
|
||||
}
|
||||
|
||||
29022
inst/www/shared/shiny.js
4
inst/www/shared/shiny.min.css
vendored
449
inst/www/shared/shiny.min.js
vendored
@@ -44,9 +44,9 @@ div:where(.shiny-html-output) {
|
||||
/* uiOutput()/ conditionalPanel() are "pass-through" containers when they have children. */
|
||||
&:has(> *) {
|
||||
display: contents;
|
||||
/* Pass along styles that no longer impact the pass-through container */
|
||||
/* Pass along styles that no longer impact the pass-through container */
|
||||
&.recalculating > * {
|
||||
opacity: 0.3;
|
||||
opacity: var(--_shiny-fade-opacity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -156,7 +156,8 @@ html.autoreload-enabled #shiny-disconnected-overlay.reloading {
|
||||
}
|
||||
|
||||
.recalculating {
|
||||
opacity: 0.3;
|
||||
--_shiny-fade-opacity: var(--shiny-fade-opacity, 0.3);
|
||||
opacity: var(--_shiny-fade-opacity);
|
||||
transition: opacity 250ms ease 500ms;
|
||||
}
|
||||
|
||||
@@ -383,6 +384,14 @@ html.autoreload-enabled #shiny-disconnected-overlay.reloading {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* Styling for textAreaInput(autoresize=TRUE) */
|
||||
textarea.textarea-autoresize.form-control {
|
||||
padding: 5px 8px;
|
||||
resize: none;
|
||||
overflow-y: hidden;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
|
||||
#shiny-notification-panel {
|
||||
position: fixed;
|
||||
|
||||
@@ -45,6 +45,58 @@ is, a function that quickly returns a promise) and allows even that very
|
||||
session to immediately unblock and carry on with other user interactions.
|
||||
}
|
||||
|
||||
\examples{
|
||||
\dontshow{if (rlang::is_interactive() && rlang::is_installed("mirai")) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf}
|
||||
library(shiny)
|
||||
library(bslib)
|
||||
library(mirai)
|
||||
|
||||
# Set background processes for running tasks
|
||||
daemons(1)
|
||||
# Reset when the app is stopped
|
||||
onStop(function() daemons(0))
|
||||
|
||||
ui <- page_fluid(
|
||||
titlePanel("Extended Task Demo"),
|
||||
p(
|
||||
'Click the button below to perform a "calculation"',
|
||||
"that takes a while to perform."
|
||||
),
|
||||
input_task_button("recalculate", "Recalculate"),
|
||||
p(textOutput("result"))
|
||||
)
|
||||
|
||||
server <- function(input, output) {
|
||||
rand_task <- ExtendedTask$new(function() {
|
||||
mirai(
|
||||
{
|
||||
# Slow operation goes here
|
||||
Sys.sleep(2)
|
||||
sample(1:100, 1)
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
# Make button state reflect task.
|
||||
# If using R >=4.1, you can do this instead:
|
||||
# rand_task <- ExtendedTask$new(...) |> bind_task_button("recalculate")
|
||||
bind_task_button(rand_task, "recalculate")
|
||||
|
||||
observeEvent(input$recalculate, {
|
||||
# Invoke the extended in an observer
|
||||
rand_task$invoke()
|
||||
})
|
||||
|
||||
output$result <- renderText({
|
||||
# React to updated results when the task completes
|
||||
number <- rand_task$result()
|
||||
paste0("Your number is ", number, ".")
|
||||
})
|
||||
}
|
||||
|
||||
shinyApp(ui, server)
|
||||
\dontshow{\}) # examplesIf}
|
||||
}
|
||||
\section{Methods}{
|
||||
\subsection{Public methods}{
|
||||
\itemize{
|
||||
@@ -71,11 +123,12 @@ server function.
|
||||
\item{\code{func}}{The long-running operation to execute. This should be an
|
||||
asynchronous function, meaning, it should use the
|
||||
\href{https://rstudio.github.io/promises/}{\{promises\}} package, most
|
||||
likely in conjuction with the
|
||||
likely in conjunction with the
|
||||
\href{https://mirai.r-lib.org}{\{mirai\}} or
|
||||
\href{https://rstudio.github.io/promises/articles/promises_04_futures.html}{\{future\}}
|
||||
package. (In short, the return value of \code{func} should be a
|
||||
\code{\link[future:future]{Future}} object, or a \code{promise}, or something else
|
||||
that \code{\link[promises:is.promise]{promises::as.promise()}} understands.)
|
||||
\code{\link[mirai:mirai]{mirai}}, \code{\link[future:future]{Future}}, \code{promise},
|
||||
or something else that \code{\link[promises:is.promise]{promises::as.promise()}} understands.)
|
||||
|
||||
It's also important that this logic does not read from any
|
||||
reactive inputs/sources, as inputs may change after the function is
|
||||
|
||||
@@ -182,7 +182,7 @@ If you want to use a cache that is shared across multiple R processes, you
|
||||
can use a \code{\link[cachem:cache_disk]{cachem::cache_disk()}}. You can create a application-level shared
|
||||
cache by putting this at the top of your app.R, server.R, or global.R:
|
||||
|
||||
\if{html}{\out{<div class="sourceCode">}}\preformatted{shinyOptions(cache = cachem::cache_disk(file.path(dirname(tempdir()), "myapp-cache"))
|
||||
\if{html}{\out{<div class="sourceCode">}}\preformatted{shinyOptions(cache = cachem::cache_disk(file.path(dirname(tempdir()), "myapp-cache")))
|
||||
}\if{html}{\out{</div>}}
|
||||
|
||||
This will create a subdirectory in your system temp directory named
|
||||
@@ -234,8 +234,8 @@ With a cached reactive expression, the key and/or value expression can be
|
||||
promises, but rather objects provided by the
|
||||
\href{https://rstudio.github.io/promises/}{\pkg{promises}} package, which
|
||||
are similar to promises in JavaScript. (See \code{\link[promises:promise]{promises::promise()}} for more
|
||||
information.) You can also use \code{\link[future:future]{future::future()}} objects to run code in a
|
||||
separate process or even on a remote machine.
|
||||
information.) You can also use \code{\link[mirai:mirai]{mirai::mirai()}} or \code{\link[future:future]{future::future()}}
|
||||
objects to run code in a separate process or even on a remote machine.
|
||||
|
||||
If the value returns a promise, then anything that consumes the cached
|
||||
reactive must expect it to return a promise.
|
||||
|
||||
@@ -11,6 +11,8 @@ busyIndicatorOptions(
|
||||
spinner_size = NULL,
|
||||
spinner_delay = NULL,
|
||||
spinner_selector = NULL,
|
||||
fade_opacity = NULL,
|
||||
fade_selector = NULL,
|
||||
pulse_background = NULL,
|
||||
pulse_height = NULL,
|
||||
pulse_speed = NULL
|
||||
@@ -45,6 +47,13 @@ if the computation finishes quickly.}
|
||||
scoping the spinner customization. The default (\code{NULL}) will apply the
|
||||
spinner customization to the parent element of the spinner.}
|
||||
|
||||
\item{fade_opacity}{The opacity (a number between 0 and 1) for recalculating
|
||||
output. Set to 1 to "disable" the fade.}
|
||||
|
||||
\item{fade_selector}{A character string containing a CSS selector for
|
||||
scoping the spinner customization. The default (\code{NULL}) will apply the
|
||||
spinner customization to the parent element of the spinner.}
|
||||
|
||||
\item{pulse_background}{A CSS background definition for the pulse. The
|
||||
default uses a
|
||||
\href{https://developer.mozilla.org/en-US/docs/Web/CSS/gradient/linear-gradient}{linear-gradient}
|
||||
@@ -57,11 +66,21 @@ CSS size.}
|
||||
time.}
|
||||
}
|
||||
\description{
|
||||
When busy indicators are enabled (see \code{\link[=useBusyIndicators]{useBusyIndicators()}}), a spinner is
|
||||
shown on each calculating/recalculating output, and a pulsing banner is shown
|
||||
at the top of the page when the app is otherwise busy. This function allows
|
||||
you to customize the appearance of those busy indicators. To apply the
|
||||
customization, include the result of this function inside the app's UI.
|
||||
Shiny automatically includes busy indicators, which more specifically means:
|
||||
\enumerate{
|
||||
\item Calculating/recalculating outputs have a spinner overlay.
|
||||
\item Outputs fade out/in when recalculating.
|
||||
\item When no outputs are calculating/recalculating, but Shiny is busy
|
||||
doing something else (e.g., a download, side-effect, etc), a page-level
|
||||
pulsing banner is shown.
|
||||
}
|
||||
|
||||
This function allows you to customize the appearance of these busy indicators
|
||||
by including the result of this function inside the app's UI. Note that,
|
||||
unless \code{spinner_selector} (or \code{fade_selector}) is specified, the spinner/fade
|
||||
customization applies to the parent element. If the customization should
|
||||
instead apply to the entire page, set \code{spinner_selector = 'html'} and
|
||||
\code{fade_selector = 'html'}.
|
||||
}
|
||||
\examples{
|
||||
\dontshow{if (rlang::is_interactive()) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf}
|
||||
@@ -113,5 +132,5 @@ shinyApp(ui, server)
|
||||
\dontshow{\}) # examplesIf}
|
||||
}
|
||||
\seealso{
|
||||
\code{\link[=useBusyIndicators]{useBusyIndicators()}} for enabling/disabling busy indicators.
|
||||
\code{\link[=useBusyIndicators]{useBusyIndicators()}} to disable/enable busy indicators.
|
||||
}
|
||||
|
||||
@@ -72,8 +72,8 @@ example, some render functions call \code{\link[=createWebDependency]{createWebD
|
||||
is able to serve JS and CSS resources.}
|
||||
|
||||
\item{q}{Quosure of the expression \code{x}. When capturing expressions to create
|
||||
your quosure, it is recommended to use \code{\link[=enquo0]{enquo0()}} to not unquote the
|
||||
object too early. See \code{\link[=enquo0]{enquo0()}} for more details.}
|
||||
your quosure, it is recommended to use \code{\link[rlang:defusing-advanced]{rlang::enquo0()}} to not unquote
|
||||
the object too early. See \code{\link[rlang:defusing-advanced]{rlang::enquo0()}} for more details.}
|
||||
|
||||
\item{label}{A label for the object to be shown in the debugger. Defaults to
|
||||
the name of the calling function.}
|
||||
|
||||
@@ -60,4 +60,14 @@ server <- function(input, output) {
|
||||
|
||||
shinyApp(ui, server)
|
||||
}
|
||||
|
||||
}
|
||||
\seealso{
|
||||
\itemize{
|
||||
\item The download handler, like other outputs, is suspended (disabled) by
|
||||
default for download buttons and links that are hidden. Use
|
||||
\code{\link[=outputOptions]{outputOptions()}} to control this behavior, e.g. to set
|
||||
\code{suspendWhenHidden = FALSE} if the download is initiated by
|
||||
programmatically clicking on the download button using JavaScript.
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,9 @@ numericInput(
|
||||
min = NA,
|
||||
max = NA,
|
||||
step = NA,
|
||||
width = NULL
|
||||
width = NULL,
|
||||
...,
|
||||
updateOn = c("change", "blur")
|
||||
)
|
||||
}
|
||||
\arguments{
|
||||
@@ -29,6 +31,16 @@ numericInput(
|
||||
|
||||
\item{width}{The width of the input, e.g. \code{'400px'}, or \code{'100\%'};
|
||||
see \code{\link[=validateCssUnit]{validateCssUnit()}}.}
|
||||
|
||||
\item{...}{Ignored, included to require named arguments and for future
|
||||
feature expansion.}
|
||||
|
||||
\item{updateOn}{A character vector specifying when the input should be
|
||||
updated. Options are \code{"change"} (default) and \code{"blur"}. Use \code{"change"} to
|
||||
update the input immediately whenever the value changes. Use \code{"blur"}to
|
||||
delay the input update until the input loses focus (the user moves away
|
||||
from the input), or when Enter is pressed (or Cmd/Ctrl + Enter for
|
||||
\code{\link[=textAreaInput]{textAreaInput()}}).}
|
||||
}
|
||||
\value{
|
||||
A numeric input control that can be added to a UI definition.
|
||||
|
||||
@@ -4,7 +4,15 @@
|
||||
\alias{passwordInput}
|
||||
\title{Create a password input control}
|
||||
\usage{
|
||||
passwordInput(inputId, label, value = "", width = NULL, placeholder = NULL)
|
||||
passwordInput(
|
||||
inputId,
|
||||
label,
|
||||
value = "",
|
||||
width = NULL,
|
||||
placeholder = NULL,
|
||||
...,
|
||||
updateOn = c("change", "blur")
|
||||
)
|
||||
}
|
||||
\arguments{
|
||||
\item{inputId}{The \code{input} slot that will be used to access the value.}
|
||||
@@ -19,6 +27,16 @@ see \code{\link[=validateCssUnit]{validateCssUnit()}}.}
|
||||
\item{placeholder}{A character string giving the user a hint as to what can
|
||||
be entered into the control. Internet Explorer 8 and 9 do not support this
|
||||
option.}
|
||||
|
||||
\item{...}{Ignored, included to require named arguments and for future
|
||||
feature expansion.}
|
||||
|
||||
\item{updateOn}{A character vector specifying when the input should be
|
||||
updated. Options are \code{"change"} (default) and \code{"blur"}. Use \code{"change"} to
|
||||
update the input immediately whenever the value changes. Use \code{"blur"}to
|
||||
delay the input update until the input loses focus (the user moves away
|
||||
from the input), or when Enter is pressed (or Cmd/Ctrl + Enter for
|
||||
\code{\link[=textAreaInput]{textAreaInput()}}).}
|
||||
}
|
||||
\value{
|
||||
A text input control that can be added to a UI definition.
|
||||
|
||||
@@ -18,7 +18,10 @@ reactive(
|
||||
is.reactive(x)
|
||||
}
|
||||
\arguments{
|
||||
\item{x}{For \code{is.reactive()}, an object to test. For \code{reactive()}, an expression. When passing in a \code{\link[=quo]{quo()}}sure with \code{reactive()}, remember to use \code{\link[rlang:inject]{rlang::inject()}} to distinguish that you are passing in the content of your quosure, not the expression of the quosure.}
|
||||
\item{x}{For \code{is.reactive()}, an object to test. For \code{reactive()}, an
|
||||
expression. When passing in a \code{\link[rlang:defusing-advanced]{rlang::quo()}}sure with \code{reactive()},
|
||||
remember to use \code{\link[rlang:inject]{rlang::inject()}} to distinguish that you are passing in
|
||||
the content of your quosure, not the expression of the quosure.}
|
||||
|
||||
\item{env}{The parent environment for the reactive expression. By default,
|
||||
this is the calling environment, the same as when defining an ordinary
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
\alias{reactlog}
|
||||
\alias{reactlogShow}
|
||||
\alias{reactlogReset}
|
||||
\alias{reactlogAddMark}
|
||||
\title{Reactive Log Visualizer}
|
||||
\usage{
|
||||
reactlog()
|
||||
@@ -11,10 +12,15 @@ reactlog()
|
||||
reactlogShow(time = TRUE)
|
||||
|
||||
reactlogReset()
|
||||
|
||||
reactlogAddMark(session = getDefaultReactiveDomain())
|
||||
}
|
||||
\arguments{
|
||||
\item{time}{A boolean that specifies whether or not to display the
|
||||
time that each reactive takes to calculate a result.}
|
||||
|
||||
\item{session}{The Shiny session to assign the mark to. Defaults to the
|
||||
current session.}
|
||||
}
|
||||
\description{
|
||||
Provides an interactive browser-based tool for visualizing reactive
|
||||
@@ -22,7 +28,7 @@ dependencies and execution in your application.
|
||||
}
|
||||
\details{
|
||||
To use the reactive log visualizer, start with a fresh R session and
|
||||
run the command \code{options(shiny.reactlog=TRUE)}; then launch your
|
||||
run the command \code{reactlog::reactlog_enable()}; then launch your
|
||||
application in the usual way (e.g. using \code{\link[=runApp]{runApp()}}). At
|
||||
any time you can hit Ctrl+F3 (or for Mac users, Command+F3) in your
|
||||
web browser to launch the reactive log visualization.
|
||||
@@ -45,17 +51,37 @@ browser will not load new activity into the report; you will need to
|
||||
call \code{reactlogShow()} explicitly.
|
||||
|
||||
For security and performance reasons, do not enable
|
||||
\code{shiny.reactlog} in production environments. When the option is
|
||||
enabled, it's possible for any user of your app to see at least some
|
||||
of the source code of your reactive expressions and observers.
|
||||
\code{options(shiny.reactlog=TRUE)} (or \code{reactlog::reactlog_enable()}) in
|
||||
production environments. When the option is enabled, it's possible
|
||||
for any user of your app to see at least some of the source code of
|
||||
your reactive expressions and observers. In addition, reactlog
|
||||
should be considered a memory leak as it will constantly grow and
|
||||
will never reset until the R session is restarted.
|
||||
}
|
||||
\section{Functions}{
|
||||
\itemize{
|
||||
\item \code{reactlog()}: Return a list of reactive information. Can be used in conjunction with
|
||||
\link[reactlog:reactlog_show]{reactlog::reactlog_show} to later display the reactlog graph.
|
||||
\item \code{reactlog()}: Return a list of reactive information. Can be used in
|
||||
conjunction with \link[reactlog:reactlog_show]{reactlog::reactlog_show} to later display the reactlog
|
||||
graph.
|
||||
|
||||
\item \code{reactlogShow()}: Display a full reactlog graph for all sessions.
|
||||
|
||||
\item \code{reactlogReset()}: Resets the entire reactlog stack. Useful for debugging and removing all prior reactive history.
|
||||
\item \code{reactlogReset()}: Resets the entire reactlog stack. Useful for debugging
|
||||
and removing all prior reactive history.
|
||||
|
||||
\item \code{reactlogAddMark()}: Adds "mark" entry into the reactlog stack. This is
|
||||
useful for programmatically adding a marked entry in the reactlog, rather
|
||||
than using your keyboard's key combination.
|
||||
|
||||
For example, we can \emph{mark} the reactlog at the beginning of an
|
||||
\code{observeEvent}'s calculation:
|
||||
|
||||
\if{html}{\out{<div class="sourceCode r">}}\preformatted{observeEvent(input$my_event_trigger, \{
|
||||
# Add a mark in the reactlog
|
||||
reactlogAddMark()
|
||||
# Run your regular event reaction code here...
|
||||
....
|
||||
\})
|
||||
}\if{html}{\out{</div>}}
|
||||
|
||||
}}
|
||||
|
||||
@@ -12,9 +12,10 @@ registerThemeDependency(func)
|
||||
of them.}
|
||||
}
|
||||
\description{
|
||||
This function registers a function that returns an \code{\link[=htmlDependency]{htmlDependency()}} or list
|
||||
of such objects. If \code{session$setCurrentTheme()} is called, the function will
|
||||
be re-executed, and the resulting html dependency will be sent to the client.
|
||||
This function registers a function that returns an
|
||||
\code{\link[htmltools:htmlDependency]{htmltools::htmlDependency()}} or list of such objects. If
|
||||
\code{session$setCurrentTheme()} is called, the function will be re-executed, and
|
||||
the resulting html dependency will be sent to the client.
|
||||
}
|
||||
\details{
|
||||
Note that \code{func} should \strong{not} be an anonymous function, or a function which
|
||||
|
||||
@@ -125,6 +125,9 @@ shinyApp(ui, server)
|
||||
}
|
||||
}
|
||||
\seealso{
|
||||
For more details on how the images are generated, and how to control
|
||||
\itemize{
|
||||
\item For more details on how the images are generated, and how to control
|
||||
the output, see \code{\link[=plotPNG]{plotPNG()}}.
|
||||
\item Use \code{\link[=outputOptions]{outputOptions()}} to set general output options for an image output.
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,3 +126,6 @@ vecFun()
|
||||
|
||||
})
|
||||
}
|
||||
\seealso{
|
||||
\code{\link[=outputOptions]{outputOptions()}}
|
||||
}
|
||||
|
||||
@@ -52,5 +52,5 @@ shinyApp(ui, server)
|
||||
|
||||
}
|
||||
\seealso{
|
||||
\code{\link[=uiOutput]{uiOutput()}}
|
||||
\code{\link[=uiOutput]{uiOutput()}}, \code{\link[=outputOptions]{outputOptions()}}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,9 @@ interactive sessions only.}
|
||||
to the \code{shiny.host} option, if set, or \code{"127.0.0.1"} if not.}
|
||||
|
||||
\item{display.mode}{The mode in which to display the example. Defaults to
|
||||
\code{showcase}, but may be set to \code{normal} to see the example without
|
||||
\code{"auto"}, which uses the value of \code{DisplayMode} in the example's
|
||||
\code{DESCRIPTION} file. Set to \code{"showcase"} to show the app code and
|
||||
description with the running app, or \code{"normal"} to see the example without
|
||||
code or commentary.}
|
||||
|
||||
\item{package}{The package in which to find the example (defaults to
|
||||
|
||||
@@ -61,7 +61,7 @@ Other contributors:
|
||||
\item John Fraser (showdown.js library) [contributor, copyright holder]
|
||||
\item John Gruber (showdown.js library) [contributor, copyright holder]
|
||||
\item Ivan Sagalaev (highlight.js library) [contributor, copyright holder]
|
||||
\item R Core Team (tar implementation from R) [contributor, copyright holder]
|
||||
\item R Core Team (tar implementation from R) [contributor, copyright holder]
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -44,16 +44,20 @@ have the extensions: r, htm, html, js, css, png, jpg, jpeg, gif. If any
|
||||
changes are detected, all connected Shiny sessions are reloaded. This
|
||||
allows for fast feedback loops when tweaking Shiny UI.
|
||||
|
||||
Since monitoring for changes is expensive (we simply poll for last
|
||||
modified times), this feature is intended only for development.
|
||||
Monitoring for changes is no longer expensive, thanks to the \pkg{watcher}
|
||||
package, but this feature is still intended only for development.
|
||||
|
||||
You can customize the file patterns Shiny will monitor by setting the
|
||||
shiny.autoreload.pattern option. For example, to monitor only ui.R:
|
||||
\code{options(shiny.autoreload.pattern = glob2rx("ui.R"))}
|
||||
shiny.autoreload.pattern option. For example, to monitor only \code{ui.R}:
|
||||
\code{options(shiny.autoreload.pattern = glob2rx("ui.R"))}.
|
||||
|
||||
The default polling interval is 500 milliseconds. You can change this
|
||||
by setting e.g. \code{options(shiny.autoreload.interval = 2000)} (every
|
||||
two seconds).}
|
||||
As mentioned above, Shiny no longer polls watched files for changes.
|
||||
Instead, using \pkg{watcher}, Shiny is notified of file changes as they
|
||||
occur. These changes are batched together within a customizable latency
|
||||
period. You can adjust this period by setting
|
||||
\code{options(shiny.autoreload.interval = 2000)} (in milliseconds). This value
|
||||
converted to seconds and passed to the \code{latency} argument of
|
||||
\code{\link[watcher:watcher]{watcher::watcher()}}. The default latency is 250ms.}
|
||||
\item{shiny.deprecation.messages (defaults to \code{TRUE})}{This controls whether messages for
|
||||
deprecated functions in Shiny will be printed. See
|
||||
\code{\link[=shinyDeprecated]{shinyDeprecated()}} for more information.}
|
||||
@@ -68,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.}
|
||||
|
||||
@@ -13,7 +13,10 @@ textAreaInput(
|
||||
cols = NULL,
|
||||
rows = NULL,
|
||||
placeholder = NULL,
|
||||
resize = NULL
|
||||
resize = NULL,
|
||||
...,
|
||||
autoresize = FALSE,
|
||||
updateOn = c("change", "blur")
|
||||
)
|
||||
}
|
||||
\arguments{
|
||||
@@ -46,6 +49,19 @@ option.}
|
||||
\item{resize}{Which directions the textarea box can be resized. Can be one of
|
||||
\code{"both"}, \code{"none"}, \code{"vertical"}, and \code{"horizontal"}. The default, \code{NULL},
|
||||
will use the client browser's default setting for resizing textareas.}
|
||||
|
||||
\item{...}{Ignored, included to require named arguments and for future
|
||||
feature expansion.}
|
||||
|
||||
\item{autoresize}{If \code{TRUE}, the textarea will automatically resize to fit
|
||||
the input text.}
|
||||
|
||||
\item{updateOn}{A character vector specifying when the input should be
|
||||
updated. Options are \code{"change"} (default) and \code{"blur"}. Use \code{"change"} to
|
||||
update the input immediately whenever the value changes. Use \code{"blur"}to
|
||||
delay the input update until the input loses focus (the user moves away
|
||||
from the input), or when Enter is pressed (or Cmd/Ctrl + Enter for
|
||||
\code{\link[=textAreaInput]{textAreaInput()}}).}
|
||||
}
|
||||
\value{
|
||||
A textarea input control that can be added to a UI definition.
|
||||
|
||||
@@ -4,7 +4,15 @@
|
||||
\alias{textInput}
|
||||
\title{Create a text input control}
|
||||
\usage{
|
||||
textInput(inputId, label, value = "", width = NULL, placeholder = NULL)
|
||||
textInput(
|
||||
inputId,
|
||||
label,
|
||||
value = "",
|
||||
width = NULL,
|
||||
placeholder = NULL,
|
||||
...,
|
||||
updateOn = c("change", "blur")
|
||||
)
|
||||
}
|
||||
\arguments{
|
||||
\item{inputId}{The \code{input} slot that will be used to access the value.}
|
||||
@@ -19,6 +27,16 @@ see \code{\link[=validateCssUnit]{validateCssUnit()}}.}
|
||||
\item{placeholder}{A character string giving the user a hint as to what can
|
||||
be entered into the control. Internet Explorer 8 and 9 do not support this
|
||||
option.}
|
||||
|
||||
\item{...}{Ignored, included to require named arguments and for future
|
||||
feature expansion.}
|
||||
|
||||
\item{updateOn}{A character vector specifying when the input should be
|
||||
updated. Options are \code{"change"} (default) and \code{"blur"}. Use \code{"change"} to
|
||||
update the input immediately whenever the value changes. Use \code{"blur"}to
|
||||
delay the input update until the input loses focus (the user moves away
|
||||
from the input), or when Enter is pressed (or Cmd/Ctrl + Enter for
|
||||
\code{\link[=textAreaInput]{textAreaInput()}}).}
|
||||
}
|
||||
\value{
|
||||
A text input control that can be added to a UI definition.
|
||||
|
||||