Compare commits

..

59 Commits

Author SHA1 Message Date
karangattu
5f9176ac3b yarn build (GitHub Actions) 2024-10-31 16:48:08 +00:00
Karan Gathani
8f6ccd6bc4 remove comment 2024-10-31 09:43:06 -07:00
karangattu
380dfe0904 yarn build (GitHub Actions) 2024-10-31 16:22:50 +00:00
Karan Gathani
774def26de Add entry in NEWS.md 2024-10-31 09:18:40 -07:00
Karan
b24cbcb7b8 Merge branch 'main' into add-clientdata-height-width 2024-10-30 21:12:03 -07:00
Carson
4d3ec8e7bd Add to initial values; re-send window sizes in a debounced and decoupled fashion on resize; remove user-agent and mobile values 2024-10-30 17:10:06 -05:00
karangattu
1018f7cc62 yarn build (GitHub Actions) 2024-10-29 01:29:43 +00:00
Karan Gathani
d87bcc5184 Merge branch 'add-clientdata-height-width' of https://github.com/rstudio/shiny into add-clientdata-height-width 2024-10-28 18:25:39 -07:00
Karan Gathani
ca590f3854 use existing sendImageSizeFns instead of new SendWindowSize 2024-10-28 18:25:37 -07:00
karangattu
a56cd962f0 yarn build (GitHub Actions) 2024-10-28 23:56:39 +00:00
Karan Gathani
078aeb23a8 use debouncer when fetching window sizes 2024-10-28 16:52:05 -07:00
Adam Foryś
79af1d6c92 Fix url bookmarking with possibility to modify excludes (#3762)
* Fix url bookmarking with possibility to modify excludes

* Update NEWS.md
2024-10-28 09:36:42 -05:00
karangattu
f0bbc1db56 yarn build (GitHub Actions) 2024-10-22 20:02:09 +00:00
Karan Gathani
930040b66c Add viewport and scrollable size values to clientdata 2024-10-22 12:50:48 -07:00
Yihui Xie
a145add5d4 Use double-tilde for strikethrough in Markdown tests (#4144)
* Use double-tilde for strikethrough in Markdown tests

The current dev version of commonmark has disabled single tilde   for strikethrough: https://github.com/r-lib/commonmark/pull/33 Using double-tilde will make it consistent with Pandoc's Markdown (where single-tilde is for subscripts).

* Skip tests if commonmark is outdated

* Revert "Skip tests if commonmark is outdated"

This reverts commit 97bee20863.

---------

Co-authored-by: Carson Sievert <cpsievert1@gmail.com>
2024-10-15 11:21:53 -05:00
Dan Gealow
abf71389be Fix sporadic dates (#3664) (#3665)
* Remove dateInput and dateRangeInput handlers for keyup and input events

This prevents spurious updates while typing, but still sends when enter is pressed, focus is lost, or the GUI is clicked (due to the remaining `changeDate` and `change` handlers).

* chore: small edits to comments and NEWS item

---------

Co-authored-by: Garrick Aden-Buie <garrick@adenbuie.com>
2024-09-30 17:26:43 -04:00
Garrick Aden-Buie
2e2114f99d fix(busy): Show pulse if only UI are recalculating (#4137)
* fix(busy): Show pulse if only UI are recalculating

Because UI elements don't get spinners
2024-09-30 13:55:47 -04:00
Garrick Aden-Buie
09d415502f docs(NEWS): Fix name of sliderInput() function (#4136) 2024-09-27 11:14:40 -04:00
Garrick Aden-Buie
c489fef4ff fix(input_slider): Make sure last used handle is always above others (#4131) 2024-09-27 10:09:43 -04:00
Garrick Aden-Buie
9d12b0fca7 fix(conditionalPanel): Coerce condition result to boolean (#4127)
Co-authored-by: Kamil Zyla <kamil@appsilon.com>
2024-09-27 09:38:15 -04:00
Garrick Aden-Buie
cc9b9d4e6a feat(pulse): Tweak pulse animation and height (#4122) 2024-09-27 09:31:44 -04:00
Joe Cheng
34f9e4484d Merge pull request #4134 from rstudio/test/fix-reactivity-timing
fix: Timing of throttle/debounce reactivity test
2024-09-25 15:51:29 -07:00
Garrick Aden-Buie
03a3f8f886 test(reactivity): Consolidate identical tests into for loop 2024-09-25 10:09:17 -04:00
Garrick Aden-Buie
b900db0c74 test: Update the other test 2024-09-24 22:05:52 -04:00
Garrick Aden-Buie
5fb3ebc2d9 ci: run tests again 2024-09-24 22:00:19 -04:00
Garrick Aden-Buie
fbc6b2df57 chore: take out debugging code 2024-09-24 21:32:27 -04:00
Garrick Aden-Buie
6208225354 fix: Tweak updates to avoid overlapping events 2024-09-24 21:22:40 -04:00
Garrick Aden-Buie
e22b693418 chore: show value in debug too 2024-09-24 21:01:29 -04:00
Garrick Aden-Buie
c7ca49c634 debug: Add debugging messages for debounce/throttle test 2024-09-24 20:44:31 -04:00
Carson Sievert
d84aa94762 Start new version (#4113)
* Start new version

* `yarn build` (GitHub Actions)

* Sync package version (GitHub Actions)

---------

Co-authored-by: cpsievert <cpsievert@users.noreply.github.com>
2024-08-01 10:28:25 -05:00
Carson Sievert
89e2c18531 v1.9.1 release candidate (#4112)
* v1.9.1 release candidate

* `yarn build` (GitHub Actions)

* Sync package version (GitHub Actions)

---------

Co-authored-by: cpsievert <cpsievert@users.noreply.github.com>
2024-08-01 09:42:31 -05:00
Joe Cheng
43d36c08dc Remove double-scaling in coordmap.getPanelCss() (#4111)
Fixes #4110
2024-07-31 11:31:31 -05:00
Carson Sievert
4bc330e5dd Start new version (#4108)
* Start new version

* `yarn build` (GitHub Actions)

* Sync package version (GitHub Actions)

---------

Co-authored-by: cpsievert <cpsievert@users.noreply.github.com>
2024-07-29 17:21:35 -05:00
Carson Sievert
56ab530d87 v1.9.0 release candidate (#4105)
* Start v1.9.0 release candidate

* Check-in revdep results

* `yarn build` (GitHub Actions)

* Sync package version (GitHub Actions)

* `yarn build` (GitHub Actions)

* ran revdepcheck on cloud. 2 errors reported. Both seem like false positives

* Fix R CMD check note about Rd links targets missing package anchors

---------

Co-authored-by: cpsievert <cpsievert@users.noreply.github.com>
Co-authored-by: Barret Schloerke <barret@posit.co>
Co-authored-by: schloerke <schloerke@users.noreply.github.com>
2024-07-29 17:10:38 -05:00
Garrick Aden-Buie
599209a036 chore: make pulse and spinner opt-in (for now) (#4107)
* chore: make pulse and spinner opt-in (for now)

* Reword busy indication NEWS section

---------

Co-authored-by: Carson <cpsievert1@gmail.com>
2024-07-29 11:21:28 -05:00
Joe Cheng
15b5fa6c01 Click handler on scaled image getting clipped (#4094)
* Fix #3234: Click handler on scaled image getting clipped

There were two related problems here, both happening in the same scenario:
when an imageOutput with click handlers is showing an image at less than
its natural size (e.g. a 1000x1000 px .png file, being displayed in the
web page at 500x500 due to max-width or for whatever other reason), any
click where the image coordinate (1000x1000) exceeds the display size
(500x500).

In the example above, a user clicks at 300x300 in the 500x500 displayed
image. We call 300x300 the "CSS coordinates". This gets scaled up into
the position in the PNG's own coordinate system, "image coordinates":
in this case, 600x600. Since the 600x600 image coordinate is greater
than the 500x500 CSS coordinate limit, the following issues were
triggered.

1. When imageOutput(click=clickOpts(clip=TRUE)) (the default), these
   clicks weren't registering at all. There was code that detected
   clicks that were inside the imageOutput but outside the actual image,
   but this code didn't take scaling into account.

2. Even with clip=FALSE, the click would be triggered BUT the `x` and `y`
   values on the click event were incorrect--they would max out at the
   CSS coordinate limit. This because plot and image output divide the
   world into "panels" and clicks snap to the nearest panel. In the case
   of image outputs, the server doesn't provide any panels, so the
   client makes one big panel that covers the whole image--but that code
   was erroneously using CSS sizes, not image sizes.

* Update NEWS
2024-07-26 11:09:39 -05:00
Carson Sievert
3f4676d9a6 Enable busy indicators by default, add ability to disable/customize fade (#4104)
* Follow up to #4040: enable busy indicators by default

* Make our spinner invisible when wrapped inside a shinycssloaders::withSpinner() container

* Add the ability to disable/customize recalculating opacity (i.e., fade)

* Fix bug with fade not being applied correctly when the output container has no children

* `devtools::document()` (GitHub Actions)

* `yarn build` (GitHub Actions)

* Update NEWS.md

* Follow up to b7e7af: need to also rest opacity for :empty case (for initial calculation)

* Rd docs fixes/improvements

---------

Co-authored-by: cpsievert <cpsievert@users.noreply.github.com>
2024-07-24 12:57:42 -05:00
Winston Chang
bb89cf9235 Add Shiny.initializedPromise (#4063)
* Convert Shiny from interface to class

* Remove unused global Shiny type

* Add prettier plugin for organizing imports

* Disable eslint indentation rule

* Simplify types

* Add Shiny.connectedPromise and Shiny.sessionInitPromise

* Fix typing issue

* Move prettier plugin to devDependencies

* Rename Shiny class to ShinyClass, and export type

* Remove global Shiny type; use internal imports

* Small code cleanup

* Move initShiny() function into ShinyClass

* Rebuild type files

* Raise error if window.Shiny already exists

* Rename promises

* Add InitStatusPromise class

* `yarn build` (GitHub Actions)

* Update news

* Remove isConnected

* Update yarn.lock

* Rename isInitialized to initializedPromise

* Rebuild shiny.js

* `yarn build` (GitHub Actions)

* Update NEWS

---------

Co-authored-by: wch <wch@users.noreply.github.com>
2024-07-23 22:11:20 -05:00
Randy Zwitch
25c40967da Update issue template to use new forum.posit.co URL (#4038)
Co-authored-by: Garrick Aden-Buie <garrick@adenbuie.com>
Co-authored-by: Carson Sievert <cpsievert1@gmail.com>
2024-07-23 14:59:17 -04:00
Barret Schloerke
068b232e75 feat(reactlog): Add reactlogAddMark() (#4103) 2024-07-22 11:29:02 -04:00
Garrick Aden-Buie
0b7fda707e chore: Enable return of dependency CSS as Sass files (#4044)
* chore: Enable return of dependency CSS as Sass files

Makes it possible to extract the Sass files prior to compilation for the following CSS:

* shiny
* selectize
* ionrangeslider
* daterange picker

* refactor: Take a more functional approach

* fix: missing selectizeDir

* rename: __SassLayer --> __Sass
2024-06-13 16:08:54 -04:00
Carson Sievert
9fd4ba199e Close #4080: Require bslib 0.6 or higher (#4085) 2024-06-03 09:39:29 -05:00
Dean Attali
43e40c7969 news: fix function name typo (#4076) 2024-06-03 09:13:42 -05:00
Winston Chang
248f19333c Bump cachem dependency. Closes #4032 2024-05-31 21:01:05 -05:00
Garrick Aden-Buie
306c4f847b feat(options): shiny.client_devmode (#4073) 2024-05-30 17:19:41 -04:00
Carson Sievert
e689cdc522 Close #4068: take opacity from last frame of animation (#4069) 2024-05-28 10:33:20 -05:00
Winston Chang
3e0efd8484 Update formatting settings (#4065)
* Format JSON files on save

* Add prettier plugin for organizing imports

* Reorganize imports

* Fix 'routine' build issues

* `yarn build` (GitHub Actions)

---------

Co-authored-by: wch <wch@users.noreply.github.com>
2024-05-24 09:37:28 -05:00
Carson Sievert
4a8400d2a5 Delay longer before dimming opacity when spinners are active (#4062)
* make sure to delay and transition the change in opacity when busy indicators are active

* better approach

* Fix

* Delay a touch longer
2024-05-23 11:52:02 -05:00
Carson Sievert
e432bb0592 Hide the busy pulse when disconnected (#4061) 2024-05-22 12:41:59 -05:00
Carson Sievert
d002734afe Follow up to #4057: remove unused JS files (#4060) 2024-05-21 15:21:27 -05:00
Carson Sievert
54e7377f24 Fade in spinner opacity after initial delay (#4058)
* Fade in spinner opacity after initial delay

* Slightly quicker fade in
2024-05-21 15:06:30 -05:00
Carson Sievert
a49d24108f Close #4056: Gut busy indicator logic to pulse up until 1st idle (#4057) 2024-05-20 17:57:19 -05:00
Garrick Aden-Buie
733a4e8983 fix(pulse): Remove radius from pulse (#4053) 2024-05-15 15:30:18 -04:00
Garrick Aden-Buie
6309a6fca3 tests(busyIndicatorOptions): set seed in the test without a helper (#4052) 2024-05-10 17:23:54 -04:00
Carson Sievert
3d66940402 Add busy indication (#4040)
* First pass at a proper state machine for managing output progress state

* `yarn build` (GitHub Actions)

* Add useBusyIndicators(), spinnerOptions(), and pulseOptions()

* Bring in new spinner defaults

* Use an actual div instead of a pseudo-element since chromium can't be trusted to show them when animated

* Revert "Use an actual div instead of a pseudo-element since chromium can't be trusted to show them when animated"

This reverts commit 6167c1dfd7.

* Embed animation inside svg (to avoid Chromium bug). Consolidate options into a singular busyIndicatorOptions()

* Add to pkgdown reference

* `devtools::document()` (GitHub Actions)

* `yarn build` (GitHub Actions)

* Bump version

* `yarn build` (GitHub Actions)

* Sync package version (GitHub Actions)

* Apply suggestions from code review

Co-authored-by: Garrick Aden-Buie <garrick@adenbuie.com>

* Update snapshots

* `devtools::document()` (GitHub Actions)

* Address feedback

* Bring in more spinner type options

* fix use of fs

* Code review

* `devtools::document()` (GitHub Actions)

* Sync package version (GitHub Actions)

* Update snapshots

* Fix comments

* Make snapshot consistent cross-platform

* Fix namespace issue

* Reduce specificity of position relative

* Skip snapshot on windows; update news

* Whoops

* Scope spinner customizations to parent element by default

* Update snapshots

* Reorder spinner types

* Set a private random seed in tests

* Better id naming

---------

Co-authored-by: cpsievert <cpsievert@users.noreply.github.com>
Co-authored-by: Garrick Aden-Buie <garrick@adenbuie.com>
2024-05-10 14:58:43 -05:00
Carson Sievert
2872c87e32 Allow for progress binding to move from Idle to Invalidated status (#4047)
* Close #4046: Allow for progress binding to move from Idle to Invalidated status

* `yarn build` (GitHub Actions)

---------

Co-authored-by: cpsievert <cpsievert@users.noreply.github.com>
2024-05-07 17:46:46 -05:00
Carson Sievert
ecb591f2e1 Disable downloadLink()/downloadButton() until their href is ready (#4041)
* Close #3606: disable downloadLink()/downloadButton() until their href has been populated

* Update news
2024-05-06 09:36:59 -05:00
Carson Sievert
8e37d45948 Improve/fix output progress reporting (#4039)
* First pass at a proper state machine for managing output progress state

* `yarn build` (GitHub Actions)

* Improved naming

* Include ascii version of diagram (as well as the source file

* Update srcts/src/shiny/outputProgress.ts

Co-authored-by: Joe Cheng <joe@rstudio.com>

* `yarn build` (GitHub Actions)

* Only call showProgress for outputs that have actually changed their recalculating status

* Add some more commentary

---------

Co-authored-by: cpsievert <cpsievert@users.noreply.github.com>
Co-authored-by: Joe Cheng <joe@rstudio.com>
2024-05-02 18:01:29 -05:00
Winston Chang
c11f120bb9 Change eslint-disable comments to eslint-disable-next-line (#4037)
* Change eslint-disable comments to eslint-disable-next-line

* Allow floating promise

* `yarn build` (GitHub Actions)

---------

Co-authored-by: wch <wch@users.noreply.github.com>
2024-04-30 13:19:48 -05:00
153 changed files with 15191 additions and 13840 deletions

View File

@@ -35,10 +35,6 @@ rules:
default-case:
- error
indent:
- error
- 2
- SwitchCase: 1
linebreak-style:
- error
- unix

View File

@@ -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.

View File

@@ -5,6 +5,7 @@ echo "Updating package.json version to match DESCRIPTION Version"
Rscript ./tools/updatePackageJsonVersion.R
if [ -n "$(git status --porcelain package.json)" ]
then
echo "package.json has changed after running ./tools/updatePackageJsonVersion.R. Re-running 'yarn build'"
yarn build
git add ./inst package.json && git commit -m 'Sync package version (GitHub Actions)' || echo "No package version to commit"
else

View File

@@ -15,4 +15,10 @@
"files.trimTrailingWhitespace": true,
"files.insertFinalNewline": true,
},
"[json]": {
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode",
"files.trimTrailingWhitespace": true,
"files.insertFinalNewline": true,
},
}

View File

@@ -1,7 +1,7 @@
Package: shiny
Type: Package
Title: Web Application Framework for R
Version: 1.8.1.9000
Version: 1.9.1.9000
Authors@R: c(
person("Winston", "Chang", role = c("aut", "cre"), email = "winston@posit.co", comment = c(ORCID = "0000-0002-1576-2126")),
person("Joe", "Cheng", role = "aut", email = "joe@posit.co"),
@@ -91,8 +91,8 @@ Imports:
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:
datasets,
@@ -128,6 +128,7 @@ Collate:
'map.R'
'utils.R'
'bootstrap.R'
'busy-indicators-spinners.R'
'busy-indicators.R'
'cache-utils.R'
'deprecated.R'
@@ -205,7 +206,7 @@ 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

View File

@@ -78,6 +78,7 @@ export(br)
export(browserViewer)
export(brushOpts)
export(brushedPoints)
export(busyIndicatorOptions)
export(callModule)
export(captureStackTraces)
export(checkboxGroupInput)
@@ -204,7 +205,6 @@ export(pre)
export(prependTab)
export(printError)
export(printStackTrace)
export(pulseOptions)
export(quoToFunction)
export(radioButtons)
export(reactive)
@@ -216,6 +216,7 @@ export(reactiveVal)
export(reactiveValues)
export(reactiveValuesToList)
export(reactlog)
export(reactlogAddMark)
export(reactlogReset)
export(reactlogShow)
export(registerInputHandler)
@@ -273,7 +274,6 @@ export(snapshotExclude)
export(snapshotPreprocessInput)
export(snapshotPreprocessOutput)
export(span)
export(spinnerOptions)
export(splitLayout)
export(stopApp)
export(strong)

56
NEWS.md
View File

@@ -1,9 +1,63 @@
# shiny (development version)
## New features and improvements
* Small improvements to the default pulse busy indicator to better blend with any background. It's also now slightly smaller by default. (#4122)
* When spinners and the pulse busy indicators are enabled, Shiny now shows the pulse indicator when dynamic UI elements are recalculating if no other spinners are present in the app. (#4137)
* Capture and send client window size and scroll dimensions to the server which can be accessed via `session$clientData`. (#4147)
## 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)
# 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)
@@ -16,7 +70,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)

View File

@@ -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),

View File

@@ -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,
@@ -1276,10 +1277,13 @@ downloadButton <- function(outputId,
...,
icon = shiny::icon("download")) {
tags$a(id=outputId,
class=paste('btn btn-default shiny-download-link', class),
class='btn btn-default shiny-download-link disabled',
class=class,
href='',
target='_blank',
download=NA,
"aria-disabled"="true",
tabindex="-1",
validateIcon(icon),
label, ...)
}
@@ -1288,10 +1292,13 @@ downloadButton <- function(outputId,
#' @export
downloadLink <- function(outputId, label="Download", class=NULL, ...) {
tags$a(id=outputId,
class=paste(c('shiny-download-link', class), collapse=" "),
class='shiny-download-link disabled',
class=class,
href='',
target='_blank',
download=NA,
"aria-disabled"="true",
tabindex="-1",
label, ...)
}

View File

@@ -0,0 +1,4 @@
# Generated by tools/updateSpinnerTypes.R: do not edit by hand
.busySpinnerTypes <-
c("ring", "ring2", "ring3", "bars", "bars2", "bars3", "pulse",
"pulse2", "pulse3", "dots", "dots2", "dots3")

View File

@@ -1,17 +1,31 @@
#' Use and customize busy indicator types.
#' Enable/disable busy indication
#'
#' To enable busy indicators, include the result of this function in the app's UI.
#' Busy indicators provide a visual cue to users when the server is busy
#' calculating outputs or otherwise performing tasks (e.g., producing
#' downloads). 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. Busy indication is enabled by
#' default for UI created with \pkg{bslib}, but must be enabled otherwise. To
#' enable/disable, include the result of this function in anywhere in the app's
#' UI.
#'
#' When both `spinners` and `pulse` are set to `TRUE`, the pulse is disabled
#' when spinner(s) are active. When both `spinners` and `pulse` are set to
#' `FALSE`, no busy indication is shown (other than the gray-ing out of
#' recalculating outputs).
#' When both `spinners` and `pulse` are set to `TRUE`, the pulse is
#' automatically disabled when spinner(s) are active. When both `spinners` and
#' `pulse` are set to `FALSE`, no busy indication is shown (other than the
#' graying out of recalculating outputs).
#'
#' @param ... Currently ignored.
#' @param spinners Whether to show a spinner on each calculating/recalculating
#' 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)`.
#'
#' @param spinners Overlay a spinner on each calculating/recalculating output.
#' @param pulse Show a pulsing banner at the top of the window when the server is busy.
#' @export
#' @seealso [spinnerOptions()] [pulseOptions()]
#' @examplesIf interactive()
#' @seealso [busyIndicatorOptions()] for customizing the appearance of the busy
#' indicators.
#' @examplesIf rlang::is_interactive()
#'
#' library(bslib)
#'
@@ -36,140 +50,233 @@
#' }
#'
#' shinyApp(ui, server)
useBusyIndicators <- function(spinners = TRUE, pulse = TRUE) {
useBusyIndicators <- function(..., spinners = TRUE, pulse = TRUE, fade = TRUE) {
rlang::check_dots_empty()
attrs <- list("shinyBusySpinners" = spinners, "shinyBusyPulse" = pulse)
js <- Map(function(key, value) {
if (value) {
js <- vapply(names(attrs), character(1), FUN = function(key) {
if (attrs[[key]]) {
sprintf("document.documentElement.dataset.%s = 'true';", key)
} else {
sprintf("delete document.documentElement.dataset.%s;", key)
}
}, names(attrs), attrs)
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 spinning busy indicators.
#' Customize busy indicator options
#'
#' Include the result of this function in the app's UI to customize spinner
#' appearance.
#' @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.
#'
#' @details To effectively disable spinners, set the `size` to "0px".
#' 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 type The type of spinner to use. Builtin options include: tadpole,
#' disc, dots, dot-track, and bounce. A custom type may also provided, which
#' should be a valid value for the CSS
#' [mask-image](https://developer.mozilla.org/en-US/docs/Web/CSS/mask-image)
#' property.
#' @param color The color of the spinner. This can be any valid CSS color.
#' Defaults to the app's "primary" color (if Bootstrap is on the page) or
#' light-blue if not.
#' @param size The size of the spinner. This can be any valid CSS size.
#' @param easing The easing function to use for the spinner animation. This can
#' be any valid CSS [easing
#' function](https://developer.mozilla.org/en-US/docs/Web/CSS/easing-function).
#' @param speed The amount of time for the spinner to complete a single
#' revolution. This can be any valid CSS time.
#' @param delay The amount of time to wait before showing the spinner. This can
#' be any valid CSS time and can useful for not showing the spinner
#' @param ... Currently ignored.
#' @param spinner_type The type of spinner. Pre-bundled types include:
#' '`r paste0(.busySpinnerTypes, collapse = "', '")`'.
#'
#' A path to a local SVG file can also be provided. The SVG should adhere to
#' the following rules:
#' * The SVG itself should contain the animation.
#' * It should avoid absolute sizes (the spinner's containing DOM element
#' size is set in CSS by `spinner_size`, so it should fill that container).
#' * It should avoid setting absolute colors (the spinner's containing DOM element
#' color is set in CSS by `spinner_color`, so it should inherit that color).
#' @param spinner_color The color of the spinner. This can be any valid CSS
#' color. Defaults to the app's "primary" color if Bootstrap is on the page.
#' @param spinner_size The size of the spinner. This can be any valid CSS size.
#' @param spinner_delay The amount of time to wait before showing the spinner.
#' This can be any valid CSS time and can be useful for not showing the spinner
#' if the computation finishes quickly.
#' @param css_selector A CSS selector for scoping the spinner customization.
#' Defaults to the root element.
#' @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)
#' of the theme's indigo, purple, and pink colors.
#' @param pulse_height The height of the pulsing banner. This can be any valid
#' CSS size.
#' @param pulse_speed The speed of the pulsing banner. This can be any valid CSS
#' time.
#'
#' @export
#' @seealso [useBusyIndicators()] [pulseOptions()]
#' @examplesIf interactive()
#' @seealso [useBusyIndicators()] to disable/enable busy indicators.
#' @examplesIf rlang::is_interactive()
#'
#' library(bslib)
#'
#' card_ui <- function(id, spinner_type = id) {
#' card(
#' busyIndicatorOptions(spinner_type = spinner_type),
#' card_header(paste("Spinner:", spinner_type)),
#' plotOutput(shiny::NS(id, "plot"))
#' )
#' }
#'
#' card_server <- function(id, simulate = reactive()) {
#' moduleServer(
#' id = id,
#' function(input, output, session) {
#' output$plot <- renderPlot({
#' Sys.sleep(1)
#' simulate()
#' plot(x = rnorm(100), y = rnorm(100))
#' })
#' }
#' )
#' }
#'
#' ui <- page_fillable(
#' useBusyIndicators(),
#' spinnerOptions(color = "orange"),
#' card(
#' card_header(
#' "A plot",
#' input_task_button("simulate", "Simulate"),
#' class = "d-flex justify-content-between align-items-center"
#' ),
#' plotOutput("p"),
#' input_task_button("simulate", "Simulate", icon = icon("refresh")),
#' layout_columns(
#' card_ui("ring"),
#' card_ui("bars"),
#' card_ui("dots"),
#' card_ui("pulse"),
#' col_widths = 6
#' )
#' )
#'
#' server <- function(input, output) {
#' output$p <- renderPlot({
#' input$simulate
#' Sys.sleep(4)
#' plot(x = rnorm(100), y = rnorm(100))
#' })
#' server <- function(input, output, session) {
#' simulate <- reactive(input$simulate)
#' card_server("ring", simulate)
#' card_server("bars", simulate)
#' card_server("dots", simulate)
#' card_server("pulse", simulate)
#' }
#'
#' shinyApp(ui, server)
spinnerOptions <- function(
type = NULL,
#'
busyIndicatorOptions <- function(
...,
color = NULL,
size = NULL,
easing = NULL,
speed = NULL,
delay = NULL,
css_selector = ":root"
spinner_type = NULL,
spinner_color = NULL,
spinner_size = NULL,
spinner_delay = NULL,
spinner_selector = NULL,
fade_opacity = NULL,
fade_selector = NULL,
pulse_background = NULL,
pulse_height = NULL,
pulse_speed = NULL
) {
# bounce requires a different animation than the others
if (isTRUE(type == "bounce")) {
animation <- "shiny-busy-spinner-bounce"
speed <- speed %||% "0.8s"
} else {
animation <- NULL
}
rlang::check_dots_empty()
# Supported types have a CSS var already defined with their SVG data
if (isTRUE(type %in% c("tadpole", "disc", "dots", "dot-track", "bounce"))) {
type <- sprintf("var(--_shiny-spinner-type-%s)", type)
}
res <- tagList(
spinnerOptions(
type = spinner_type,
color = spinner_color,
size = spinner_size,
delay = spinner_delay,
selector = spinner_selector
),
fadeOptions(opacity = fade_opacity, selector = fade_selector),
pulseOptions(
background = pulse_background,
height = pulse_height,
speed = pulse_speed
)
)
# Options are controlled via CSS variables.
css_vars <- paste0(c(
if (!is.null(type)) sprintf("--shiny-spinner-mask-img: %s", type),
if (!is.null(easing)) sprintf("--shiny-spinner-easing: %s", easing),
if (!is.null(animation)) sprintf("--shiny-spinner-animation: %s", animation),
if (!is.null(color)) sprintf("--shiny-spinner-color: %s", color),
if (!is.null(size)) sprintf("--shiny-spinner-size: %s", size),
if (!is.null(speed)) sprintf("--shiny-spinner-speed: %s", speed),
if (!is.null(delay)) sprintf("--shiny-spinner-delay: %s", delay)
), collapse = ";")
# The CSS cascade allows this to be called multiple times, and as long as the CSS
# selector is the same, the last call takes precedence. Also, css_selector allows
# for scoping of the spinner customization.
tags$style(HTML(paste0(css_selector, " {", css_vars, "}")))
bslib::as.card_item(dropNulls(res))
}
#' Customize the pulsing busy indicator.
#'
#' Include the result of this function in the app's UI to customize the pulsing
#' banner.
#'
#' @param color The color of the pulsing banner. This can be any valid CSS
#' color. Defaults to the app's "primary" color (if Bootstrap is on the page)
#' or light-blue if not.
#' @param height The height of the pulsing banner. This can be any valid CSS
#' size. Defaults to "3.5px".
#' @export
#' @seealso [useBusyIndicators()] [spinnerOptions()]
pulseOptions <- function(color = NULL, height = NULL, speed = NULL) {
css_vars <- paste0(c(
if (!is.null(color)) sprintf("--shiny-pulse-color: %s", color),
if (!is.null(height)) sprintf("--shiny-pulse-height: %s", height),
if (!is.null(speed)) sprintf("--shiny-pulse-speed: %s", speed)
), collapse = ";")
spinnerOptions <- function(type = NULL, color = NULL, size = NULL, delay = NULL, selector = NULL) {
if (is.null(type) && is.null(color) && is.null(size) && is.null(delay) && is.null(selector)) {
return(NULL)
}
url <- NULL
if (!is.null(type)) {
stopifnot(is.character(type) && length(type) == 1)
if (file.exists(type) && grepl("\\.svg$", type)) {
typeRaw <- readBin(type, "raw", n = file.info(type)$size)
url <- sprintf("url('data:image/svg+xml;base64,%s')", rawToBase64(typeRaw))
} else {
type <- rlang::arg_match(type, .busySpinnerTypes)
url <- sprintf("url('spinners/%s.svg')", type)
}
}
# Options controlled via CSS variables.
css_vars <- htmltools::css(
"--shiny-spinner-url" = url,
"--shiny-spinner-color" = htmltools::parseCssColors(color),
"--shiny-spinner-size" = htmltools::validateCssUnit(size),
"--shiny-spinner-delay" = delay
)
id <- NULL
if (is.null(selector)) {
id <- paste0("spinner-options-", p_randomInt(100, 1000000))
selector <- sprintf(":has(> #%s)", id)
}
css <- HTML(paste0(selector, " {", css_vars, "}"))
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)
}
css_vars <- htmltools::css(
"--shiny-pulse-background" = background,
"--shiny-pulse-height" = htmltools::validateCssUnit(height),
"--shiny-pulse-speed" = speed
)
tags$style(HTML(paste0(":root {", css_vars, "}")))
}
@@ -181,6 +288,7 @@ busyIndicatorDependency <- function() {
src = "www/shared/busy-indicators",
package = "shiny",
stylesheet = "busy-indicators.css",
script = "busy-indicators.js"
# TODO-future: In next release make spinners and pulse opt-out
# head = as.character(useBusyIndicators())
)
}

View File

@@ -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

122
R/graph.R
View File

@@ -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 = ""

View File

@@ -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,

View File

@@ -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",

View File

@@ -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,

View File

@@ -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

View File

@@ -151,6 +151,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
#' }
#'
#'

View File

@@ -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,

View File

@@ -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(

View File

@@ -0,0 +1,154 @@
<mxfile host="app.diagrams.net" modified="2024-05-07T22:40:15.581Z" agent="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36" etag="Zsitjb4PT-sW3A63SWd7" version="24.3.1" type="device">
<diagram name="Page-1" id="zz6aoPEyabkTD7ESu8ts">
<mxGraphModel dx="595" dy="889" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="850" pageHeight="1100" math="0" shadow="0">
<root>
<mxCell id="0" />
<mxCell id="1" parent="0" />
<mxCell id="DS1AFzV_2DL1v2c9v1jZ-1" value="Initial" style="ellipse;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1">
<mxGeometry x="120" y="270" width="80" height="40" as="geometry" />
</mxCell>
<mxCell id="DS1AFzV_2DL1v2c9v1jZ-2" value="Running" style="ellipse;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1">
<mxGeometry x="270" y="270" width="80" height="40" as="geometry" />
</mxCell>
<mxCell id="DS1AFzV_2DL1v2c9v1jZ-3" value="" style="endArrow=classic;html=1;rounded=0;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" parent="1" source="DS1AFzV_2DL1v2c9v1jZ-1" target="DS1AFzV_2DL1v2c9v1jZ-2" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="260" y="480" as="sourcePoint" />
<mxPoint x="310" y="270" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="DS1AFzV_2DL1v2c9v1jZ-4" value="Recalculating" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1">
<mxGeometry x="210" y="250" width="60" height="30" as="geometry" />
</mxCell>
<mxCell id="DS1AFzV_2DL1v2c9v1jZ-6" value="" style="endArrow=classic;html=1;rounded=0;exitX=0.5;exitY=1;exitDx=0;exitDy=0;" parent="1" source="DS1AFzV_2DL1v2c9v1jZ-2" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="320" y="220" as="sourcePoint" />
<mxPoint x="310" y="350" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="DS1AFzV_2DL1v2c9v1jZ-7" value="Idle" style="ellipse;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1">
<mxGeometry x="270" y="350" width="80" height="40" as="geometry" />
</mxCell>
<mxCell id="DS1AFzV_2DL1v2c9v1jZ-8" value="Recalculated" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1">
<mxGeometry x="330" y="310" width="60" height="30" as="geometry" />
</mxCell>
<mxCell id="DS1AFzV_2DL1v2c9v1jZ-9" value="" style="endArrow=classic;html=1;rounded=0;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" parent="1" source="DS1AFzV_2DL1v2c9v1jZ-7" target="DS1AFzV_2DL1v2c9v1jZ-10" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="320" y="320" as="sourcePoint" />
<mxPoint x="310" y="440" as="targetPoint" />
<Array as="points">
<mxPoint x="320" y="410" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="DS1AFzV_2DL1v2c9v1jZ-10" value="Value" style="ellipse;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="280" y="440" width="80" height="40" as="geometry" />
</mxCell>
<mxCell id="DS1AFzV_2DL1v2c9v1jZ-11" value="Error" style="ellipse;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="370" y="440" width="80" height="40" as="geometry" />
</mxCell>
<mxCell id="DS1AFzV_2DL1v2c9v1jZ-12" value="Persistent" style="ellipse;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1">
<mxGeometry x="90" y="440" width="80" height="40" as="geometry" />
</mxCell>
<mxCell id="DS1AFzV_2DL1v2c9v1jZ-13" value="Cancel" style="ellipse;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="180" y="440" width="80" height="40" as="geometry" />
</mxCell>
<mxCell id="DS1AFzV_2DL1v2c9v1jZ-14" value="&lt;span style=&quot;text-align: start; font-size: 10pt; font-family: Arial;&quot; data-sheets-userformat=&quot;{&amp;quot;2&amp;quot;:513,&amp;quot;3&amp;quot;:{&amp;quot;1&amp;quot;:0},&amp;quot;12&amp;quot;:0}&quot; data-sheets-value=&quot;{&amp;quot;1&amp;quot;:2,&amp;quot;2&amp;quot;:&amp;quot;{progress: {type: \&amp;quot;binding\&amp;quot;, message: {persistent: true}}}&amp;quot;}&quot; data-sheets-root=&quot;1&quot;&gt;{progress: {type: &quot;binding&quot;, message: {persistent: true}}}&lt;/span&gt;" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1">
<mxGeometry x="45" y="340" width="170" height="30" as="geometry" />
</mxCell>
<mxCell id="DS1AFzV_2DL1v2c9v1jZ-15" value="" style="endArrow=classic;html=1;rounded=0;exitX=0.5;exitY=1;exitDx=0;exitDy=0;" parent="1" source="DS1AFzV_2DL1v2c9v1jZ-10" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="320" y="400" as="sourcePoint" />
<mxPoint x="310" y="550" as="targetPoint" />
<Array as="points">
<mxPoint x="320" y="520" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="DS1AFzV_2DL1v2c9v1jZ-16" value="" style="endArrow=classic;html=1;rounded=0;exitX=0.5;exitY=1;exitDx=0;exitDy=0;" parent="1" source="DS1AFzV_2DL1v2c9v1jZ-11" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="320" y="490" as="sourcePoint" />
<mxPoint x="320" y="550" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="DS1AFzV_2DL1v2c9v1jZ-17" value="" style="endArrow=classic;html=1;rounded=0;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" parent="1" source="DS1AFzV_2DL1v2c9v1jZ-12" target="DS1AFzV_2DL1v2c9v1jZ-18" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="330" y="500" as="sourcePoint" />
<mxPoint x="290" y="540" as="targetPoint" />
<Array as="points" />
</mxGeometry>
</mxCell>
<mxCell id="DS1AFzV_2DL1v2c9v1jZ-18" value="Invalidated" style="ellipse;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1">
<mxGeometry x="260" y="550" width="80" height="40" as="geometry" />
</mxCell>
<mxCell id="DS1AFzV_2DL1v2c9v1jZ-20" value="" style="curved=1;endArrow=classic;html=1;rounded=0;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=1;entryY=0.5;entryDx=0;entryDy=0;" parent="1" source="DS1AFzV_2DL1v2c9v1jZ-18" target="DS1AFzV_2DL1v2c9v1jZ-2" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="260" y="480" as="sourcePoint" />
<mxPoint x="310" y="430" as="targetPoint" />
<Array as="points">
<mxPoint x="420" y="610" />
<mxPoint x="550" y="470" />
<mxPoint x="440" y="320" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="DS1AFzV_2DL1v2c9v1jZ-23" value="Recalculating" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1">
<mxGeometry x="450" y="340" width="60" height="30" as="geometry" />
</mxCell>
<mxCell id="DS1AFzV_2DL1v2c9v1jZ-24" value="" style="endArrow=classic;html=1;rounded=0;exitX=0;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" parent="1" source="DS1AFzV_2DL1v2c9v1jZ-2" target="DS1AFzV_2DL1v2c9v1jZ-12" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="320" y="400" as="sourcePoint" />
<mxPoint x="320" y="450" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="DS1AFzV_2DL1v2c9v1jZ-25" value="" style="endArrow=classic;html=1;rounded=0;exitX=1;exitY=1;exitDx=0;exitDy=0;entryX=0.395;entryY=-0.025;entryDx=0;entryDy=0;entryPerimeter=0;" parent="1" source="DS1AFzV_2DL1v2c9v1jZ-7" target="DS1AFzV_2DL1v2c9v1jZ-11" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="330" y="410" as="sourcePoint" />
<mxPoint x="330" y="460" as="targetPoint" />
<Array as="points">
<mxPoint x="380" y="410" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="DS1AFzV_2DL1v2c9v1jZ-26" value="" style="endArrow=classic;html=1;rounded=0;exitX=0;exitY=1;exitDx=0;exitDy=0;entryX=1;entryY=0;entryDx=0;entryDy=0;" parent="1" source="DS1AFzV_2DL1v2c9v1jZ-7" target="DS1AFzV_2DL1v2c9v1jZ-13" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="340" y="420" as="sourcePoint" />
<mxPoint x="340" y="470" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="DS1AFzV_2DL1v2c9v1jZ-27" value="Value" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1">
<mxGeometry x="270" y="400" width="60" height="30" as="geometry" />
</mxCell>
<mxCell id="DS1AFzV_2DL1v2c9v1jZ-28" value="Error" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1">
<mxGeometry x="330" y="400" width="60" height="30" as="geometry" />
</mxCell>
<mxCell id="DS1AFzV_2DL1v2c9v1jZ-29" value="No message" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1">
<mxGeometry x="200" y="400" width="60" height="30" as="geometry" />
</mxCell>
<mxCell id="DS1AFzV_2DL1v2c9v1jZ-30" value="" style="endArrow=classic;html=1;rounded=0;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0;entryY=0;entryDx=0;entryDy=0;" parent="1" source="DS1AFzV_2DL1v2c9v1jZ-13" target="DS1AFzV_2DL1v2c9v1jZ-18" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="230" y="490" as="sourcePoint" />
<mxPoint x="300" y="558" as="targetPoint" />
<Array as="points">
<mxPoint x="240" y="520" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="DS1AFzV_2DL1v2c9v1jZ-31" value="&lt;span style=&quot;font-family: Arial; font-size: 13px; text-align: left; white-space: pre-wrap; background-color: rgb(255, 255, 255);&quot;&gt;{progress: {type: &quot;binding&quot;}}&lt;/span&gt;" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="1" vertex="1">
<mxGeometry x="190" y="490" width="180" height="30" as="geometry" />
</mxCell>
<mxCell id="DS1AFzV_2DL1v2c9v1jZ-35" value="&lt;h1 style=&quot;margin-top: 0px;&quot;&gt;Shiny output progress states&lt;/h1&gt;&lt;p&gt;This diagram depicts a state machine of output binding progress state. Each node represents a possible state and each edge represents a server-&amp;gt;client message that moves outputs from one state to another. &lt;b&gt;If a node is highlighted in blue&lt;/b&gt;, then the output should be showing a busy state when visible (i.e., &lt;font face=&quot;Courier New&quot;&gt;binding.showProgress(true)&lt;/font&gt;)&lt;/p&gt;" style="text;html=1;whiteSpace=wrap;overflow=hidden;rounded=0;" parent="1" vertex="1">
<mxGeometry x="85" y="120" width="465" height="120" as="geometry" />
</mxCell>
<mxCell id="J9lKobNiy15ndT9nfcn--1" value="" style="curved=1;endArrow=classic;html=1;rounded=0;exitX=1;exitY=0;exitDx=0;exitDy=0;entryX=1;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="1" source="DS1AFzV_2DL1v2c9v1jZ-7" target="DS1AFzV_2DL1v2c9v1jZ-18">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="280" y="480" as="sourcePoint" />
<mxPoint x="220" y="510" as="targetPoint" />
<Array as="points">
<mxPoint x="610" y="420" />
</Array>
</mxGeometry>
</mxCell>
</root>
</mxGraphModel>
</diagram>
</mxfile>

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 312 KiB

File diff suppressed because one or more lines are too long

View File

@@ -1,3 +0,0 @@
/*! shiny 1.8.1.9000 | (c) 2012-2024 RStudio, PBC. | License: GPL-3 | file LICENSE */
"use strict";(function(){document.documentElement.classList.add("shiny-not-yet-idle");$(document).one("shiny:idle",function(){document.documentElement.classList.remove("shiny-not-yet-idle")});})();
//# sourceMappingURL=busy-indicators.js.map

View File

@@ -1,7 +0,0 @@
{
"version": 3,
"sources": ["../../../../srcts/extras/busy-indicators/busy-indicators.ts"],
"sourcesContent": ["// This of this like the .shiny-busy class that shiny.js puts on the root element,\n// except it's added before shiny.js is initialized, connected, etc.\n// TODO: maybe shiny.js should be doing something like this?\ndocument.documentElement.classList.add(\"shiny-not-yet-idle\");\n$(document).one(\"shiny:idle\", function () {\n document.documentElement.classList.remove(\"shiny-not-yet-idle\");\n});"],
"mappings": ";yBAGA,SAAS,gBAAgB,UAAU,IAAI,oBAAoB,EAC3D,EAAE,QAAQ,EAAE,IAAI,aAAc,UAAY,CACxC,SAAS,gBAAgB,UAAU,OAAO,oBAAoB,CAChE,CAAC",
"names": []
}

View File

@@ -0,0 +1,20 @@
The MIT License (MIT)
Copyright (c) Utkarsh Verma
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -0,0 +1 @@
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><style>.spinner_hzlK{animation:spinner_vc4H .8s linear infinite;animation-delay:-.8s}.spinner_koGT{animation-delay:-.65s}.spinner_YF1u{animation-delay:-.5s}@keyframes spinner_vc4H{0%{y:1px;height:22px}93.75%{y:5px;height:14px;opacity:.2}}</style><rect class="spinner_hzlK" x="1" y="1" width="6" height="22"/><rect class="spinner_hzlK spinner_koGT" x="9" y="1" width="6" height="22"/><rect class="spinner_hzlK spinner_YF1u" x="17" y="1" width="6" height="22"/></svg>

After

Width:  |  Height:  |  Size: 526 B

View File

@@ -0,0 +1 @@
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><style>.spinner_jCIR{animation:spinner_B8Vq .9s linear infinite;animation-delay:-.9s}.spinner_upm8{animation-delay:-.8s}.spinner_2eL5{animation-delay:-.7s}.spinner_Rp9l{animation-delay:-.6s}.spinner_dy3W{animation-delay:-.5s}@keyframes spinner_B8Vq{0%,66.66%{animation-timing-function:cubic-bezier(0.36,.61,.3,.98);y:6px;height:12px}33.33%{animation-timing-function:cubic-bezier(0.36,.61,.3,.98);y:1px;height:22px}}</style><rect class="spinner_jCIR" x="1" y="6" width="2.8" height="12"/><rect class="spinner_jCIR spinner_upm8" x="5.8" y="6" width="2.8" height="12"/><rect class="spinner_jCIR spinner_2eL5" x="10.6" y="6" width="2.8" height="12"/><rect class="spinner_jCIR spinner_Rp9l" x="15.4" y="6" width="2.8" height="12"/><rect class="spinner_jCIR spinner_dy3W" x="20.2" y="6" width="2.8" height="12"/></svg>

After

Width:  |  Height:  |  Size: 873 B

View File

@@ -0,0 +1 @@
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><style>.spinner_OSmW{transform-origin:center;animation:spinner_T6mA .75s step-end infinite}@keyframes spinner_T6mA{8.3%{transform:rotate(30deg)}16.6%{transform:rotate(60deg)}25%{transform:rotate(90deg)}33.3%{transform:rotate(120deg)}41.6%{transform:rotate(150deg)}50%{transform:rotate(180deg)}58.3%{transform:rotate(210deg)}66.6%{transform:rotate(240deg)}75%{transform:rotate(270deg)}83.3%{transform:rotate(300deg)}91.6%{transform:rotate(330deg)}100%{transform:rotate(360deg)}}</style><g class="spinner_OSmW"><rect x="11" y="1" width="2" height="5" opacity=".14"/><rect x="11" y="1" width="2" height="5" transform="rotate(30 12 12)" opacity=".29"/><rect x="11" y="1" width="2" height="5" transform="rotate(60 12 12)" opacity=".43"/><rect x="11" y="1" width="2" height="5" transform="rotate(90 12 12)" opacity=".57"/><rect x="11" y="1" width="2" height="5" transform="rotate(120 12 12)" opacity=".71"/><rect x="11" y="1" width="2" height="5" transform="rotate(150 12 12)" opacity=".86"/><rect x="11" y="1" width="2" height="5" transform="rotate(180 12 12)"/></g></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -0,0 +1 @@
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><style>.spinner_b2T7{animation:spinner_xe7Q .8s linear infinite}.spinner_YRVV{animation-delay:-.65s}.spinner_c9oY{animation-delay:-.5s}@keyframes spinner_xe7Q{93.75%,100%{r:3px}46.875%{r:.2px}}</style><circle class="spinner_b2T7" cx="4" cy="12" r="3"/><circle class="spinner_b2T7 spinner_YRVV" cx="12" cy="12" r="3"/><circle class="spinner_b2T7 spinner_c9oY" cx="20" cy="12" r="3"/></svg>

After

Width:  |  Height:  |  Size: 449 B

View File

@@ -0,0 +1 @@
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><style>.spinner_Wezc{transform-origin:center;animation:spinner_Oiah .75s step-end infinite}@keyframes spinner_Oiah{8.3%{transform:rotate(30deg)}16.6%{transform:rotate(60deg)}25%{transform:rotate(90deg)}33.3%{transform:rotate(120deg)}41.6%{transform:rotate(150deg)}50%{transform:rotate(180deg)}58.3%{transform:rotate(210deg)}66.6%{transform:rotate(240deg)}75%{transform:rotate(270deg)}83.3%{transform:rotate(300deg)}91.6%{transform:rotate(330deg)}100%{transform:rotate(360deg)}}</style><g class="spinner_Wezc"><circle cx="12" cy="2.5" r="1.5" opacity=".14"/><circle cx="16.75" cy="3.77" r="1.5" opacity=".29"/><circle cx="20.23" cy="7.25" r="1.5" opacity=".43"/><circle cx="21.50" cy="12.00" r="1.5" opacity=".57"/><circle cx="20.23" cy="16.75" r="1.5" opacity=".71"/><circle cx="16.75" cy="20.23" r="1.5" opacity=".86"/><circle cx="12" cy="21.5" r="1.5"/></g></svg>

After

Width:  |  Height:  |  Size: 926 B

View File

@@ -0,0 +1 @@
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><style>.spinner_DupU{animation:spinner_sM3D 1.2s infinite}.spinner_GWtZ{animation-delay:.1s}.spinner_dwN6{animation-delay:.2s}.spinner_46QP{animation-delay:.3s}.spinner_PD82{animation-delay:.4s}.spinner_eUgh{animation-delay:.5s}.spinner_eUaP{animation-delay:.6s}.spinner_j38H{animation-delay:.7s}.spinner_tVmX{animation-delay:.8s}.spinner_DQhX{animation-delay:.9s}.spinner_GIL4{animation-delay:1s}.spinner_n0Yb{animation-delay:1.1s}@keyframes spinner_sM3D{0%,50%{animation-timing-function:cubic-bezier(0,1,0,1);r:0}10%{animation-timing-function:cubic-bezier(.53,0,.61,.73);r:2px}}</style><circle class="spinner_DupU" cx="12" cy="3" r="0"/><circle class="spinner_DupU spinner_GWtZ" cx="16.50" cy="4.21" r="0"/><circle class="spinner_DupU spinner_n0Yb" cx="7.50" cy="4.21" r="0"/><circle class="spinner_DupU spinner_dwN6" cx="19.79" cy="7.50" r="0"/><circle class="spinner_DupU spinner_GIL4" cx="4.21" cy="7.50" r="0"/><circle class="spinner_DupU spinner_46QP" cx="21.00" cy="12.00" r="0"/><circle class="spinner_DupU spinner_DQhX" cx="3.00" cy="12.00" r="0"/><circle class="spinner_DupU spinner_PD82" cx="19.79" cy="16.50" r="0"/><circle class="spinner_DupU spinner_tVmX" cx="4.21" cy="16.50" r="0"/><circle class="spinner_DupU spinner_eUgh" cx="16.50" cy="19.79" r="0"/><circle class="spinner_DupU spinner_j38H" cx="7.50" cy="19.79" r="0"/><circle class="spinner_DupU spinner_eUaP" cx="12" cy="21" r="0"/></svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -0,0 +1 @@
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><style>.spinner_ZCsl{animation:spinner_qV4G 1.2s cubic-bezier(0.52,.6,.25,.99) infinite}.spinner_gaIW{animation-delay:.6s}@keyframes spinner_qV4G{0%{r:0;opacity:1}100%{r:11px;opacity:0}}</style><circle class="spinner_ZCsl" cx="12" cy="12" r="0"/><circle class="spinner_ZCsl spinner_gaIW" cx="12" cy="12" r="0"/></svg>

After

Width:  |  Height:  |  Size: 378 B

View File

@@ -0,0 +1 @@
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><style>.spinner_ngNb{animation:spinner_ZRWK 1.2s cubic-bezier(0.52,.6,.25,.99) infinite}.spinner_6TBP{animation-delay:.6s}@keyframes spinner_ZRWK{0%{transform:translate(12px,12px) scale(0);opacity:1}100%{transform:translate(0,0) scale(1);opacity:0}}</style><path class="spinner_ngNb" d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,20a9,9,0,1,1,9-9A9,9,0,0,1,12,21Z" transform="translate(12, 12) scale(0)"/><path class="spinner_ngNb spinner_6TBP" d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,20a9,9,0,1,1,9-9A9,9,0,0,1,12,21Z" transform="translate(12, 12) scale(0)"/></svg>

After

Width:  |  Height:  |  Size: 635 B

View File

@@ -0,0 +1 @@
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><style>.spinner_Uvk8{animation:spinner_otJF 1.6s cubic-bezier(.52,.6,.25,.99) infinite}.spinner_ypeD{animation-delay:.2s}.spinner_y0Rj{animation-delay:.4s}@keyframes spinner_otJF{0%{transform:translate(12px,12px) scale(0);opacity:1}75%,100%{transform:translate(0,0) scale(1);opacity:0}}</style><path class="spinner_Uvk8" d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,20a9,9,0,1,1,9-9A9,9,0,0,1,12,21Z" transform="translate(12, 12) scale(0)"/><path class="spinner_Uvk8 spinner_ypeD" d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,20a9,9,0,1,1,9-9A9,9,0,0,1,12,21Z" transform="translate(12, 12) scale(0)"/><path class="spinner_Uvk8 spinner_y0Rj" d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,20a9,9,0,1,1,9-9A9,9,0,0,1,12,21Z" transform="translate(12, 12) scale(0)"/></svg>

After

Width:  |  Height:  |  Size: 834 B

View File

@@ -0,0 +1 @@
<svg stroke="#000" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><style>.spinner_V8m1{transform-origin:center;animation:spinner_zKoa 2s linear infinite}.spinner_V8m1 circle{stroke-linecap:round;animation:spinner_YpZS 1.5s ease-in-out infinite}@keyframes spinner_zKoa{100%{transform:rotate(360deg)}}@keyframes spinner_YpZS{0%{stroke-dasharray:0 150;stroke-dashoffset:0}47.5%{stroke-dasharray:42 150;stroke-dashoffset:-16}95%,100%{stroke-dasharray:42 150;stroke-dashoffset:-59}}</style><g class="spinner_V8m1"><circle cx="12" cy="12" r="9.5" fill="none" stroke-width="3"></circle></g></svg>

After

Width:  |  Height:  |  Size: 598 B

View File

@@ -0,0 +1 @@
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><style>.spinner_ajPY{transform-origin:center;animation:spinner_AtaB .75s infinite linear}@keyframes spinner_AtaB{100%{transform:rotate(360deg)}}</style><path d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,19a8,8,0,1,1,8-8A8,8,0,0,1,12,20Z" opacity=".25"/><path d="M10.14,1.16a11,11,0,0,0-9,8.92A1.59,1.59,0,0,0,2.46,12,1.52,1.52,0,0,0,4.11,10.7a8,8,0,0,1,6.66-6.61A1.42,1.42,0,0,0,12,2.69h0A1.57,1.57,0,0,0,10.14,1.16Z" class="spinner_ajPY"/></svg>

After

Width:  |  Height:  |  Size: 509 B

View File

@@ -0,0 +1 @@
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><style>.spinner_aj0A{transform-origin:center;animation:spinner_KYSC .75s infinite linear}@keyframes spinner_KYSC{100%{transform:rotate(360deg)}}</style><path d="M12,4a8,8,0,0,1,7.89,6.7A1.53,1.53,0,0,0,21.38,12h0a1.5,1.5,0,0,0,1.48-1.75,11,11,0,0,0-21.72,0A1.5,1.5,0,0,0,2.62,12h0a1.53,1.53,0,0,0,1.49-1.3A8,8,0,0,1,12,4Z" class="spinner_aj0A"/></svg>

After

Width:  |  Height:  |  Size: 412 B

View File

@@ -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;
}

View File

@@ -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;

File diff suppressed because one or more lines are too long

View File

@@ -1,2 +1,2 @@
/*! shiny 1.8.1.9000 | (c) 2012-2024 RStudio, PBC. | License: GPL-3 | file LICENSE */
/*! shiny 1.9.1.9000 | (c) 2012-2024 RStudio, PBC. | License: GPL-3 | file LICENSE */
#showcase-well{border-radius:0}.shiny-code{background-color:#fff;margin-bottom:0}.shiny-code code{font-family:Menlo,Consolas,Courier New,monospace}.shiny-code-container{margin-top:20px;clear:both}.shiny-code-container h3{display:inline;margin-right:15px}.showcase-header{font-size:16px;font-weight:400}.showcase-code-link{text-align:right;padding:15px}#showcase-app-container{vertical-align:top}#showcase-code-tabs{margin-right:15px}#showcase-code-tabs pre{border:none;line-height:1em}#showcase-code-tabs .nav,#showcase-code-tabs ul{margin-bottom:0}#showcase-code-tabs .tab-content{border-style:solid;border-color:#e5e5e5;border-width:0px 1px 1px 1px;overflow:auto;border-bottom-right-radius:4px;border-bottom-left-radius:4px}#showcase-app-code{width:100%}#showcase-code-position-toggle{float:right}#showcase-sxs-code{padding-top:20px;vertical-align:top}.showcase-code-license{display:block;text-align:right}#showcase-code-content pre{background-color:#fff}

File diff suppressed because one or more lines are too long

View File

@@ -1,3 +1,3 @@
/*! shiny 1.8.1.9000 | (c) 2012-2024 RStudio, PBC. | License: GPL-3 | file LICENSE */
/*! shiny 1.9.1.9000 | (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)});})();
//# sourceMappingURL=shiny-testmode.js.map

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -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);
}
}
}

View File

@@ -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;
}

136
man/busyIndicatorOptions.Rd Normal file
View File

@@ -0,0 +1,136 @@
% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/busy-indicators.R
\name{busyIndicatorOptions}
\alias{busyIndicatorOptions}
\title{Customize busy indicator options}
\usage{
busyIndicatorOptions(
...,
spinner_type = NULL,
spinner_color = NULL,
spinner_size = NULL,
spinner_delay = NULL,
spinner_selector = NULL,
fade_opacity = NULL,
fade_selector = NULL,
pulse_background = NULL,
pulse_height = NULL,
pulse_speed = NULL
)
}
\arguments{
\item{...}{Currently ignored.}
\item{spinner_type}{The type of spinner. Pre-bundled types include:
'ring', 'ring2', 'ring3', 'bars', 'bars2', 'bars3', 'pulse', 'pulse2', 'pulse3', 'dots', 'dots2', 'dots3'.
A path to a local SVG file can also be provided. The SVG should adhere to
the following rules:
\itemize{
\item The SVG itself should contain the animation.
\item It should avoid absolute sizes (the spinner's containing DOM element
size is set in CSS by \code{spinner_size}, so it should fill that container).
\item It should avoid setting absolute colors (the spinner's containing DOM element
color is set in CSS by \code{spinner_color}, so it should inherit that color).
}}
\item{spinner_color}{The color of the spinner. This can be any valid CSS
color. Defaults to the app's "primary" color if Bootstrap is on the page.}
\item{spinner_size}{The size of the spinner. This can be any valid CSS size.}
\item{spinner_delay}{The amount of time to wait before showing the spinner.
This can be any valid CSS time and can be useful for not showing the spinner
if the computation finishes quickly.}
\item{spinner_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{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}
of the theme's indigo, purple, and pink colors.}
\item{pulse_height}{The height of the pulsing banner. This can be any valid
CSS size.}
\item{pulse_speed}{The speed of the pulsing banner. This can be any valid CSS
time.}
}
\description{
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}
library(bslib)
card_ui <- function(id, spinner_type = id) {
card(
busyIndicatorOptions(spinner_type = spinner_type),
card_header(paste("Spinner:", spinner_type)),
plotOutput(shiny::NS(id, "plot"))
)
}
card_server <- function(id, simulate = reactive()) {
moduleServer(
id = id,
function(input, output, session) {
output$plot <- renderPlot({
Sys.sleep(1)
simulate()
plot(x = rnorm(100), y = rnorm(100))
})
}
)
}
ui <- page_fillable(
useBusyIndicators(),
input_task_button("simulate", "Simulate", icon = icon("refresh")),
layout_columns(
card_ui("ring"),
card_ui("bars"),
card_ui("dots"),
card_ui("pulse"),
col_widths = 6
)
)
server <- function(input, output, session) {
simulate <- reactive(input$simulate)
card_server("ring", simulate)
card_server("bars", simulate)
card_server("dots", simulate)
card_server("pulse", simulate)
}
shinyApp(ui, server)
\dontshow{\}) # examplesIf}
}
\seealso{
\code{\link[=useBusyIndicators]{useBusyIndicators()}} to disable/enable busy indicators.
}

View File

@@ -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.}

View File

@@ -1,23 +0,0 @@
% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/busy-indicators.R
\name{pulseOptions}
\alias{pulseOptions}
\title{Customize the pulsing busy indicator.}
\usage{
pulseOptions(color = NULL, height = NULL, speed = NULL)
}
\arguments{
\item{color}{The color of the pulsing banner. This can be any valid CSS
color. Defaults to the app's "primary" color (if Bootstrap is on the page)
or light-blue if not.}
\item{height}{The height of the pulsing banner. This can be any valid CSS
size. Defaults to "3.5px".}
}
\description{
Include the result of this function in the app's UI to customize the pulsing
banner.
}
\seealso{
\code{\link[=useBusyIndicators]{useBusyIndicators()}} \code{\link[=spinnerOptions]{spinnerOptions()}}
}

View File

@@ -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

View File

@@ -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>}}
}}

View File

@@ -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

View File

@@ -1,82 +0,0 @@
% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/busy-indicators.R
\name{spinnerOptions}
\alias{spinnerOptions}
\title{Customize spinning busy indicators.}
\usage{
spinnerOptions(
type = NULL,
...,
color = NULL,
size = NULL,
easing = NULL,
speed = NULL,
delay = NULL,
css_selector = ":root"
)
}
\arguments{
\item{type}{The type of spinner to use. Builtin options include: tadpole,
disc, dots, dot-track, and bounce. A custom type may also provided, which
should be a valid value for the CSS
\href{https://developer.mozilla.org/en-US/docs/Web/CSS/mask-image}{mask-image}
property.}
\item{color}{The color of the spinner. This can be any valid CSS color.
Defaults to the app's "primary" color (if Bootstrap is on the page) or
light-blue if not.}
\item{size}{The size of the spinner. This can be any valid CSS size.}
\item{easing}{The easing function to use for the spinner animation. This can
be any valid CSS \href{https://developer.mozilla.org/en-US/docs/Web/CSS/easing-function}{easing function}.}
\item{speed}{The amount of time for the spinner to complete a single
revolution. This can be any valid CSS time.}
\item{delay}{The amount of time to wait before showing the spinner. This can
be any valid CSS time and can useful for not showing the spinner
if the computation finishes quickly.}
\item{css_selector}{A CSS selector for scoping the spinner customization.
Defaults to the root element.}
}
\description{
Include the result of this function in the app's UI to customize spinner
appearance.
}
\details{
To effectively disable spinners, set the \code{size} to "0px".
}
\examples{
\dontshow{if (interactive()) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf}
library(bslib)
ui <- page_fillable(
useBusyIndicators(),
spinnerOptions(color = "orange"),
card(
card_header(
"A plot",
input_task_button("simulate", "Simulate"),
class = "d-flex justify-content-between align-items-center"
),
plotOutput("p"),
)
)
server <- function(input, output) {
output$p <- renderPlot({
input$simulate
Sys.sleep(4)
plot(x = rnorm(100), y = rnorm(100))
})
}
shinyApp(ui, server)
\dontshow{\}) # examplesIf}
}
\seealso{
\code{\link[=useBusyIndicators]{useBusyIndicators()}} \code{\link[=pulseOptions]{pulseOptions()}}
}

View File

@@ -2,26 +2,40 @@
% Please edit documentation in R/busy-indicators.R
\name{useBusyIndicators}
\alias{useBusyIndicators}
\title{Use and customize busy indicator types.}
\title{Enable/disable busy indication}
\usage{
useBusyIndicators(spinners = TRUE, pulse = TRUE)
useBusyIndicators(..., spinners = TRUE, pulse = TRUE, fade = TRUE)
}
\arguments{
\item{spinners}{Overlay a spinner on each calculating/recalculating output.}
\item{...}{Currently ignored.}
\item{pulse}{Show a pulsing banner at the top of the window when the server is busy.}
\item{spinners}{Whether to show a spinner on each calculating/recalculating
output.}
\item{pulse}{Whether to show a pulsing banner at the top of the page when the
app is busy.}
\item{fade}{Whether to fade recalculating outputs. A value of \code{FALSE} is
equivalent to \code{busyIndicatorOptions(fade_opacity=1)}.}
}
\description{
To enable busy indicators, include the result of this function in the app's UI.
Busy indicators provide a visual cue to users when the server is busy
calculating outputs or otherwise performing tasks (e.g., producing
downloads). 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. Busy indication is enabled by
default for UI created with \pkg{bslib}, but must be enabled otherwise. To
enable/disable, include the result of this function in anywhere in the app's
UI.
}
\details{
When both \code{spinners} and \code{pulse} are set to \code{TRUE}, the pulse is disabled
when spinner(s) are active. When both \code{spinners} and \code{pulse} are set to
\code{FALSE}, no busy indication is shown (other than the gray-ing out of
recalculating outputs).
When both \code{spinners} and \code{pulse} are set to \code{TRUE}, the pulse is
automatically disabled when spinner(s) are active. When both \code{spinners} and
\code{pulse} are set to \code{FALSE}, no busy indication is shown (other than the
graying out of recalculating outputs).
}
\examples{
\dontshow{if (interactive()) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf}
\dontshow{if (rlang::is_interactive()) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf}
library(bslib)
@@ -49,5 +63,6 @@ shinyApp(ui, server)
\dontshow{\}) # examplesIf}
}
\seealso{
\code{\link[=spinnerOptions]{spinnerOptions()}} \code{\link[=pulseOptions]{pulseOptions()}}
\code{\link[=busyIndicatorOptions]{busyIndicatorOptions()}} for customizing the appearance of the busy
indicators.
}

View File

@@ -3,7 +3,7 @@
"homepage": "https://shiny.rstudio.com",
"repository": "github:rstudio/shiny",
"name": "@types/rstudio-shiny",
"version": "1.8.1-alpha.9000",
"version": "1.9.1-alpha.9000",
"license": "GPL-3.0-only",
"main": "",
"browser": "",
@@ -53,7 +53,7 @@
"esbuild": "^0.15.10",
"esbuild-plugin-babel": "https://github.com/schloerke/esbuild-plugin-babel#patch-2",
"esbuild-plugin-globals": "^0.1.1",
"esbuild-sass-plugin": "^2.9.0",
"esbuild-plugin-sass": "^1.0.1",
"eslint": "^8.24.0",
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-jest": "^27.0.4",
@@ -69,6 +69,7 @@
"phantomjs-prebuilt": "^2.1.16",
"postcss": "^8.3.5",
"prettier": "^2.7.1",
"prettier-plugin-organize-imports": "^3.2.4",
"readcontrol": "^1.0.0",
"replace": "^1.2.1",
"ts-jest": "^26",
@@ -94,5 +95,11 @@
"coverage": "type-coverage -p tsconfig.json --at-least 90",
"circular": "madge --circular --extensions ts srcts/src",
"circular_image": "madge --circular --extensions ts --image madge.svg srcts/src"
},
"prettier": {
"plugins": [
"prettier-plugin-organize-imports"
],
"organizeImportsSkipDestructiveCodeActions": true
}
}

View File

@@ -1,25 +1,43 @@
# Revdeps
## Failed to check (18)
## Failed to check (29)
|package |version |error |warning |note |
|:------------------|:-------|:-----|:-------|:----|
|bigPint |? | | | |
|bioCancer |? | | | |
|ctsem |3.9.1 |1 | | |
|diveR |? | | | |
|EBImage |? | | | |
|g3viz |? | | | |
|GeneNetworkBuilder |? | | | |
|grandR |? | | | |
|InterCellar |? | | | |
|LACE |? | | | |
|loon.shiny |? | | | |
|MatrixQCvis |? | | | |
|modchart |? | | | |
|multilevelcoda |1.2.3 |1 | | |
|omicsViewer |? | | | |
|RQuantLib |0.4.21 |1 | | |
|rstanarm |2.32.1 |1 | | |
|Seurat |? | | | |
|package |version |error |warning |note |
|:---------------|:-------|:-----|:-------|:----|
|alevinQC |? | | | |
|animalEKF |1.2 |1 | | |
|animaltracker |? | | | |
|antaresViz |? | | | |
|AovBay |0.1.0 |1 | | |
|bdclean |? | | | |
|chipPCR |1.0-2 |1 | | |
|ctsem |3.10.0 |1 | | |
|diveR |? | | | |
|DynNom |5.1 |1 | | |
|hydflood |? | | | |
|loon.shiny |? | | | |
|modchart |? | | | |
|MODIStsp |? | | | |
|protGear |? | | | |
|robmedExtra |0.1.0 |1 | | |
|RQuantLib |0.4.23 |1 | | |
|rstanarm |2.32.1 |1 | | |
|scPipe |? | | | |
|sen2r |? | | | |
|SensMap |0.7 |1 | | |
|Seurat |5.1.0 |1 | | |
|shinyTempSignal |0.0.8 |1 | | |
|Signac |1.13.0 |1 | | |
|simplevis |? | | | |
|statsr |0.3.0 |1 | | |
|teachingApps |? | | | |
|tidyvpc |1.5.1 |1 | | |
|TVTB |? | | | |
## New problems (2)
|package |version |error |warning |note |
|:-------|:-------|:--------|:-------|:------|
|[EgoCor](problems.md#egocor)|1.2.0 |1 __+1__ | |-1 |
|[mxfda](problems.md#mxfda)|0.2.1 | | |__+1__ |

View File

@@ -1,19 +1,36 @@
## revdepcheck results
We checked 1201 reverse dependencies (1191 from CRAN + 10 from Bioconductor), comparing R CMD check results across CRAN and dev versions of this package.
We checked 1221 reverse dependencies (1208 from CRAN + 13 from Bioconductor), comparing R CMD check results across CRAN and dev versions of this package.
* We saw 0 new problems
* We failed to check 8 packages
* We saw 2 new problems
* We failed to check 16 packages
Issues with CRAN packages are summarised below.
### New problems
(This reports the first line of each new failure)
* EgoCor
checking running R code from vignettes ... ERROR
* mxfda
checking installed package size ... NOTE
### Failed to check
* ctsem (NA)
* diveR (NA)
* grandR (NA)
* loon.shiny (NA)
* multilevelcoda (NA)
* RQuantLib (NA)
* rstanarm (NA)
* Seurat (NA)
* animalEKF (NA)
* AovBay (NA)
* chipPCR (NA)
* ctsem (NA)
* diveR (NA)
* DynNom (NA)
* loon.shiny (NA)
* robmedExtra (NA)
* RQuantLib (NA)
* rstanarm (NA)
* SensMap (NA)
* Seurat (NA)
* shinyTempSignal (NA)
* Signac (NA)
* statsr (NA)
* tidyvpc (NA)

View File

@@ -1 +1,93 @@
*Wow, no problems at all. :)*
# EgoCor
<details>
* Version: 1.2.0
* GitHub: https://github.com/julia-dyck/EgoCor
* Source code: https://github.com/cran/EgoCor
* Date/Publication: 2024-03-28 18:10:02 UTC
* Number of recursive dependencies: 74
Run `revdepcheck::cloud_details(, "EgoCor")` for more info
</details>
## Newly broken
* checking running R code from vignettes ... ERROR
```
Errors in running code in vignettes:
when running code in Intro_to_EgoCor.Rmd
...
Warning in gstat::fit.variogram(emp.sv, model = v, fit.sills = TRUE, fit.ranges = TRUE, :
linear model has singular covariance matrix
Warning in gstat::fit.variogram(emp.sv, model = v, fit.sills = TRUE, fit.ranges = TRUE, :
linear model has singular covariance matrix
Warning in gstat::fit.variogram(emp.sv, model = v, fit.sills = TRUE, fit.ranges = TRUE, :
No convergence after 200 iterations: try different initial values?
When sourcing Intro_to_EgoCor.R:
Error: missing value where TRUE/FALSE needed
Execution halted
Intro_to_EgoCor.Rmd using UTF-8... failed
```
## Newly fixed
* checking re-building of vignette outputs ... NOTE
```
Error(s) in re-building vignettes:
--- re-building Intro_to_EgoCor.Rmd using rmarkdown
```
## In both
* checking examples ... ERROR
```
Running examples in EgoCor-Ex.R failed
The error most likely occurred in:
> ### Name: vario.reg.prep
> ### Title: Adjustment for covariates before semi-variogram model fitting
> ### Aliases: vario.reg.prep
>
> ### ** Examples
>
> ## Example 1
...
+ }
Warning in check_dep_version() :
ABI version mismatch:
lme4 was built with Matrix ABI version 1
Current Matrix ABI version is 0
Please re-install lme4 from source or restore original Matrix package
Error in initializePtr() :
function 'cholmod_factor_ldetA' not provided by package 'Matrix'
Calls: <Anonymous> ... initialize -> <Anonymous> -> initializePtr -> .Call
Execution halted
```
# mxfda
<details>
* Version: 0.2.1
* GitHub: https://github.com/julia-wrobel/mxfda
* Source code: https://github.com/cran/mxfda
* Date/Publication: 2024-05-08 11:00:02 UTC
* Number of recursive dependencies: 220
Run `revdepcheck::cloud_details(, "mxfda")` for more info
</details>
## Newly broken
* checking installed package size ... NOTE
```
installed size is 5.6Mb
sub-directories of 1Mb or more:
data 4.0Mb
```

View File

@@ -17,7 +17,6 @@ build({
"srcts/extras/shiny-autoreload.ts",
"srcts/extras/shiny-showcase.ts",
"srcts/extras/shiny-testmode.ts",
"srcts/extras/busy-indicators/busy-indicators.ts",
],
outdir: outDir,
});
@@ -25,7 +24,7 @@ build({
// - Sass -----------------------------------------------------------
import autoprefixer from "autoprefixer";
import sassPlugin from "esbuild-sass-plugin";
import sassPlugin from "esbuild-plugin-sass";
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore; Type definitions are not found. This occurs when `strict: true` in tsconfig.json
import postCssPlugin from "@deanc/esbuild-plugin-postcss";
@@ -58,8 +57,7 @@ build({
...sassOpts,
entryPoints: ["srcts/extras/busy-indicators/busy-indicators.scss"],
outfile: outDir + "busy-indicators/busy-indicators.css",
plugins: [sassPlugin({ type: "css", sourceMap: false })],
loader: { ".svg": "dataurl" },
bundle: true,
plugins: [sassPlugin()],
bundle: false,
metafile: true,
});

View File

@@ -1,3 +0,0 @@
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<ellipse class="spinner_rXNP" cx="12" cy="5" rx="4" ry="4" />
</svg>

Before

Width:  |  Height:  |  Size: 132 B

View File

@@ -1,78 +1,55 @@
/*
"Built-in" spinner types
It's important to list out all the types/svgs here so that esbuild can bundle them
with the CSS (see js/build.ts).
*/
:root {
--_shiny-spinner-type-tadpole: url(tadpole-spinner.svg);
--_shiny-spinner-type-disc: url(disc-spinner.svg);
--_shiny-spinner-type-dots: url(dots-spinner.svg);
--_shiny-spinner-type-dot-track: url(dot-track-spinner.svg);
--_shiny-spinner-type-bounce: url(ball.svg);
:where([data-shiny-busy-spinners] .recalculating) {
position: relative;
}
/* This data atttribute is set by ui.busy_indicators.use() */
[data-shiny-busy-spinners] {
/* This class gets set by busy_indicators.ts */
.recalculating {
position: relative;
overflow: visible; /* overflow:hidden can, in some cases, clip the spinner */
&::after {
position: absolute;
content: "";
/* ui.busy_indicators.spinner_options() */
--_shiny-spinner-mask-img: var(--shiny-spinner-mask-img, var(--_shiny-spinner-type-tadpole));
--_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-easing: var(--shiny-spinner-easing, ease-in-out);
--_shiny-spinner-speed: var(--shiny-spinner-speed, 2s);
--_shiny-spinner-delay: var(--shiny-spinner-delay, 0.5s);
--_shiny-spinner-animation: var(--shiny-spinner-animation, shiny-busy-spinner-spin);
--_shiny-spinner-delay: var(--shiny-spinner-delay, 1s);
/* Apply the spinner type as a mask */
mask-image: var(--_shiny-spinner-mask-img);
mask-position: center;
mask-size: contain;
mask-repeat: no-repeat;
-webkit-mask-image: var(--_shiny-spinner-mask-img);
-webkit-mask-position: center;
-webkit-mask-size: contain;
-webkit-mask-repeat: no-repeat;
/* Color, sizing, & positioning */
background: var(--_shiny-spinner-color);
width: var(--_shiny-spinner-size);
height: var(--_shiny-spinner-size);
inset: calc(50% - var(--_shiny-spinner-size) / 2);
/* Animation */
animation-name: var(--_shiny-spinner-animation);
animation-duration: var(--_shiny-spinner-speed);
animation-iteration-count: infinite;
animation-timing-function: var(--_shiny-spinner-easing);
animation-delay: var(--_shiny-spinner-delay);
mask-image: var(--_shiny-spinner-url);
-webkit-mask-image: var(--_shiny-spinner-url);
content: "";
scale: 0;
opacity: 0;
animation-delay: var(--_shiny-spinner-delay);
animation-name: fade-in;
animation-duration: 250ms;
animation-fill-mode: forwards;
}
/*
shiny.css puts `opacity: 0.3` on .recalculating, which unfortunately applies to
the spinner. Undo that, but still apply (smaller) opacity to immediate children
that aren't shiny-output-busy.
that aren't recalculating.
*/
opacity: 1;
&:has(> *), &:empty {
opacity: 1;
}
> *:not(.recalculating) {
opacity: 0.2;
opacity: var(--_shiny-fade-opacity);
transition: opacity 250ms ease var(--shiny-spinner-delay, 1s);
}
/*
Disable spinner on uiOutput() mainly because (for other reasons) it has
`display:contents`, which breaks the ::after positioning.
Note that, even if we could position it, we'd probably want to disable it
if it has shiny-output-busy children.
if it has recalculating children.
*/
&.shiny-html-output::after {
display: none;
@@ -87,15 +64,15 @@
--shiny-pulse-background,
linear-gradient(
120deg,
var(--bs-body-bg, #fff),
transparent,
var(--bs-indigo, #4b00c1),
var(--bs-purple, #74149c),
var(--bs-pink, #bf007f),
var(--bs-body-bg, #fff)
transparent
)
);
--_shiny-pulse-height: var(--shiny-pulse-height, 5px);
--_shiny-pulse-speed: var(--shiny-pulse-speed, 1.85s);
--_shiny-pulse-height: var(--shiny-pulse-height, 3px);
--_shiny-pulse-speed: var(--shiny-pulse-speed, 1.2s);
/* Color, sizing, & positioning */
position: fixed;
@@ -103,12 +80,12 @@
left: 0;
height: var(--_shiny-pulse-height);
background: var(--_shiny-pulse-background);
border-radius: 50%;
z-index: 9999;
/* Animation */
animation-name: busy-page-pulse;
animation-duration: var(--_shiny-pulse-speed);
animation-direction: alternate;
animation-iteration-count: infinite;
animation-timing-function: ease-in-out;
@@ -117,14 +94,19 @@
/*
In spinners+pulse mode (the recommended default), show a page-level banner if the
page is busy, but there are no shiny-output-busy elements.
page is busy, but there are no recalculating elements.
*/
[data-shiny-busy-spinners][data-shiny-busy-pulse] {
&.shiny-busy:not(:has(.recalculating))::after {
&.shiny-busy::after {
@include shiny-page-busy;
}
&.shiny-not-yet-idle:not(:has(.recalculating))::after {
@include shiny-page-busy;
// Hide the pulse if there are spinners on the page
// (Note: UI outputs don't get spinners)
&.shiny-busy:has(.recalculating:not(.shiny-html-output))::after {
display: none;
}
&.shiny-busy:has(#shiny-disconnected-overlay)::after {
display: none;
}
}
@@ -133,61 +115,49 @@
&.shiny-busy::after {
@include shiny-page-busy;
}
&.shiny-not-yet-idle::after {
@include shiny-page-busy;
&.shiny-busy:has(#shiny-disconnected-overlay)::after {
display: none;
}
}
/* Keyframes behind most spinner types */
@keyframes shiny-busy-spinner-spin {
/* Keyframes for the fading spinner */
@keyframes fade-in {
0% {
scale: 1;
rotate: 0deg;
opacity: 0;
}
100% {
scale: 1;
rotate: 360deg;
}
}
/* For busy_indicators.spinner_options(type="bounce") */
@keyframes shiny-busy-spinner-bounce {
0% {
animation-timing-function: cubic-bezier(0.33, 0, 0.66, 0.33);
translate: 0 calc(var(--_shiny-spinner-size) * (5 / 24));
scale: 1 1;
}
46.875% {
translate: 0 calc(var(--_shiny-spinner-size) * (20 / 24));
scale: 1 1;
}
50% {
animation-timing-function: cubic-bezier(0.33, 0.66, 0.66, 1);
translate: 0 calc(var(--_shiny-spinner-size) * (20.5 / 24));
scale: 1.2 0.85;
}
53.125% {
scale: 1 1;
}
100% {
translate: 0 calc(var(--_shiny-spinner-size) * (5 / 24));
scale: 1 1;
opacity: 1;
}
}
/* Keyframes for the pulsing banner */
@keyframes busy-page-pulse {
0% {
left: -75%;
width: 75%;
left: -14%;
right: 97%;
}
50% {
left: 100%;
width: 75%;
45% {
left: 0%;
right: 14%;
}
/* Go back */
100% {
left: -75%;
width: 75%;
55% {
left: 14%;
right: 0%;
}
to {
left: 97%;
right: -14%;
}
}
// Effectively disable the spinner when it's wrapped in shinycssloader::withSpinner()
// since that's a sign our spinner isn't needed.
// The reason this sets size to 0px instead of display:none is so, if someone
// really wants to show the spinner, they can override this with a custom size.
.shiny-spinner-output-container {
--shiny-spinner-size: 0px;
}

View File

@@ -1,7 +0,0 @@
// This of this like the .shiny-busy class that shiny.js puts on the root element,
// except it's added before shiny.js is initialized, connected, etc.
// TODO: maybe shiny.js should be doing something like this?
document.documentElement.classList.add("shiny-not-yet-idle");
$(document).one("shiny:idle", function () {
document.documentElement.classList.remove("shiny-not-yet-idle");
});

View File

@@ -1,5 +0,0 @@
<svg viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd"
d="M9 16.7833C13.2986 16.7833 17.3579 13.2986 17.3579 9C17.3579 4.70142 13.2986 1.21672 9 1.21672C4.70141 1.21672 1.21672 4.70142 1.21672 9C1.21672 13.2986 4.70141 16.7833 9 16.7833ZM9 17.8678C13.8976 17.8678 17.3579 13.8976 17.3579 9C17.3579 4.10245 13.8976 0.132202 9 0.132202C4.10245 0.132202 0.132198 4.10245 0.132198 9C0.132198 13.8976 4.10245 17.8678 9 17.8678Z"
fill="black" />
</svg>

Before

Width:  |  Height:  |  Size: 521 B

View File

@@ -1,5 +0,0 @@
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,19a8,8,0,1,1,8-8A8,8,0,0,1,12,20Z"
opacity=".25" />
<circle class="spinner_7WDj" cx="12" cy="2.5" r="1.5" />
</svg>

Before

Width:  |  Height:  |  Size: 238 B

View File

@@ -1,11 +0,0 @@
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<g class="spinner_Wezc">
<circle cx="12" cy="2.5" r="1.5" opacity=".14" />
<circle cx="16.75" cy="3.77" r="1.5" opacity=".29" />
<circle cx="20.23" cy="7.25" r="1.5" opacity=".43" />
<circle cx="21.50" cy="12.00" r="1.5" opacity=".57" />
<circle cx="20.23" cy="16.75" r="1.5" opacity=".71" />
<circle cx="16.75" cy="20.23" r="1.5" opacity=".86" />
<circle cx="12" cy="21.5" r="1.5" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 522 B

View File

@@ -1,5 +0,0 @@
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path class="spinner_0XTQ"
d="M12,23a9.63,9.63,0,0,1-8-9.5,9.51,9.51,0,0,1,6.79-9.1A1.66,1.66,0,0,0,12,2.81h0a1.67,1.67,0,0,0-1.94-1.64A11,11,0,0,0,12,23Z" />
</svg>

Before

Width:  |  Height:  |  Size: 234 B

View File

@@ -2,19 +2,12 @@
// Project: Shiny <https://shiny.rstudio.com/>
// Definitions by: RStudio <https://www.rstudio.com/>
import type { Shiny as RStudioShiny } from "../src/shiny/index";
import type { ShinyClass } from "../src/shiny/index";
declare global {
// Tell Shiny variable globally exists
// eslint-disable-next-line @typescript-eslint/naming-convention
const Shiny: RStudioShiny;
// Tell window.Shiny exists
interface Window {
// eslint-disable-next-line @typescript-eslint/naming-convention
Shiny: RStudioShiny;
Shiny: ShinyClass;
}
// Make `Shiny` a globally available type definition. (No need to import the type)
type Shiny = RStudioShiny;
}

View File

@@ -1,6 +1,6 @@
import $ from "jquery";
import { InputBinding } from "./inputBinding";
import { hasDefinedProperty } from "../../utils";
import { InputBinding } from "./inputBinding";
type CheckedHTMLElement = HTMLInputElement;

View File

@@ -1,8 +1,8 @@
import $ from "jquery";
import { InputBinding } from "./inputBinding";
import { $escape, updateLabel, hasDefinedProperty } from "../../utils";
import { $escape, hasDefinedProperty, updateLabel } from "../../utils";
import type { CheckedHTMLElement } from "./checkbox";
import { InputBinding } from "./inputBinding";
type CheckboxGroupHTMLElement = CheckedHTMLElement;
type ValueLabelObject = {

View File

@@ -1,13 +1,13 @@
import $ from "jquery";
import { InputBinding } from "./inputBinding";
import {
formatDateUTC,
updateLabel,
$escape,
parseDate,
formatDateUTC,
hasDefinedProperty,
parseDate,
updateLabel,
} from "../../utils";
import type { NotUndefined } from "../../utils/extraTypes";
import { InputBinding } from "./inputBinding";
declare global {
interface JQuery {
@@ -40,19 +40,15 @@ class DateInputBindingBase extends InputBinding {
el;
}
subscribe(el: HTMLElement, callback: (x: boolean) => void): void {
$(el).on(
"keyup.dateInputBinding input.dateInputBinding",
// event: Event
function () {
// Use normal debouncing policy when typing
callback(true);
}
);
// Don't update when in the middle of typing; listening on keyup or input
// tends to send spurious values to the server, based on unpredictable
// browser-dependant interpretation of partially-typed date strings.
$(el).on(
"changeDate.dateInputBinding change.dateInputBinding",
// event: Event
function () {
// Send immediately when clicked
// Or if typing, when enter pressed or focus lost
callback(false);
}
);

View File

@@ -161,19 +161,15 @@ class DateRangeInputBinding extends DateInputBindingBase {
this._setMax($endinput[0], $endinput.data("max-date"));
}
subscribe(el: HTMLElement, callback: (x: boolean) => void): void {
$(el).on(
"keyup.dateRangeInputBinding input.dateRangeInputBinding",
// event: Event
function () {
// Use normal debouncing policy when typing
callback(true);
}
);
// Don't update when in the middle of typing; listening on keyup or input
// tends to send spurious values to the server, based on unpredictable
// browser-dependant interpretation of partially-typed date strings.
$(el).on(
"changeDate.dateRangeInputBinding change.dateRangeInputBinding",
// event: Event
function () {
// Send immediately when clicked
// Or if typing, when enter pressed or focus lost
callback(false);
}
);

View File

@@ -1,7 +1,7 @@
import $ from "jquery";
import { InputBinding } from "./inputBinding";
import { FileUploader } from "../../file/fileProcessor";
import { shinyShinyApp } from "../../shiny/initedMethods";
import { InputBinding } from "./inputBinding";
const zoneActive = "shiny-file-input-active";
const zoneOver = "shiny-file-input-over";

View File

@@ -2,27 +2,26 @@ import { BindingRegistry } from "../registry";
import { InputBinding } from "./inputBinding";
import { ActionButtonInputBinding } from "./actionbutton";
import { CheckboxInputBinding } from "./checkbox";
import { CheckboxGroupInputBinding } from "./checkboxgroup";
import { DateInputBinding } from "./date";
import { DateRangeInputBinding } from "./daterange";
import { FileInputBinding } from "./fileinput";
import { NumberInputBinding } from "./number";
import { PasswordInputBinding } from "./password";
import { RadioInputBinding } from "./radio";
import { SelectInputBinding } from "./selectInput";
import { SliderInputBinding } from "./slider";
import { BootstrapTabInputBinding } from "./tabinput";
import { TextInputBinding } from "./text";
import { TextareaInputBinding } from "./textarea";
import { RadioInputBinding } from "./radio";
import { DateInputBinding } from "./date";
import { SliderInputBinding } from "./slider";
import { DateRangeInputBinding } from "./daterange";
import { SelectInputBinding } from "./selectInput";
import { ActionButtonInputBinding } from "./actionbutton";
import { BootstrapTabInputBinding } from "./tabinput";
import { FileInputBinding } from "./fileinput";
// TODO-barret make this an init method
type InitInputBindings = {
function initInputBindings(): {
inputBindings: BindingRegistry<InputBinding>;
fileInputBinding: FileInputBinding;
};
function initInputBindings(): InitInputBindings {
} {
const inputBindings = new BindingRegistry<InputBinding>();
inputBindings.register(new TextInputBinding(), "shiny.textInput");

View File

@@ -1,6 +1,6 @@
import $ from "jquery";
import { InputBinding } from "./inputBinding";
import { $escape, hasDefinedProperty, updateLabel } from "../../utils";
import { InputBinding } from "./inputBinding";
type RadioHTMLElement = HTMLInputElement;

View File

@@ -1,7 +1,7 @@
import $ from "jquery";
import { InputBinding } from "./inputBinding";
import { $escape, hasDefinedProperty, updateLabel } from "../../utils";
import { indirectEval } from "../../utils/eval";
import { InputBinding } from "./inputBinding";
type SelectHTMLElement = HTMLSelectElement & { nonempty: boolean };
@@ -154,7 +154,7 @@ class SelectInputBinding extends InputBinding {
selectize.settings.load = function (query: string, callback: CallbackFn) {
const settings = selectize.settings;
/* eslint-disable @typescript-eslint/no-floating-promises */
/* eslint-disable-next-line @typescript-eslint/no-floating-promises */
$.ajax({
url: data.url,
data: {

View File

@@ -5,10 +5,10 @@ import type {
import $ from "jquery";
// import { NameValueHTMLElement } from ".";
import {
formatDateUTC,
updateLabel,
$escape,
formatDateUTC,
hasDefinedProperty,
updateLabel,
} from "../../utils";
import type { TextHTMLElement } from "./text";

View File

@@ -1,6 +1,6 @@
import $ from "jquery";
import { InputBinding } from "./inputBinding";
import { hasDefinedProperty, isBS3 } from "../../utils";
import { InputBinding } from "./inputBinding";
type TabInputReceiveMessageData = { value?: string };

View File

@@ -1,5 +1,5 @@
import $ from "jquery";
import { $escape, updateLabel, hasDefinedProperty } from "../../utils";
import { $escape, hasDefinedProperty, updateLabel } from "../../utils";
import { InputBinding } from "./inputBinding";
@@ -122,5 +122,4 @@ class TextInputBinding extends TextInputBindingBase {
}
export { TextInputBinding, TextInputBindingBase };
export type { TextHTMLElement, TextReceiveMessageData };

View File

@@ -1,11 +1,11 @@
import $ from "jquery";
import { OutputBinding } from "./outputBinding";
import { shinyUnbindAll } from "../../shiny/initedMethods";
import type { ErrorsMessageValue } from "../../shiny/shinyapp";
import { debounce } from "../../time";
import { escapeHTML } from "../../utils";
import { indirectEval } from "../../utils/eval";
import type { ErrorsMessageValue } from "../../shiny/shinyapp";
import { OutputBinding } from "./outputBinding";
class DatatableOutputBinding extends OutputBinding {
find(scope: HTMLElement): JQuery<HTMLElement> {

View File

@@ -7,7 +7,10 @@ class DownloadLinkOutputBinding extends OutputBinding {
return $(scope).find("a.shiny-download-link");
}
renderValue(el: HTMLElement, data: string): void {
$(el).attr("href", data);
el.setAttribute("href", data);
el.classList.remove("disabled");
el.removeAttribute("aria-disabled");
el.removeAttribute("tabindex");
}
// Progress shouldn't be shown on the download button
// (progress will be shown as a page level pulse instead)

View File

@@ -1,9 +1,9 @@
import $ from "jquery";
import { OutputBinding } from "./outputBinding";
import { shinyUnbindAll } from "../../shiny/initedMethods";
import { renderContentAsync } from "../../shiny/render";
import type { ErrorsMessageValue } from "../../shiny/shinyapp";
import { OutputBinding } from "./outputBinding";
class HtmlOutputBinding extends OutputBinding {
find(scope: HTMLElement): JQuery<HTMLElement> {

View File

@@ -1,5 +1,4 @@
import $ from "jquery";
import { OutputBinding } from "./outputBinding";
import {
createBrushHandler,
createClickHandler,
@@ -8,16 +7,17 @@ import {
disableDrag,
initCoordmap,
} from "../../imageutils";
import type { CoordmapInit } from "../../imageutils/initCoordmap";
import type { ErrorsMessageValue } from "../../shiny/shinyapp";
import {
strToBool,
getComputedLinkColor,
getStyle,
hasOwnProperty,
strToBool,
} from "../../utils";
import { isIE, IEVersion } from "../../utils/browser";
import type { CoordmapInit } from "../../imageutils/initCoordmap";
import type { ErrorsMessageValue } from "../../shiny/shinyapp";
import { IEVersion, isIE } from "../../utils/browser";
import { ifUndefined } from "../../utils/object";
import { OutputBinding } from "./outputBinding";
class ImageOutputBinding extends OutputBinding {
find(scope: HTMLElement): JQuery<HTMLElement> {

View File

@@ -1,9 +1,9 @@
import { TextOutputBinding } from "./text";
import { BindingRegistry } from "../registry";
import { DownloadLinkOutputBinding } from "./downloadlink";
import { DatatableOutputBinding } from "./datatable";
import { DownloadLinkOutputBinding } from "./downloadlink";
import { HtmlOutputBinding } from "./html";
import { imageOutputBinding } from "./image";
import { TextOutputBinding } from "./text";
import { OutputBinding } from "./outputBinding";

View File

@@ -1,6 +1,6 @@
import $ from "jquery";
import { asArray } from "../../utils";
import type { ErrorsMessageValue } from "../../shiny/shinyapp";
import { asArray } from "../../utils";
class OutputBinding {
name!: string;

View File

@@ -1,6 +1,7 @@
/* eslint-disable @typescript-eslint/naming-convention */
import { LitElement, html, css } from "lit";
import { css, html, LitElement } from "lit";
import { Shiny } from "..";
import { ShinyClientError } from "../shiny/error";
const buttonStyles = css`

View File

@@ -1,8 +1,8 @@
import $ from "jquery";
import { triggerFileInputChanged } from "../events/inputChanged";
import { $escape } from "../utils";
import type { ShinyApp } from "../shiny/shinyapp";
import { getFileInputBinding } from "../shiny/initedMethods";
import type { ShinyApp } from "../shiny/shinyapp";
import { $escape } from "../utils";
type JobId = string;
type UploadUrl = string;
@@ -180,7 +180,7 @@ class FileUploader extends FileProcessor {
onFile(file: File, cont: () => void): void {
this.onProgress(file, 0);
/* eslint-disable @typescript-eslint/no-floating-promises */
/* eslint-disable-next-line @typescript-eslint/no-floating-promises */
$.ajax(this.uploadUrl, {
type: "POST",
cache: false,

View File

@@ -1,7 +1,7 @@
import $ from "jquery";
import { equal, isnan, mapValues, roundSignif } from "../utils";
import type { Coordmap } from "./initCoordmap";
import { findOrigin } from "./initCoordmap";
import { equal, isnan, mapValues, roundSignif } from "../utils";
import type { Panel } from "./initPanelScales";
import type { Offset } from "./findbox";
@@ -656,5 +656,4 @@ function createBrush(
}
export { createBrush };
export type { Bounds, BrushOpts, BoundsCss };

View File

@@ -1,13 +1,13 @@
import $ from "jquery";
import { imageOutputBinding } from "../bindings/output/image";
import type { InputRatePolicy } from "../inputPolicies";
import { shinySetInputValue } from "../shiny/initedMethods";
import { Debouncer, Throttler } from "../time";
import type { Bounds, BoundsCss, BrushOpts } from "./createBrush";
import { createBrush } from "./createBrush";
import type { BoundsCss, Bounds, BrushOpts } from "./createBrush";
import type { Offset } from "./findbox";
import type { Coordmap } from "./initCoordmap";
import type { Panel } from "./initPanelScales";
import type { InputRatePolicy } from "../inputPolicies";
// ----------------------------------------------------------
// Handler creators for click, hover, brush.

View File

@@ -1,9 +1,9 @@
import { createBrush } from "./createBrush";
import { createClickInfo } from "./createClickInfo";
import {
createBrushHandler,
createClickHandler,
createHoverHandler,
createBrushHandler,
} from "./createHandlers";
import { disableDrag } from "./disableDrag";
import { findBox } from "./findbox";

View File

@@ -1,8 +1,8 @@
import $ from "jquery";
import { shinySetInputValue } from "../shiny/initedMethods";
import { mapValues } from "../utils";
import type { Offset } from "./findbox";
import type { Bounds } from "./createBrush";
import type { Offset } from "./findbox";
import type { Panel, PanelInit } from "./initPanelScales";
import { initPanelScales } from "./initPanelScales";
@@ -162,8 +162,8 @@ function initCoordmap(
const bounds = {
top: 0,
left: 0,
right: img.clientWidth - 1,
bottom: img.clientHeight - 1,
right: img.naturalWidth - 1,
bottom: img.naturalHeight - 1,
};
coordmap_.panels[0] = {
@@ -290,11 +290,10 @@ function initCoordmap(
const matches = []; // Panels that match
const dists = []; // Distance of offset to each matching panel
let b;
let i;
for (i = 0; i < coordmap.panels.length; i++) {
b = coordmap.panels[i].range;
const b = coordmap.panels[i].range;
if (
x <= b.right + expandImg.x &&
@@ -413,5 +412,5 @@ function initCoordmap(
return coordmap;
}
export { findOrigin, initCoordmap };
export type { Coordmap, CoordmapInit };
export { initCoordmap, findOrigin };

View File

@@ -1,8 +1,8 @@
// Map a value x from a domain to a range. If clip is true, clip it to the
import type { Offset } from "./findbox";
import { mapValues } from "../utils";
import type { Bounds } from "./createBrush";
import type { Offset } from "./findbox";
// range.
function mapLinear(

View File

@@ -1,3 +1,4 @@
import { init } from "./initialize";
export { Shiny, type ShinyClass } from "./initialize";
init();

View File

@@ -1,6 +1,6 @@
import $ from "jquery";
import { isIE, setIsQt, setIsIE, setIEVersion } from "../utils/browser";
import { isIE, setIEVersion, setIsIE, setIsQt } from "../utils/browser";
import { userAgent } from "../utils/userAgent";
function getIEVersion() {

View File

@@ -1,16 +1,21 @@
import { determineBrowserInfo } from "./browser";
import { disableFormSubmission } from "./disableForm";
import { trackHistory } from "./history";
import { determineBrowserInfo } from "./browser";
import { windowShiny } from "../window/libraries";
import { setShiny } from "../shiny";
import { ShinyClass } from "../shiny";
import { setUserAgent } from "../utils/userAgent";
import { windowUserAgent } from "../window/userAgent";
import { initReactlog } from "../shiny/reactlog";
// eslint-disable-next-line @typescript-eslint/naming-convention
let Shiny: ShinyClass;
function init(): void {
setShiny(windowShiny());
if (window.Shiny) {
throw new Error("Trying to create window.Shiny, but it already exists!");
}
Shiny = window.Shiny = new ShinyClass();
setUserAgent(windowUserAgent()); // before determineBrowserInfo()
determineBrowserInfo();
@@ -21,4 +26,4 @@ function init(): void {
initReactlog();
}
export { init };
export { init, Shiny, type ShinyClass };

View File

@@ -1,12 +1,11 @@
import { InputBatchSender } from "./inputBatchSender";
import { InputNoResendDecorator } from "./inputNoResendDecorator";
import { InputEventDecorator } from "./inputEventDecorator";
import { InputRateDecorator } from "./inputRateDecorator";
import { InputDeferDecorator } from "./inputDeferDecorator";
import { InputEventDecorator } from "./inputEventDecorator";
import { InputNoResendDecorator } from "./inputNoResendDecorator";
import { InputRateDecorator } from "./inputRateDecorator";
import { InputValidateDecorator } from "./inputValidateDecorator";
import type { InputPolicy } from "./inputPolicy";
import type { EventPriority } from "./inputPolicy";
import type { EventPriority, InputPolicy } from "./inputPolicy";
import type { InputRatePolicy } from "./inputRatePolicy";
export {
@@ -17,5 +16,4 @@ export {
InputDeferDecorator,
InputValidateDecorator,
};
export type { InputPolicy, EventPriority, InputRatePolicy };

Some files were not shown because too many files have changed in this diff Show More