mirror of
https://github.com/rstudio/shiny.git
synced 2026-01-11 07:58:11 -05:00
Compare commits
32 Commits
switchInpu
...
wch-css-lo
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fe909f9fc9 | ||
|
|
16e0d9e355 | ||
|
|
d430b80191 | ||
|
|
2ffa8707ea | ||
|
|
cbd06cbd8e | ||
|
|
d3aa1acfbf | ||
|
|
c2232ae07a | ||
|
|
cf0a865d6f | ||
|
|
4942b3e6ad | ||
|
|
f374a1512a | ||
|
|
1558c848f4 | ||
|
|
4a2bb8fc43 | ||
|
|
fad21af146 | ||
|
|
850a628978 | ||
|
|
4d2311841d | ||
|
|
5c4175cd5f | ||
|
|
2931e40c7b | ||
|
|
6a6eae1ce1 | ||
|
|
210642e96c | ||
|
|
c97fad30ef | ||
|
|
268c9afec3 | ||
|
|
5c919ae565 | ||
|
|
c94f411fc6 | ||
|
|
22d408aa7b | ||
|
|
a44fdc1b11 | ||
|
|
50ca830ec6 | ||
|
|
e643cd3824 | ||
|
|
2660a50d31 | ||
|
|
927912efe3 | ||
|
|
9b49a24e74 | ||
|
|
0824b22532 | ||
|
|
000feead00 |
@@ -3,11 +3,11 @@ Type: Package
|
||||
Title: Web Application Framework for R
|
||||
Version: 1.6.0.9000
|
||||
Authors@R: c(
|
||||
person("Winston", "Chang", role = c("aut", "cre"), email = "winston@rstudio.com"),
|
||||
person("Winston", "Chang", role = c("aut", "cre"), email = "winston@rstudio.com", comment = c(ORCID = "0000-0002-1576-2126")),
|
||||
person("Joe", "Cheng", role = "aut", email = "joe@rstudio.com"),
|
||||
person("JJ", "Allaire", role = "aut", email = "jj@rstudio.com"),
|
||||
person("Carson", "Sievert", role = "aut", email = "carson@rstudio.com"),
|
||||
person("Barret", "Schloerke", role = "aut", email = "barret@rstudio.com"),
|
||||
person("Carson", "Sievert", role = "aut", email = "carson@rstudio.com", comment = c(ORCID = "0000-0002-4958-2844")),
|
||||
person("Barret", "Schloerke", role = "aut", email = "barret@rstudio.com", comment = c(ORCID = "0000-0001-9986-114X")),
|
||||
person("Yihui", "Xie", role = "aut", email = "yihui@rstudio.com"),
|
||||
person("Jeff", "Allen", role = "aut", email = "jeff@rstudio.com"),
|
||||
person("Jonathan", "McPherson", role = "aut", email = "jonathan@rstudio.com"),
|
||||
|
||||
@@ -222,6 +222,7 @@ export(reactlogReset)
|
||||
export(reactlogShow)
|
||||
export(registerInputHandler)
|
||||
export(registerThemeDependency)
|
||||
export(register_devmode_option)
|
||||
export(removeInputHandler)
|
||||
export(removeModal)
|
||||
export(removeNotification)
|
||||
|
||||
6
NEWS.md
6
NEWS.md
@@ -17,7 +17,7 @@ shiny 1.6.0.9000
|
||||
* Closed #3321: New informative warning when `shiny.tag` object(s) are supplied to `...`. In this case we will continue to create an "empty" nav item and include the content on every tab, but the warning will mention the (new) `header`/`footer` args, which is likely what the user wants.
|
||||
* Closed #3320: The HTML markup that `tabPanel()` et. al generate (for Bootstrap nav) is now Bootstrap 4+ compliant when used with `theme = bslib::bs_theme()`.
|
||||
* Closed #1928: `NULL` values are now dropped instead of producing an empty nav item.
|
||||
|
||||
|
||||
### Other improvements
|
||||
|
||||
* Shiny's core JavaScript code was converted to TypeScript. For the latest development information, please see the [README.md in `./srcts`](https://github.com/rstudio/shiny/tree/master/srcts). (#3296)
|
||||
@@ -28,6 +28,10 @@ shiny 1.6.0.9000
|
||||
|
||||
### Bug fixes
|
||||
|
||||
* Closed #3374: `quoToFunction()` now works correctly with nested quosures; and as a result, quasi-quotation with rendering function (e.g., `renderPrint()`, `renderPlot()`, etc) now works as expected with nested quosures. (#3373)
|
||||
|
||||
* Exported `register_devmode_option()`. This method was described in the documentation for `devmode()` but was never exported. See `?devmode()` for more details on how to register Shiny Developer options using `register_devmode_option()`. (#3364)
|
||||
|
||||
### Library updates
|
||||
|
||||
* Closed #3286: Updated to Font-Awesome 5.15.2. (#3288)
|
||||
|
||||
@@ -43,6 +43,7 @@ bootstrapPage <- function(..., title = NULL, responsive = deprecated(), theme =
|
||||
}
|
||||
|
||||
args <- list(
|
||||
jqueryDependency(),
|
||||
if (!is.null(title)) tags$head(tags$title(title)),
|
||||
if (is.character(theme)) {
|
||||
if (length(theme) > 1) stop("`theme` must point to a single CSS file, not multiple files.")
|
||||
@@ -91,6 +92,10 @@ getLang <- function(ui) {
|
||||
#' @export
|
||||
bootstrapLib <- function(theme = NULL) {
|
||||
tagFunction(function() {
|
||||
if (isRunning()) {
|
||||
setCurrentTheme(theme)
|
||||
}
|
||||
|
||||
# If we're not compiling Bootstrap Sass (from bslib), return the
|
||||
# static Bootstrap build.
|
||||
if (!is_bs_theme(theme)) {
|
||||
@@ -112,7 +117,6 @@ bootstrapLib <- function(theme = NULL) {
|
||||
# Note also that since this is shinyOptions() (and not options()), the
|
||||
# option is automatically reset when the app (or session) exits
|
||||
if (isRunning()) {
|
||||
setCurrentTheme(theme)
|
||||
registerThemeDependency(bs_theme_deps)
|
||||
|
||||
} else {
|
||||
@@ -1010,9 +1014,12 @@ buildTabItem <- function(index, tabsetId, foundSelected, tabs = NULL,
|
||||
|
||||
buildNavItem <- function(divTag, tabsetId, index) {
|
||||
id <- paste("tab", tabsetId, index, sep = "-")
|
||||
title <- tagGetAttribute(divTag, "title")
|
||||
value <- tagGetAttribute(divTag, "data-value")
|
||||
icon <- getIcon(iconClass = tagGetAttribute(divTag, "data-icon-class"))
|
||||
# Get title attribute directory (not via tagGetAttribute()) so that contents
|
||||
# don't get passed to as.character().
|
||||
# https://github.com/rstudio/shiny/issues/3352
|
||||
title <- divTag$attribs[["title"]]
|
||||
value <- divTag$attribs[["data-value"]]
|
||||
icon <- getIcon(iconClass = divTag$attribs[["data-icon-class"]])
|
||||
active <- isTabSelected(divTag)
|
||||
divTag <- tagAppendAttributes(divTag, class = if (active) "active")
|
||||
divTag$attribs$id <- id
|
||||
|
||||
@@ -233,6 +233,7 @@ registered_devmode_options <- Map$new()
|
||||
#' devmode_default = FALSE
|
||||
#' )
|
||||
#' ```
|
||||
#'
|
||||
#' @param name Name of option to look for in `options()`
|
||||
#' @param default Default value to return if `in_devmode()` returns
|
||||
#' `TRUE` and the specified option is not set in [`options()`].
|
||||
@@ -243,6 +244,7 @@ registered_devmode_options <- Map$new()
|
||||
#' `TRUE` and the specified option is not set in [`options()`]. For
|
||||
#' `get_devmode_option()`, if `devmode_default` is missing, the
|
||||
#' registered `devmode_default` value will be used.
|
||||
#' @export
|
||||
#' @examples
|
||||
#' # Ex: Within shiny, we register the option "shiny.minified"
|
||||
#' # to default to `FALSE` when in Dev Mode
|
||||
|
||||
@@ -2175,7 +2175,7 @@ ShinySession <- R6Class(
|
||||
if (getOption("shiny.allowoutputreads", FALSE)) {
|
||||
.subset2(x, 'impl')$getOutput(name)
|
||||
} else {
|
||||
rlang::abort(paste0("Can't read output '", output, "'"))
|
||||
rlang::abort(paste0("Can't read output '", name, "'"))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
48
R/shinyui.R
48
R/shinyui.R
@@ -47,27 +47,8 @@ renderPage <- function(ui, showcase=0, testMode=FALSE) {
|
||||
)
|
||||
}
|
||||
|
||||
jquery <- function() {
|
||||
version <- getOption("shiny.jquery.version", 3)
|
||||
if (version == 3) {
|
||||
return(htmlDependency(
|
||||
"jquery", version_jquery,
|
||||
c(href = "shared"),
|
||||
script = "jquery.min.js"
|
||||
))
|
||||
}
|
||||
if (version == 1) {
|
||||
return(htmlDependency(
|
||||
"jquery", "1.12.4",
|
||||
c(href = "shared/legacy"),
|
||||
script = "jquery.min.js"
|
||||
))
|
||||
}
|
||||
stop("Unsupported version of jQuery: ", version)
|
||||
}
|
||||
|
||||
shiny_deps <- c(
|
||||
list(jquery()),
|
||||
list(jqueryDependency()),
|
||||
shinyDependencies()
|
||||
)
|
||||
|
||||
@@ -82,6 +63,33 @@ renderPage <- function(ui, showcase=0, testMode=FALSE) {
|
||||
enc2utf8(paste(collapse = "\n", html))
|
||||
}
|
||||
|
||||
jqueryDependency <- function() {
|
||||
version <- getOption("shiny.jquery.version", 3)
|
||||
if (version == 3) {
|
||||
return(htmlDependency(
|
||||
"jquery", version_jquery,
|
||||
src = c(
|
||||
href = "shared",
|
||||
file = "www/shared"
|
||||
),
|
||||
package = "shiny",
|
||||
script = "jquery.min.js"
|
||||
))
|
||||
}
|
||||
if (version == 1) {
|
||||
return(htmlDependency(
|
||||
"jquery", "1.12.4",
|
||||
src = c(
|
||||
href = "shared/legacy",
|
||||
file = "www/shared/legacy"
|
||||
),
|
||||
package = "shiny",
|
||||
script = "jquery.min.js"
|
||||
))
|
||||
}
|
||||
stop("Unsupported version of jQuery: ", version)
|
||||
}
|
||||
|
||||
shinyDependencies <- function() {
|
||||
list(
|
||||
bslib::bs_dependency_defer(shinyDependencyCSS),
|
||||
|
||||
@@ -538,9 +538,10 @@ installExprFunction <- function(expr, name, eval.env = parent.frame(2),
|
||||
#' @export
|
||||
quoToFunction <- function(q, label, ..stacktraceon = FALSE) {
|
||||
q <- as_quosure(q)
|
||||
# Use new_function() instead of as_function(), because as_function() adds an
|
||||
# extra parent environment. (This may not actually be a problem, though.)
|
||||
func <- new_function(NULL, get_expr(q), get_env(q))
|
||||
func <- as_function(q)
|
||||
# as_function returns a function that takes `...`. We want one that takes no
|
||||
# args.
|
||||
formals(func) <- list()
|
||||
wrapFunctionLabel(func, label, ..stacktraceon = ..stacktraceon)
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
|
||||
Selectize.define("selectize-plugin-a11y",function(t){var e,s,a=this;void 0===a.accessibility&&(a.accessibility={}),a.accessibility.helpers={randomId:function(t){for(var e="",i=t||10,s="abcdefghijklmnopqrstuvwxyz0123456789",a=s.length,o=0;o<i;o++)e+=s[Math.floor(a*Math.random())];return e}},a.accessibility.liveRegion={$region:"",speak:function(t){var e=$("<div></div>");e.text(t),this.$region.html(e)},domListener:function(){var t=new MutationObserver(function(t){t.forEach(function(t){t=$(t.target);if(t.hasClass("items"))if(t.hasClass("dropdown-active")){a.$control_input.attr("aria-expanded","true");var e=a.$dropdown_content[0].children;for(i=0;i<e.length;i++){var s=e[i].attributes;s.role||e[i].setAttribute("role","option"),s.id||e[i].setAttribute("id",a.accessibility.helpers.randomId())}}else a.$control_input.attr("aria-expanded","false"),a.$control_input.removeAttr("aria-activedescendant");else t.hasClass("active")&&t.attr("data-value")&&(a.$control_input.attr("aria-activedescendant",t.attr("id")),a.accessibility.liveRegion.speak(t.text(),500))})});t.observe(a.$dropdown[0],{attributes:!0,attributeFilter:["class"],subtree:!0,attributeOldValue:!0}),t.observe(a.$control[0],{attributes:!0,attributeFilter:["class"]}),t.observe(a.$control_input[0],{attributes:!0,attributeFilter:["value"]})},setAttributes:function(){this.$region.attr({"aria-live":"assertive",role:"log","aria-relevant":"additions","aria-atomic":"true"})},setStyles:function(){this.$region.css({position:"absolute",width:"1px",height:"1px","margin-top":"-1px",clip:"rect(1px, 1px, 1px, 1px)",overflow:"hidden"})},init:function(){this.$region=$("<div>"),this.setAttributes(),this.setStyles(),$("body").append(this.$region),this.domListener()}},this.setup=(e=a.setup,function(){e.apply(this,arguments);a.accessibility.helpers.randomId();var t=a.accessibility.helpers.randomId();a.$control.on("keydown",function(t){13===t.keyCode&&(a.settings.openOnFocus?(a.settings.openOnFocus=!1,a.focus(),setTimeout(function(){a.settings.openOnFocus=!0},0)):a.focus())}),a.$control_input.attr({role:"combobox","aria-expanded":"false",haspopup:"listbox","aria-owns":t,"aria-label":a.$wrapper.closest("[data-accessibility-selectize-label]").attr("data-accessibility-selectize-label")}),a.$dropdown_content.attr({role:"listbox",id:t}),a.accessibility.liveRegion.init()}),this.destroy=(s=a.destroy,function(){return a.accessibility.liveRegion.$region.remove(),s.apply(this,arguments)})});
|
||||
Selectize.define("selectize-plugin-a11y",function(c){var t=this,l=13;typeof t.accessibility=="undefined"&&(t.accessibility={}),t.accessibility.helpers={randomId:function(e){for(var a="",s=e||10,r="abcdefghijklmnopqrstuvwxyz0123456789",n=r.length,o=0;o<s;o++)a+=r[Math.floor(n*Math.random())];return a}},t.accessibility.liveRegion={$region:"",speak:function(e){var a=$("<div></div>");a.text(e),this.$region.html(a)},domListener:function(){var e=new MutationObserver(function(a){a.forEach(function(s){var r=$(s.target);if(r.hasClass("items"))if(r.hasClass("dropdown-active")){t.$control_input.attr("aria-expanded","true");var n=t.$dropdown_content[0].children;for(i=0;i<n.length;i++){var o=n[i].attributes;o.role||n[i].setAttribute("role","option"),o.id||n[i].setAttribute("id",t.accessibility.helpers.randomId())}}else t.$control_input.attr("aria-expanded","false"),t.$control_input.removeAttr("aria-activedescendant");else r.hasClass("active")&&r.attr("data-value")&&(t.$control_input.attr("aria-activedescendant",r.attr("id")),t.accessibility.liveRegion.speak(r.text(),500))})});e.observe(t.$dropdown[0],{attributes:!0,attributeFilter:["class"],subtree:!0,attributeOldValue:!0}),e.observe(t.$control[0],{attributes:!0,attributeFilter:["class"]}),e.observe(t.$control_input[0],{attributes:!0,attributeFilter:["value"]})},setAttributes:function(){this.$region.attr({"aria-live":"assertive",role:"log","aria-relevant":"additions","aria-atomic":"true"})},setStyles:function(){this.$region.css({position:"absolute",width:"1px",height:"1px","margin-top":"-1px",clip:"rect(1px, 1px, 1px, 1px)",overflow:"hidden"})},init:function(){this.$region=$("<div>"),this.setAttributes(),this.setStyles(),$("body").append(this.$region),this.domListener()}},this.setup=function(){var e=t.setup;return function(){e.apply(this,arguments);var a=t.accessibility.helpers.randomId(),s=t.accessibility.helpers.randomId();t.$control.on("keydown",function(r){r.keyCode===l&&(t.settings.openOnFocus?(t.settings.openOnFocus=!1,t.focus(),setTimeout(function(){t.settings.openOnFocus=!0},0)):t.focus())}),t.$control_input.attr({role:"combobox","aria-expanded":"false",haspopup:"listbox","aria-owns":s,"aria-label":t.$wrapper.closest("[data-accessibility-selectize-label]").attr("data-accessibility-selectize-label")}),t.$dropdown_content.attr({role:"listbox",id:s}),t.accessibility.liveRegion.init()}}(),this.destroy=function(){var e=t.destroy;return function(){return t.accessibility.liveRegion.$region.remove(),e.apply(this,arguments)}}()});
|
||||
|
||||
@@ -3127,9 +3127,9 @@
|
||||
}
|
||||
});
|
||||
function ensureTabsetHasVisibleTab($tabset) {
|
||||
if ($tabset.find("li.active").not(".dropdown").length === 0) {
|
||||
var inputBinding = $tabset.data("shiny-input-binding");
|
||||
if (!inputBinding.getValue($tabset)) {
|
||||
var destTabValue = getFirstTab($tabset);
|
||||
var inputBinding = $tabset.data("shiny-input-binding");
|
||||
var evt = jQuery.Event("shiny:updateinput");
|
||||
evt.binding = inputBinding;
|
||||
$tabset.trigger(evt);
|
||||
@@ -4730,7 +4730,7 @@
|
||||
if (!restyle) {
|
||||
$head.append(links);
|
||||
} else {
|
||||
var refreshStyle = function refreshStyle2(href2, oldSheet) {
|
||||
var refreshStyle = function refreshStyle2(href2, oldSheet, onload) {
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open("GET", href2);
|
||||
xhr.onload = function() {
|
||||
@@ -4744,6 +4744,7 @@
|
||||
setTimeout(function() {
|
||||
return removeSheet(oldSheet);
|
||||
}, 500);
|
||||
onload();
|
||||
};
|
||||
xhr.send();
|
||||
};
|
||||
@@ -4768,21 +4769,29 @@
|
||||
var oldSheet = findSheet(link.attr("href"));
|
||||
var href2 = link.attr("href") + "?restyle=" + new Date().getTime();
|
||||
if (isIE()) {
|
||||
refreshStyle(href2, oldSheet);
|
||||
refreshStyle(href2, oldSheet, scheduleCssReporter);
|
||||
} else {
|
||||
link.attr("href", href2);
|
||||
link.attr("onload", function() {
|
||||
setTimeout(function() {
|
||||
return removeSheet(oldSheet);
|
||||
}, 500);
|
||||
var dummy_id = "dummy-" + Math.floor(Math.random() * 99999999);
|
||||
var css_string = "#" + dummy_id + " { color: #" + Math.floor(Math.random() * 16777215).toString(16) + "; transition: 0.2s all; visibility: hidden; position: fixed; top: 0; left: 0; }";
|
||||
var base64_css_string = "data:text/css;base64," + btoa(css_string);
|
||||
var dummy_link = document.createElement("link");
|
||||
dummy_link.rel = "stylesheet";
|
||||
dummy_link.type = "text/css";
|
||||
dummy_link.href = base64_css_string;
|
||||
var $dummy_el = import_jquery6.default("<div id=" + dummy_id + "></div>");
|
||||
import_jquery6.default(document.body).append($dummy_el);
|
||||
$dummy_el.one("transitionend", function() {
|
||||
sendImageSize();
|
||||
removeSheet(oldSheet);
|
||||
$dummy_el.remove();
|
||||
});
|
||||
$head.append(dummy_link);
|
||||
});
|
||||
$head.append(link);
|
||||
}
|
||||
});
|
||||
var bindDebouncer = new Debouncer(null, Shiny.bindAll, 100);
|
||||
setTimeout(function() {
|
||||
return bindDebouncer.normalCall();
|
||||
}, 100);
|
||||
}
|
||||
}
|
||||
if (dep.script && !restyle) {
|
||||
@@ -6440,6 +6449,7 @@
|
||||
}
|
||||
});
|
||||
inputBindings.register(fileInputBinding, "shiny.fileInputBinding");
|
||||
var sendImageSize;
|
||||
function initShiny() {
|
||||
var shinyapp = Shiny.shinyapp = new ShinyApp();
|
||||
function bindOutputs() {
|
||||
@@ -6764,9 +6774,9 @@
|
||||
});
|
||||
}
|
||||
var sendImageSizeDebouncer = new Debouncer(null, doSendImageSize, 0);
|
||||
function sendImageSize() {
|
||||
sendImageSize = function sendImageSize() {
|
||||
sendImageSizeDebouncer.normalCall();
|
||||
}
|
||||
};
|
||||
inputBatchSender.lastChanceCallback.push(function() {
|
||||
if (sendImageSizeDebouncer.isPending())
|
||||
sendImageSizeDebouncer.immediateCall();
|
||||
@@ -7777,10 +7787,10 @@
|
||||
return linkColor;
|
||||
}
|
||||
function isBS3() {
|
||||
if (!import_jquery5.default.fn.tooltip) {
|
||||
if (!import_jquery5.default.fn.tab) {
|
||||
return false;
|
||||
}
|
||||
return import_jquery5.default.fn.tooltip.Constructor.VERSION.match(/^3\./);
|
||||
return import_jquery5.default.fn.tab.Constructor.VERSION.match(/^3\./);
|
||||
}
|
||||
|
||||
// src/shiny.ts
|
||||
|
||||
File diff suppressed because one or more lines are too long
2
inst/www/shared/shiny.min.js
vendored
2
inst/www/shared/shiny.min.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
63
srcts/esbuild.external_libs.mjs
Normal file
63
srcts/esbuild.external_libs.mjs
Normal file
@@ -0,0 +1,63 @@
|
||||
import {readdirSync, unlinkSync, writeFileSync} from "fs";
|
||||
import esbuild from "esbuild";
|
||||
import globalsPlugin from "esbuild-plugin-globals";
|
||||
|
||||
// import process from "process";
|
||||
// let watch = process.argv.length >= 3 && process.argv[2] == "--watch";
|
||||
|
||||
let instdir = "../inst/";
|
||||
let outdir = instdir + "/www/shared/";
|
||||
|
||||
let opts = {
|
||||
bundle: false,
|
||||
watch: false,
|
||||
target: "es5",
|
||||
sourcemap: false,
|
||||
};
|
||||
|
||||
console.log("Building datepicker");
|
||||
const locale_files = readdirSync(instdir + "www/shared/datepicker/js/locales/")
|
||||
|
||||
let require_files = locale_files.map(function(filename) {
|
||||
return `require("./locales/${ filename }");`;
|
||||
}).join("\n");
|
||||
|
||||
let tmpfile = instdir + "www/shared/datepicker/js/temp.js";
|
||||
writeFileSync(tmpfile,
|
||||
`require("./bootstrap-datepicker.js");
|
||||
${require_files}`)
|
||||
await esbuild.build({
|
||||
...opts,
|
||||
plugins:[
|
||||
globalsPlugin({
|
||||
jquery: "window.jQuery",
|
||||
})
|
||||
],
|
||||
bundle: true,
|
||||
entryPoints: [tmpfile],
|
||||
outfile: instdir + "www/shared/datepicker/js/bootstrap-datepicker.min.js",
|
||||
external: ['jquery'],
|
||||
minify: true,
|
||||
});
|
||||
// Clean up
|
||||
unlinkSync(tmpfile);
|
||||
|
||||
console.log("Building ionrangeslider");
|
||||
await esbuild.build({
|
||||
...opts,
|
||||
entryPoints: [
|
||||
instdir + "www/shared/ionrangeslider/js/ion.rangeSlider.js"
|
||||
],
|
||||
outfile: instdir + "www/shared/ionrangeslider/js/ion.rangeSlider.min.js",
|
||||
minify: true,
|
||||
});
|
||||
|
||||
console.log("Building selectize");
|
||||
await esbuild.build({
|
||||
...opts,
|
||||
entryPoints: [
|
||||
instdir + "www/shared/selectize/accessibility/js/selectize-plugin-a11y.js"
|
||||
],
|
||||
outfile: instdir + "www/shared/selectize/accessibility/js/selectize-plugin-a11y.min.js",
|
||||
minify: true,
|
||||
});
|
||||
@@ -42,10 +42,11 @@
|
||||
},
|
||||
"scripts": {
|
||||
"watch": "yarn run build_shiny --watch",
|
||||
"build": "yarn run build_shiny",
|
||||
"build": "yarn run build_shiny && yarn run bundle_external_libs",
|
||||
"setup_build_shiny": "yarn run lint && yarn run typescript-check",
|
||||
"build_shiny": "yarn run setup_build_shiny && yarn run bundle_shiny",
|
||||
"bundle_shiny": "node esbuild.config.mjs",
|
||||
"bundle_external_libs": "node esbuild.external_libs.mjs",
|
||||
"bundle_shiny_parcel2": "parcel build -d ../inst/www/shared --no-minify -o shiny.js src/index.ts",
|
||||
"watch_parcel2": "yarn run setup_build_shiny && parcel run -d ../inst/www/shared -o shiny.js srcjs/index.ts",
|
||||
"replace_shiny_version2": "replace --silent '\"[^\"]+\"; // @VERSION@' \"\\\"`node -e 'console.log(require(\"readcontrol\").readSync(\"../DESCRIPTION\").version)'`\\\"; // @VERSION@\" src/shiny.ts",
|
||||
|
||||
@@ -1480,13 +1480,16 @@ function main(): void {
|
||||
|
||||
// If the given tabset has no active tabs, select the first one
|
||||
function ensureTabsetHasVisibleTab($tabset) {
|
||||
if ($tabset.find("li.active").not(".dropdown").length === 0) {
|
||||
const inputBinding = $tabset.data("shiny-input-binding");
|
||||
|
||||
// Use the getValue() method to avoid duplicating the CSS selector
|
||||
// for querying the DOM for the currently active tab
|
||||
if (!inputBinding.getValue($tabset)) {
|
||||
// Note: destTabValue may be null. We still want to proceed
|
||||
// through the below logic and setValue so that the input
|
||||
// value for the tabset gets updated (i.e. input$tabsetId
|
||||
// should be null if there are no tabs).
|
||||
const destTabValue = getFirstTab($tabset);
|
||||
const inputBinding = $tabset.data("shiny-input-binding");
|
||||
const evt = jQuery.Event("shiny:updateinput");
|
||||
|
||||
evt.binding = inputBinding;
|
||||
@@ -4037,7 +4040,7 @@ function main(): void {
|
||||
$head.append(links);
|
||||
} else {
|
||||
// This inline <style> based approach works for IE11
|
||||
let refreshStyle = function (href, oldSheet) {
|
||||
let refreshStyle = function (href, oldSheet, onload) {
|
||||
const xhr = new XMLHttpRequest();
|
||||
|
||||
xhr.open("GET", href);
|
||||
@@ -4050,6 +4053,7 @@ function main(): void {
|
||||
$head.append(newStyle);
|
||||
setTimeout(() => oldStyle.remove(), 500);
|
||||
setTimeout(() => removeSheet(oldSheet), 500);
|
||||
onload();
|
||||
};
|
||||
xhr.send();
|
||||
};
|
||||
@@ -4086,26 +4090,43 @@ function main(): void {
|
||||
// <link> -based approach
|
||||
|
||||
if (isIE()) {
|
||||
refreshStyle(href, oldSheet);
|
||||
refreshStyle(href, oldSheet, scheduleCssReporter);
|
||||
} else {
|
||||
link.attr("href", href);
|
||||
// Once the new <link> is loaded, schedule the old <link> to be removed
|
||||
// on the next tick which is needed to avoid FOUC
|
||||
link.attr("onload", () => {
|
||||
setTimeout(() => removeSheet(oldSheet), 500);
|
||||
const dummy_id = "dummy-" + Math.floor(Math.random() * 99999999);
|
||||
const css_string =
|
||||
"#" + dummy_id +
|
||||
" { color: #" +
|
||||
Math.floor(Math.random() * 16777215).toString(16) + "; " +
|
||||
"transition: 0.2s all; " +
|
||||
"visibility: hidden; " +
|
||||
"position: fixed; top: 0; left: 0; }";
|
||||
const base64_css_string =
|
||||
"data:text/css;base64," + btoa(css_string);
|
||||
|
||||
let dummy_link = document.createElement("link");
|
||||
|
||||
dummy_link.rel = "stylesheet";
|
||||
dummy_link.type = "text/css";
|
||||
dummy_link.href = base64_css_string;
|
||||
|
||||
let $dummy_el = $("<div id=" + dummy_id + "></div>")
|
||||
$(document.body).append($dummy_el);
|
||||
$dummy_el
|
||||
.one("transitionend", () => {
|
||||
sendImageSize();
|
||||
removeSheet(oldSheet);
|
||||
$dummy_el.remove();
|
||||
});
|
||||
|
||||
$head.append(dummy_link);
|
||||
});
|
||||
$head.append(link);
|
||||
}
|
||||
});
|
||||
|
||||
// Once the new styles are applied, CSS values that are accessible server-side
|
||||
// (e.g., getCurrentOutputInfo(), output visibility, etc) may become outdated.
|
||||
// At the time of writing, that means we need to do sendImageSize() &
|
||||
// sendOutputHiddenState() again, which can be done by re-binding.
|
||||
/* global Shiny */
|
||||
const bindDebouncer = new Debouncer(null, Shiny.bindAll, 100);
|
||||
|
||||
setTimeout(() => bindDebouncer.normalCall(), 100);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6386,6 +6407,10 @@ function main(): void {
|
||||
});
|
||||
inputBindings.register(fileInputBinding, "shiny.fileInputBinding");
|
||||
|
||||
// This function gets defined in initShiny() and 'hoisted' so it can be reused
|
||||
// (to send CSS info) inside of Shiny.renderDependencies()
|
||||
let sendImageSize;
|
||||
|
||||
// "init_shiny.js"
|
||||
function initShiny() {
|
||||
const shinyapp = (Shiny.shinyapp = new ShinyApp());
|
||||
@@ -6868,9 +6893,9 @@ function main(): void {
|
||||
}
|
||||
const sendImageSizeDebouncer = new Debouncer(null, doSendImageSize, 0);
|
||||
|
||||
function sendImageSize() {
|
||||
sendImageSize = function () {
|
||||
sendImageSizeDebouncer.normalCall();
|
||||
}
|
||||
};
|
||||
// Make sure sendImageSize actually gets called before the inputBatchSender
|
||||
// sends data to the server.
|
||||
inputBatchSender.lastChanceCallback.push(function () {
|
||||
|
||||
@@ -364,12 +364,12 @@ function getComputedLinkColor(el: HTMLElement): string {
|
||||
function isBS3(): boolean {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
if (!$.fn.tooltip) {
|
||||
if (!$.fn.tab) {
|
||||
return false;
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
return $.fn.tooltip.Constructor.VERSION.match(/^3\./);
|
||||
return $.fn.tab.Constructor.VERSION.match(/^3\./);
|
||||
}
|
||||
|
||||
export {
|
||||
|
||||
@@ -96,3 +96,19 @@ test_that("tabPanelBody validates it's input", {
|
||||
expect_error(tabPanelBody(""), "single, non-empty string")
|
||||
expect_error(tabPanelBody(letters[1:2]), "single, non-empty string")
|
||||
})
|
||||
|
||||
|
||||
# https://github.com/rstudio/shiny/issues/3352
|
||||
test_that("tabItem titles can contain tag objects", {
|
||||
title <- tagList(tags$i("Hello"), "world")
|
||||
x <- tabsetPanel(tabPanel(title, "tab content"))
|
||||
x <- renderTags(x)
|
||||
|
||||
# Result should contain (with different whitespace):
|
||||
# "<a ....> <i>Hello</i> world"
|
||||
# As opposed to:
|
||||
# "<a ....><i>Hello</i> world
|
||||
expect_true(
|
||||
grepl("<a [^>]+>\\s*<i>Hello</i>\\s+world", x$html)
|
||||
)
|
||||
})
|
||||
|
||||
@@ -239,3 +239,15 @@ test_that("dateYMD works", {
|
||||
c("2019/11/05", "")
|
||||
)
|
||||
})
|
||||
|
||||
test_that("quoToFunction handles nested quosures", {
|
||||
quo_inner <- local({
|
||||
x <- 1
|
||||
rlang::quo(x)
|
||||
})
|
||||
|
||||
quo_outer <- rlang::quo(!!quo_inner + 1)
|
||||
|
||||
func <- quoToFunction(quo_outer, "foo")
|
||||
expect_identical(func(), 2)
|
||||
})
|
||||
|
||||
@@ -66,4 +66,4 @@ sass(
|
||||
|
||||
|
||||
# Finally, run yarn build so the JS patches propogate to the minified files
|
||||
withr::with_dir(rprojroot::find_package_root_file("tools"), system("yarn build"))
|
||||
withr::with_dir(rprojroot::find_package_root_file("tools"), system("yarn bundle_external_libs"))
|
||||
|
||||
Reference in New Issue
Block a user