mirror of
https://github.com/rstudio/shiny.git
synced 2026-04-29 03:00:45 -04:00
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>
This commit is contained in:
10
NEWS.md
10
NEWS.md
@@ -1,13 +1,19 @@
|
|||||||
# shiny (development version)
|
# shiny (development version)
|
||||||
|
|
||||||
|
## New busy indication feature
|
||||||
|
|
||||||
|
Shiny now includes busy indication by default, which more specifically means:
|
||||||
|
1. Calculating/recalculating outputs now have a spinner overlay.
|
||||||
|
2. When no outputs are calculating, but Shiny is busy calculating something (e.g., a download, side-effect, etc), a page-level pulsing banner is shown.
|
||||||
|
|
||||||
|
If either 1 or 2 leads to undesirable behavior in your app, you can disable them entirely with `useBusyIndicators(spinners = FALSE, pulse = FALSE)`. 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
|
## 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)
|
* 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)
|
* 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)
|
||||||
|
|
||||||
* Added new functions, `useBusyIndicators()` and `busyIndicatorOptions()`, for enabling and customizing busy indication. Busy indicators provide a visual cue to users when the server is busy calculating outputs or otherwise serving requests to the client. When enabled, a spinner is shown on each calculating/recalculating output, and a pulsing banner is shown at the top of the page when the app is otherwise busy. (#4040)
|
|
||||||
|
|
||||||
* 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)
|
* 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)
|
* 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)
|
||||||
|
|||||||
@@ -19,6 +19,8 @@
|
|||||||
#' output.
|
#' output.
|
||||||
#' @param pulse Whether to show a pulsing banner at the top of the page when the
|
#' @param pulse Whether to show a pulsing banner at the top of the page when the
|
||||||
#' app is busy.
|
#' app is busy.
|
||||||
|
#' @param fade Whether to fade recalculating outputs. A value of `FALSE` is
|
||||||
|
#' equivalent to `busyIndicatorOptions(fade_opacity=1)`.
|
||||||
#'
|
#'
|
||||||
#' @export
|
#' @export
|
||||||
#' @seealso [busyIndicatorOptions()] for customizing the appearance of the busy
|
#' @seealso [busyIndicatorOptions()] for customizing the appearance of the busy
|
||||||
@@ -48,7 +50,7 @@
|
|||||||
#' }
|
#' }
|
||||||
#'
|
#'
|
||||||
#' shinyApp(ui, server)
|
#' shinyApp(ui, server)
|
||||||
useBusyIndicators <- function(..., spinners = TRUE, pulse = TRUE) {
|
useBusyIndicators <- function(..., spinners = TRUE, pulse = TRUE, fade = TRUE) {
|
||||||
|
|
||||||
rlang::check_dots_empty()
|
rlang::check_dots_empty()
|
||||||
|
|
||||||
@@ -62,20 +64,33 @@ useBusyIndicators <- function(..., spinners = TRUE, pulse = TRUE) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
js <- HTML(paste(js, collapse = "\n"))
|
|
||||||
|
|
||||||
# TODO: it'd be nice if htmltools had something like a page_attrs() that allowed us
|
# 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.
|
# to do this without needing to inject JS into the head.
|
||||||
tags$script(js)
|
res <- tags$script(HTML(paste(js, collapse = "\n")))
|
||||||
|
|
||||||
|
if (!fade) {
|
||||||
|
res <- tagList(res, fadeOptions(opacity = 1))
|
||||||
|
}
|
||||||
|
|
||||||
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
#' Customize busy indicator options
|
#' Customize busy indicator options
|
||||||
#'
|
#'
|
||||||
#' When busy indicators are enabled (see [useBusyIndicators()]), a spinner is
|
#' @description
|
||||||
#' shown on each calculating/recalculating output, and a pulsing banner is shown
|
#' Shiny automatically includes busy indicators, which more specifically means:
|
||||||
#' at the top of the page when the app is otherwise busy. This function allows
|
#' 1. Calculating/recalculating outputs have a spinner overlay.
|
||||||
#' you to customize the appearance of those busy indicators. To apply the
|
#' 2. Outputs fade out/in when recalculating.
|
||||||
#' customization, include the result of this function inside the app's UI.
|
#' 3. When no outputs are calculating/recalculating, but Shiny is busy
|
||||||
|
#' doing something else (e.g., a download, side-effect, etc), a page-level
|
||||||
|
#' pulsing banner is shown.
|
||||||
|
#'
|
||||||
|
#' This function allows you to customize the appearance of these busy indicators
|
||||||
|
#' by including the result of this function inside the app's UI. Note that,
|
||||||
|
#' unless `spinner_selector` (or `fade_selector`) is specified, the spinner/fade
|
||||||
|
#' customization applies to the parent element. If the customization should
|
||||||
|
#' instead apply to the entire page, set `spinner_selector = 'html'` and
|
||||||
|
#' `fade_selector = 'html'`.
|
||||||
#'
|
#'
|
||||||
#' @param ... Currently ignored.
|
#' @param ... Currently ignored.
|
||||||
#' @param spinner_type The type of spinner. Pre-bundled types include:
|
#' @param spinner_type The type of spinner. Pre-bundled types include:
|
||||||
@@ -97,6 +112,11 @@ useBusyIndicators <- function(..., spinners = TRUE, pulse = TRUE) {
|
|||||||
#' @param spinner_selector A character string containing a CSS selector for
|
#' @param spinner_selector A character string containing a CSS selector for
|
||||||
#' scoping the spinner customization. The default (`NULL`) will apply the
|
#' scoping the spinner customization. The default (`NULL`) will apply the
|
||||||
#' spinner customization to the parent element of the spinner.
|
#' 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
|
#' @param pulse_background A CSS background definition for the pulse. The
|
||||||
#' default uses a
|
#' default uses a
|
||||||
#' [linear-gradient](https://developer.mozilla.org/en-US/docs/Web/CSS/gradient/linear-gradient)
|
#' [linear-gradient](https://developer.mozilla.org/en-US/docs/Web/CSS/gradient/linear-gradient)
|
||||||
@@ -107,7 +127,7 @@ useBusyIndicators <- function(..., spinners = TRUE, pulse = TRUE) {
|
|||||||
#' time.
|
#' time.
|
||||||
#'
|
#'
|
||||||
#' @export
|
#' @export
|
||||||
#' @seealso [useBusyIndicators()] for enabling/disabling busy indicators.
|
#' @seealso [useBusyIndicators()] to disable/enable busy indicators.
|
||||||
#' @examplesIf rlang::is_interactive()
|
#' @examplesIf rlang::is_interactive()
|
||||||
#'
|
#'
|
||||||
#' library(bslib)
|
#' library(bslib)
|
||||||
@@ -162,6 +182,8 @@ busyIndicatorOptions <- function(
|
|||||||
spinner_size = NULL,
|
spinner_size = NULL,
|
||||||
spinner_delay = NULL,
|
spinner_delay = NULL,
|
||||||
spinner_selector = NULL,
|
spinner_selector = NULL,
|
||||||
|
fade_opacity = NULL,
|
||||||
|
fade_selector = NULL,
|
||||||
pulse_background = NULL,
|
pulse_background = NULL,
|
||||||
pulse_height = NULL,
|
pulse_height = NULL,
|
||||||
pulse_speed = NULL
|
pulse_speed = NULL
|
||||||
@@ -177,6 +199,7 @@ busyIndicatorOptions <- function(
|
|||||||
delay = spinner_delay,
|
delay = spinner_delay,
|
||||||
selector = spinner_selector
|
selector = spinner_selector
|
||||||
),
|
),
|
||||||
|
fadeOptions(opacity = fade_opacity, selector = fade_selector),
|
||||||
pulseOptions(
|
pulseOptions(
|
||||||
background = pulse_background,
|
background = pulse_background,
|
||||||
height = pulse_height,
|
height = pulse_height,
|
||||||
@@ -224,6 +247,26 @@ spinnerOptions <- function(type = NULL, color = NULL, size = NULL, delay = NULL,
|
|||||||
tags$style(css, id = id)
|
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) {
|
pulseOptions <- function(background = NULL, height = NULL, speed = NULL) {
|
||||||
if (is.null(background) && is.null(height) && is.null(speed)) {
|
if (is.null(background) && is.null(height) && is.null(speed)) {
|
||||||
return(NULL)
|
return(NULL)
|
||||||
@@ -244,6 +287,7 @@ busyIndicatorDependency <- function() {
|
|||||||
version = get_package_version("shiny"),
|
version = get_package_version("shiny"),
|
||||||
src = "www/shared/busy-indicators",
|
src = "www/shared/busy-indicators",
|
||||||
package = "shiny",
|
package = "shiny",
|
||||||
stylesheet = "busy-indicators.css"
|
stylesheet = "busy-indicators.css",
|
||||||
|
head = as.character(useBusyIndicators())
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
/*! shiny 1.8.1.9001 | (c) 2012-2024 RStudio, PBC. | License: GPL-3 | file LICENSE */
|
/*! shiny 1.8.1.9001 | (c) 2012-2024 RStudio, PBC. | License: GPL-3 | file LICENSE */
|
||||||
:where([data-shiny-busy-spinners] .recalculating){position:relative}[data-shiny-busy-spinners] .recalculating{opacity:1}[data-shiny-busy-spinners] .recalculating:after{position:absolute;content:"";--_shiny-spinner-url: var(--shiny-spinner-url, url(spinners/ring.svg));--_shiny-spinner-color: var(--shiny-spinner-color, var(--bs-primary, #007bc2));--_shiny-spinner-size: var(--shiny-spinner-size, 32px);--_shiny-spinner-delay: var(--shiny-spinner-delay, 1s);background:var(--_shiny-spinner-color);width:var(--_shiny-spinner-size);height:var(--_shiny-spinner-size);inset:calc(50% - var(--_shiny-spinner-size) / 2);mask-image:var(--_shiny-spinner-url);-webkit-mask-image:var(--_shiny-spinner-url);opacity:0;animation-delay:var(--_shiny-spinner-delay);animation-name:fade-in;animation-duration:.25s;animation-fill-mode:forwards}[data-shiny-busy-spinners] .recalculating>*:not(.recalculating){opacity:.2;transition:opacity .25s ease var(--shiny-spinner-delay, 1s)}[data-shiny-busy-spinners] .recalculating.shiny-html-output:after{display:none}[data-shiny-busy-spinners][data-shiny-busy-pulse].shiny-busy:after{--_shiny-pulse-background: var( --shiny-pulse-background, linear-gradient( 120deg, var(--bs-body-bg, #fff), var(--bs-indigo, #4b00c1), var(--bs-purple, #74149c), var(--bs-pink, #bf007f), var(--bs-body-bg, #fff) ) );--_shiny-pulse-height: var(--shiny-pulse-height, 5px);--_shiny-pulse-speed: var(--shiny-pulse-speed, 1.85s);position:fixed;top:0;left:0;height:var(--_shiny-pulse-height);background:var(--_shiny-pulse-background);z-index:9999;animation-name:busy-page-pulse;animation-duration:var(--_shiny-pulse-speed);animation-iteration-count:infinite;animation-timing-function:ease-in-out;content:""}[data-shiny-busy-spinners][data-shiny-busy-pulse].shiny-busy:has(.recalculating):after{display:none}[data-shiny-busy-spinners][data-shiny-busy-pulse].shiny-busy:has(#shiny-disconnected-overlay):after{display:none}[data-shiny-busy-pulse]:not([data-shiny-busy-spinners]).shiny-busy:after{--_shiny-pulse-background: var( --shiny-pulse-background, linear-gradient( 120deg, var(--bs-body-bg, #fff), var(--bs-indigo, #4b00c1), var(--bs-purple, #74149c), var(--bs-pink, #bf007f), var(--bs-body-bg, #fff) ) );--_shiny-pulse-height: var(--shiny-pulse-height, 5px);--_shiny-pulse-speed: var(--shiny-pulse-speed, 1.85s);position:fixed;top:0;left:0;height:var(--_shiny-pulse-height);background:var(--_shiny-pulse-background);z-index:9999;animation-name:busy-page-pulse;animation-duration:var(--_shiny-pulse-speed);animation-iteration-count:infinite;animation-timing-function:ease-in-out;content:""}[data-shiny-busy-pulse]:not([data-shiny-busy-spinners]).shiny-busy:has(#shiny-disconnected-overlay):after{display:none}@keyframes fade-in{0%{opacity:0}to{opacity:1}}@keyframes busy-page-pulse{0%{left:-75%;width:75%}50%{left:100%;width:75%}to{left:-75%;width:75%}}
|
:where([data-shiny-busy-spinners] .recalculating){position:relative}[data-shiny-busy-spinners] .recalculating:after{position:absolute;content:"";--_shiny-spinner-url: var(--shiny-spinner-url, url(spinners/ring.svg));--_shiny-spinner-color: var(--shiny-spinner-color, var(--bs-primary, #007bc2));--_shiny-spinner-size: var(--shiny-spinner-size, 32px);--_shiny-spinner-delay: var(--shiny-spinner-delay, 1s);background:var(--_shiny-spinner-color);width:var(--_shiny-spinner-size);height:var(--_shiny-spinner-size);inset:calc(50% - var(--_shiny-spinner-size) / 2);mask-image:var(--_shiny-spinner-url);-webkit-mask-image:var(--_shiny-spinner-url);opacity:0;animation-delay:var(--_shiny-spinner-delay);animation-name:fade-in;animation-duration:.25s;animation-fill-mode:forwards}[data-shiny-busy-spinners] .recalculating:has(>*),[data-shiny-busy-spinners] .recalculating:empty{opacity:1}[data-shiny-busy-spinners] .recalculating>*:not(.recalculating){opacity:var(--_shiny-fade-opacity);transition:opacity .25s ease var(--shiny-spinner-delay, 1s)}[data-shiny-busy-spinners] .recalculating.shiny-html-output:after{display:none}[data-shiny-busy-spinners][data-shiny-busy-pulse].shiny-busy:after{--_shiny-pulse-background: var( --shiny-pulse-background, linear-gradient( 120deg, var(--bs-body-bg, #fff), var(--bs-indigo, #4b00c1), var(--bs-purple, #74149c), var(--bs-pink, #bf007f), var(--bs-body-bg, #fff) ) );--_shiny-pulse-height: var(--shiny-pulse-height, 5px);--_shiny-pulse-speed: var(--shiny-pulse-speed, 1.85s);position:fixed;top:0;left:0;height:var(--_shiny-pulse-height);background:var(--_shiny-pulse-background);z-index:9999;animation-name:busy-page-pulse;animation-duration:var(--_shiny-pulse-speed);animation-iteration-count:infinite;animation-timing-function:ease-in-out;content:""}[data-shiny-busy-spinners][data-shiny-busy-pulse].shiny-busy:has(.recalculating):after{display:none}[data-shiny-busy-spinners][data-shiny-busy-pulse].shiny-busy:has(#shiny-disconnected-overlay):after{display:none}[data-shiny-busy-pulse]:not([data-shiny-busy-spinners]).shiny-busy:after{--_shiny-pulse-background: var( --shiny-pulse-background, linear-gradient( 120deg, var(--bs-body-bg, #fff), var(--bs-indigo, #4b00c1), var(--bs-purple, #74149c), var(--bs-pink, #bf007f), var(--bs-body-bg, #fff) ) );--_shiny-pulse-height: var(--shiny-pulse-height, 5px);--_shiny-pulse-speed: var(--shiny-pulse-speed, 1.85s);position:fixed;top:0;left:0;height:var(--_shiny-pulse-height);background:var(--_shiny-pulse-background);z-index:9999;animation-name:busy-page-pulse;animation-duration:var(--_shiny-pulse-speed);animation-iteration-count:infinite;animation-timing-function:ease-in-out;content:""}[data-shiny-busy-pulse]:not([data-shiny-busy-spinners]).shiny-busy:has(#shiny-disconnected-overlay):after{display:none}@keyframes fade-in{0%{opacity:0}to{opacity:1}}@keyframes busy-page-pulse{0%{left:-75%;width:75%}50%{left:100%;width:75%}to{left:-75%;width:75%}}.shiny-spinner-output-container{--shiny-spinner-size: 0px}
|
||||||
|
|||||||
2
inst/www/shared/shiny.min.css
vendored
2
inst/www/shared/shiny.min.css
vendored
File diff suppressed because one or more lines are too long
@@ -44,9 +44,9 @@ div:where(.shiny-html-output) {
|
|||||||
/* uiOutput()/ conditionalPanel() are "pass-through" containers when they have children. */
|
/* uiOutput()/ conditionalPanel() are "pass-through" containers when they have children. */
|
||||||
&:has(> *) {
|
&:has(> *) {
|
||||||
display: contents;
|
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 > * {
|
&.recalculating > * {
|
||||||
opacity: 0.3;
|
opacity: var(--_shiny-fade-opacity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -156,7 +156,8 @@ html.autoreload-enabled #shiny-disconnected-overlay.reloading {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.recalculating {
|
.recalculating {
|
||||||
opacity: 0.3;
|
--_shiny-fade-opacity: var(--shiny-fade-opacity, 0.3);
|
||||||
|
opacity: var(--_shiny-fade-opacity);
|
||||||
transition: opacity 250ms ease 500ms;
|
transition: opacity 250ms ease 500ms;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ busyIndicatorOptions(
|
|||||||
spinner_size = NULL,
|
spinner_size = NULL,
|
||||||
spinner_delay = NULL,
|
spinner_delay = NULL,
|
||||||
spinner_selector = NULL,
|
spinner_selector = NULL,
|
||||||
|
fade_opacity = NULL,
|
||||||
|
fade_selector = NULL,
|
||||||
pulse_background = NULL,
|
pulse_background = NULL,
|
||||||
pulse_height = NULL,
|
pulse_height = NULL,
|
||||||
pulse_speed = NULL
|
pulse_speed = NULL
|
||||||
@@ -45,6 +47,13 @@ if the computation finishes quickly.}
|
|||||||
scoping the spinner customization. The default (\code{NULL}) will apply the
|
scoping the spinner customization. The default (\code{NULL}) will apply the
|
||||||
spinner customization to the parent element of the spinner.}
|
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
|
\item{pulse_background}{A CSS background definition for the pulse. The
|
||||||
default uses a
|
default uses a
|
||||||
\href{https://developer.mozilla.org/en-US/docs/Web/CSS/gradient/linear-gradient}{linear-gradient}
|
\href{https://developer.mozilla.org/en-US/docs/Web/CSS/gradient/linear-gradient}{linear-gradient}
|
||||||
@@ -57,11 +66,21 @@ CSS size.}
|
|||||||
time.}
|
time.}
|
||||||
}
|
}
|
||||||
\description{
|
\description{
|
||||||
When busy indicators are enabled (see \code{\link[=useBusyIndicators]{useBusyIndicators()}}), a spinner is
|
Shiny automatically includes busy indicators, which more specifically means:
|
||||||
shown on each calculating/recalculating output, and a pulsing banner is shown
|
\enumerate{
|
||||||
at the top of the page when the app is otherwise busy. This function allows
|
\item Calculating/recalculating outputs have a spinner overlay.
|
||||||
you to customize the appearance of those busy indicators. To apply the
|
\item Outputs fade out/in when recalculating.
|
||||||
customization, include the result of this function inside the app's UI.
|
\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{
|
\examples{
|
||||||
\dontshow{if (rlang::is_interactive()) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf}
|
\dontshow{if (rlang::is_interactive()) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf}
|
||||||
@@ -113,5 +132,5 @@ shinyApp(ui, server)
|
|||||||
\dontshow{\}) # examplesIf}
|
\dontshow{\}) # examplesIf}
|
||||||
}
|
}
|
||||||
\seealso{
|
\seealso{
|
||||||
\code{\link[=useBusyIndicators]{useBusyIndicators()}} for enabling/disabling busy indicators.
|
\code{\link[=useBusyIndicators]{useBusyIndicators()}} to disable/enable busy indicators.
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
\alias{useBusyIndicators}
|
\alias{useBusyIndicators}
|
||||||
\title{Enable/disable busy indication}
|
\title{Enable/disable busy indication}
|
||||||
\usage{
|
\usage{
|
||||||
useBusyIndicators(..., spinners = TRUE, pulse = TRUE)
|
useBusyIndicators(..., spinners = TRUE, pulse = TRUE, fade = TRUE)
|
||||||
}
|
}
|
||||||
\arguments{
|
\arguments{
|
||||||
\item{...}{Currently ignored.}
|
\item{...}{Currently ignored.}
|
||||||
@@ -14,6 +14,9 @@ output.}
|
|||||||
|
|
||||||
\item{pulse}{Whether to show a pulsing banner at the top of the page when the
|
\item{pulse}{Whether to show a pulsing banner at the top of the page when the
|
||||||
app is busy.}
|
app is busy.}
|
||||||
|
|
||||||
|
\item{fade}{Whether to fade recalculating outputs. A value of \code{FALSE} is
|
||||||
|
equivalent to \code{busyIndicatorOptions(fade_opacity=1)}.}
|
||||||
}
|
}
|
||||||
\description{
|
\description{
|
||||||
Busy indicators provide a visual cue to users when the server is busy
|
Busy indicators provide a visual cue to users when the server is busy
|
||||||
|
|||||||
@@ -37,9 +37,11 @@
|
|||||||
the spinner. Undo that, but still apply (smaller) opacity to immediate children
|
the spinner. Undo that, but still apply (smaller) opacity to immediate children
|
||||||
that aren't recalculating.
|
that aren't recalculating.
|
||||||
*/
|
*/
|
||||||
opacity: 1;
|
&:has(> *), &:empty {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
> *:not(.recalculating) {
|
> *:not(.recalculating) {
|
||||||
opacity: 0.2;
|
opacity: var(--_shiny-fade-opacity);
|
||||||
transition: opacity 250ms ease var(--shiny-spinner-delay, 1s);
|
transition: opacity 250ms ease var(--shiny-spinner-delay, 1s);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -141,3 +143,12 @@
|
|||||||
width: 75%;
|
width: 75%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user