mirror of
https://github.com/rstudio/shiny.git
synced 2026-01-11 16:08:19 -05:00
Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
914baf594b | ||
|
|
02b0802886 | ||
|
|
0725239397 | ||
|
|
d72e8a06a7 | ||
|
|
cf79fec720 | ||
|
|
31dda45d1c | ||
|
|
9836b72661 | ||
|
|
5ec38581ca |
@@ -106,6 +106,7 @@ Collate:
|
||||
'cache-utils.R'
|
||||
'diagnose.R'
|
||||
'fileupload.R'
|
||||
'font-awesome.R'
|
||||
'graph.R'
|
||||
'reactives.R'
|
||||
'reactive-domains.R'
|
||||
|
||||
8
NEWS.md
8
NEWS.md
@@ -5,6 +5,10 @@ This release features plot caching, an important new tool for improving performa
|
||||
|
||||
## Full changelog
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* The URL paths for FontAwesome CSS/JS/font assets have changed, due to our upgrade from FontAwesome 4 to 5. This shouldn't affect you unless you're using `www/index.html` to provide your UI and have hardcoded the old FontAwesome paths into your HTML. If that's you, consider switching to [HTML templates](https://shiny.rstudio.com/articles/templates.html), which give you the syntax of raw HTML while still taking advantage of Shiny's automatic management of web dependencies.
|
||||
|
||||
### New features
|
||||
|
||||
* Added `renderCachedPlot()`, which stores plots in a cache so that they can be served up almost instantly. ([#1997](https://github.com/rstudio/shiny/pull/1997))
|
||||
@@ -49,6 +53,10 @@ This release features plot caching, an important new tool for improving performa
|
||||
|
||||
* Fixed [#2204](https://github.com/rstudio/shiny/issues/2204): `updateDateInput` could set the wrong date on days where DST begins. (Thanks @GaGaMan1101!) [#2212](https://github.com/rstudio/shiny/pull/2212)
|
||||
|
||||
* Fixed [#2225](https://github.com/rstudio/shiny/issues/2225): Input event queue can stall in apps that use async. [#2226](https://github.com/rstudio/shiny/pull/2226)
|
||||
|
||||
* Fixed [#2228](https://github.com/rstudio/shiny/issues/2228): `reactiveTimer` fails when not owned by a session. Thanks, @P-Bettega! [#2229](https://github.com/rstudio/shiny/pull/2229)
|
||||
|
||||
### Documentation Updates
|
||||
|
||||
* Addressed [#1864](https://github.com/rstudio/shiny/issues/1864) by changing `optgroup` documentation to use `list` instead of `c`. ([#2084](https://github.com/rstudio/shiny/pull/2084))
|
||||
|
||||
@@ -1508,10 +1508,6 @@ downloadLink <- function(outputId, label="Download", class=NULL, ...) {
|
||||
#'
|
||||
#'
|
||||
#' @examples
|
||||
#' icon("calendar") # standard icon
|
||||
#' icon("calendar", "fa-3x") # 3x normal size
|
||||
#' icon("cog", lib = "glyphicon") # From glyphicon library
|
||||
#'
|
||||
#' # add an icon to a submit button
|
||||
#' submitButton("Update View", icon = icon("refresh"))
|
||||
#'
|
||||
@@ -1537,8 +1533,13 @@ icon <- function(name, class = NULL, lib = "font-awesome") {
|
||||
# build the icon class (allow name to be null so that other functions
|
||||
# e.g. buildTabset can pass an explicit class value)
|
||||
iconClass <- ""
|
||||
if (!is.null(name))
|
||||
iconClass <- paste0(prefix, " ", prefix, "-", name)
|
||||
if (!is.null(name)) {
|
||||
prefix_class <- prefix
|
||||
if (prefix_class == "fa" && name %in% font_awesome_brands) {
|
||||
prefix_class <- "fab"
|
||||
}
|
||||
iconClass <- paste0(prefix_class, " ", prefix, "-", name)
|
||||
}
|
||||
if (!is.null(class))
|
||||
iconClass <- paste(iconClass, class)
|
||||
|
||||
|
||||
75
R/font-awesome.R
Normal file
75
R/font-awesome.R
Normal file
@@ -0,0 +1,75 @@
|
||||
font_awesome_brands <- c(
|
||||
"500px", "accessible-icon", "accusoft", "adn", "adversal",
|
||||
"affiliatetheme", "algolia", "alipay", "amazon", "amazon-pay",
|
||||
"amilia", "android", "angellist", "angrycreative", "angular",
|
||||
"app-store", "app-store-ios", "apper", "apple", "apple-pay",
|
||||
"asymmetrik", "audible", "autoprefixer", "avianex", "aviato",
|
||||
"aws", "bandcamp", "behance", "behance-square", "bimobject",
|
||||
"bitbucket", "bitcoin", "bity", "black-tie", "blackberry", "blogger",
|
||||
"blogger-b", "bluetooth", "bluetooth-b", "btc", "buromobelexperte",
|
||||
"buysellads", "cc-amazon-pay", "cc-amex", "cc-apple-pay", "cc-diners-club",
|
||||
"cc-discover", "cc-jcb", "cc-mastercard", "cc-paypal", "cc-stripe",
|
||||
"cc-visa", "centercode", "chrome", "cloudscale", "cloudsmith",
|
||||
"cloudversify", "codepen", "codiepie", "connectdevelop", "contao",
|
||||
"cpanel", "creative-commons", "creative-commons-by", "creative-commons-nc",
|
||||
"creative-commons-nc-eu", "creative-commons-nc-jp", "creative-commons-nd",
|
||||
"creative-commons-pd", "creative-commons-pd-alt", "creative-commons-remix",
|
||||
"creative-commons-sa", "creative-commons-sampling", "creative-commons-sampling-plus",
|
||||
"creative-commons-share", "css3", "css3-alt", "cuttlefish", "d-and-d",
|
||||
"dashcube", "delicious", "deploydog", "deskpro", "deviantart",
|
||||
"digg", "digital-ocean", "discord", "discourse", "dochub", "docker",
|
||||
"draft2digital", "dribbble", "dribbble-square", "dropbox", "drupal",
|
||||
"dyalog", "earlybirds", "ebay", "edge", "elementor", "ello",
|
||||
"ember", "empire", "envira", "erlang", "ethereum", "etsy", "expeditedssl",
|
||||
"facebook", "facebook-f", "facebook-messenger", "facebook-square",
|
||||
"firefox", "first-order", "first-order-alt", "firstdraft", "flickr",
|
||||
"flipboard", "fly", "font-awesome", "font-awesome-alt", "font-awesome-flag",
|
||||
"font-awesome-logo-full", "fonticons", "fonticons-fi", "fort-awesome",
|
||||
"fort-awesome-alt", "forumbee", "foursquare", "free-code-camp",
|
||||
"freebsd", "fulcrum", "galactic-republic", "galactic-senate",
|
||||
"get-pocket", "gg", "gg-circle", "git", "git-square", "github",
|
||||
"github-alt", "github-square", "gitkraken", "gitlab", "gitter",
|
||||
"glide", "glide-g", "gofore", "goodreads", "goodreads-g", "google",
|
||||
"google-drive", "google-play", "google-plus", "google-plus-g",
|
||||
"google-plus-square", "google-wallet", "gratipay", "grav", "gripfire",
|
||||
"grunt", "gulp", "hacker-news", "hacker-news-square", "hackerrank",
|
||||
"hips", "hire-a-helper", "hooli", "hornbill", "hotjar", "houzz",
|
||||
"html5", "hubspot", "imdb", "instagram", "internet-explorer",
|
||||
"ioxhost", "itunes", "itunes-note", "java", "jedi-order", "jenkins",
|
||||
"joget", "joomla", "js", "js-square", "jsfiddle", "kaggle", "keybase",
|
||||
"keycdn", "kickstarter", "kickstarter-k", "korvue", "laravel",
|
||||
"lastfm", "lastfm-square", "leanpub", "less", "line", "linkedin",
|
||||
"linkedin-in", "linode", "linux", "lyft", "magento", "mailchimp",
|
||||
"mandalorian", "markdown", "mastodon", "maxcdn", "medapps", "medium",
|
||||
"medium-m", "medrt", "meetup", "megaport", "microsoft", "mix",
|
||||
"mixcloud", "mizuni", "modx", "monero", "napster", "neos", "nimblr",
|
||||
"nintendo-switch", "node", "node-js", "npm", "ns8", "nutritionix",
|
||||
"odnoklassniki", "odnoklassniki-square", "old-republic", "opencart",
|
||||
"openid", "opera", "optin-monster", "osi", "page4", "pagelines",
|
||||
"palfed", "patreon", "paypal", "periscope", "phabricator", "phoenix-framework",
|
||||
"phoenix-squadron", "php", "pied-piper", "pied-piper-alt", "pied-piper-hat",
|
||||
"pied-piper-pp", "pinterest", "pinterest-p", "pinterest-square",
|
||||
"playstation", "product-hunt", "pushed", "python", "qq", "quinscape",
|
||||
"quora", "r-project", "ravelry", "react", "readme", "rebel",
|
||||
"red-river", "reddit", "reddit-alien", "reddit-square", "rendact",
|
||||
"renren", "replyd", "researchgate", "resolving", "rev", "rocketchat",
|
||||
"rockrms", "safari", "sass", "schlix", "scribd", "searchengin",
|
||||
"sellcast", "sellsy", "servicestack", "shirtsinbulk", "shopware",
|
||||
"simplybuilt", "sistrix", "sith", "skyatlas", "skype", "slack",
|
||||
"slack-hash", "slideshare", "snapchat", "snapchat-ghost", "snapchat-square",
|
||||
"soundcloud", "speakap", "spotify", "squarespace", "stack-exchange",
|
||||
"stack-overflow", "staylinked", "steam", "steam-square", "steam-symbol",
|
||||
"sticker-mule", "strava", "stripe", "stripe-s", "studiovinari",
|
||||
"stumbleupon", "stumbleupon-circle", "superpowers", "supple",
|
||||
"teamspeak", "telegram", "telegram-plane", "tencent-weibo", "the-red-yeti",
|
||||
"themeco", "themeisle", "trade-federation", "trello", "tripadvisor",
|
||||
"tumblr", "tumblr-square", "twitch", "twitter", "twitter-square",
|
||||
"typo3", "uber", "uikit", "uniregistry", "untappd", "usb", "ussunnah",
|
||||
"vaadin", "viacoin", "viadeo", "viadeo-square", "viber", "vimeo",
|
||||
"vimeo-square", "vimeo-v", "vine", "vk", "vnv", "vuejs", "weebly",
|
||||
"weibo", "weixin", "whatsapp", "whatsapp-square", "whmcs", "wikipedia-w",
|
||||
"windows", "wix", "wolf-pack-battalion", "wordpress", "wordpress-simple",
|
||||
"wpbeginner", "wpexplorer", "wpforms", "xbox", "xing", "xing-square",
|
||||
"y-combinator", "yahoo", "yandex", "yandex-international", "yelp",
|
||||
"yoast", "youtube", "youtube-square", "zhihu"
|
||||
)
|
||||
@@ -1402,14 +1402,23 @@ reactiveTimer <- function(intervalMs=1000, session = getDefaultReactiveDomain())
|
||||
|
||||
timerHandle <<- scheduleTask(intervalMs, sys.function())
|
||||
|
||||
session$cycleStartAction(function() {
|
||||
doInvalidate <- function() {
|
||||
lapply(
|
||||
dependents$values(),
|
||||
function(dep.ctx) {
|
||||
dep.ctx$invalidate()
|
||||
NULL
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
if (!is.null(session)) {
|
||||
# If this timer belongs to a session, we must wait until the next cycle is
|
||||
# ready to invalidate.
|
||||
session$cycleStartAction(doInvalidate)
|
||||
} else {
|
||||
# If this timer doesn't belong to a session, we invalidate right away.
|
||||
doInvalidate()
|
||||
}
|
||||
})
|
||||
|
||||
if (!is.null(session)) {
|
||||
|
||||
31
R/shiny.R
31
R/shiny.R
@@ -683,11 +683,42 @@ ShinySession <- R6Class(
|
||||
|
||||
# See cycleStartAction
|
||||
startCycle = function() {
|
||||
# TODO: This should check for busyCount == 0L, and remove the checks from
|
||||
# the call sites
|
||||
if (length(private$cycleStartActionQueue) > 0) {
|
||||
head <- private$cycleStartActionQueue[[1L]]
|
||||
private$cycleStartActionQueue <- private$cycleStartActionQueue[-1L]
|
||||
|
||||
# After we execute the current cycleStartAction (head), there may be
|
||||
# more items left on the queue. If the current busyCount > 0, then that
|
||||
# means an async task is running; whenever that task finishes, it will
|
||||
# decrement the busyCount back to 0 and a startCycle will then be
|
||||
# scheduled. But if the current busyCount is 0, it means that either
|
||||
# busyCount was incremented and then decremented; OR that running head()
|
||||
# never touched busyCount (one example of the latter is that an input
|
||||
# changed that didn't actually cause any observers to be invalidated,
|
||||
# i.e. an input that's used in the body of an observeEvent). Because of
|
||||
# the possibility of the latter case, we need to conditionally schedule
|
||||
# a startCycle ourselves to ensure that the remaining queue items get
|
||||
# processed.
|
||||
#
|
||||
# Since we can't actually tell whether head() increment and decremented
|
||||
# busyCount, it's possible we're calling startCycle spuriously; that's
|
||||
# OK, it's essentially a no-op in that case.
|
||||
on.exit({
|
||||
if (private$busyCount == 0L && length(private$cycleStartActionQueue) > 0L) {
|
||||
later::later(function() {
|
||||
if (private$busyCount == 0L) {
|
||||
private$startCycle()
|
||||
}
|
||||
})
|
||||
}
|
||||
}, add = TRUE)
|
||||
|
||||
head()
|
||||
}
|
||||
|
||||
invisible()
|
||||
}
|
||||
),
|
||||
public = list(
|
||||
|
||||
@@ -30,10 +30,6 @@ of a button, or as an icon for a \code{\link{tabPanel}} within a
|
||||
\code{\link{navbarPage}}.
|
||||
}
|
||||
\examples{
|
||||
icon("calendar") # standard icon
|
||||
icon("calendar", "fa-3x") # 3x normal size
|
||||
icon("cog", lib = "glyphicon") # From glyphicon library
|
||||
|
||||
# add an icon to a submit button
|
||||
submitButton("Update View", icon = icon("refresh"))
|
||||
|
||||
|
||||
Reference in New Issue
Block a user