mirror of
https://github.com/rstudio/shiny.git
synced 2026-01-14 09:28:02 -05:00
v1.11.1 release candidate (#4245)
* v1.11.1 release candidate * `yarn build` (GitHub Actions) * Revert actionButton()/actionLink() implementation to v1.11.0's behavior (re-introducing #4239) * Minimal fix to address the regression in #4239 Ideally we'd fix this issue, and also get updateActionButton() working with HTML labels, but thanks to today's release of kinesis (which snapshots all of actionButton()s markup), and CRAN dragging their feet to accept our original submission (which was fine, by the way), we can't have nice things * `yarn build` (GitHub Actions) --------- Co-authored-by: cpsievert <cpsievert@users.noreply.github.com>
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
Package: shiny
|
||||
Type: Package
|
||||
Title: Web Application Framework for R
|
||||
Version: 1.11.0.9000
|
||||
Version: 1.11.1
|
||||
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"),
|
||||
@@ -202,7 +202,6 @@ Collate:
|
||||
'test.R'
|
||||
'update-input.R'
|
||||
'utils-lang.R'
|
||||
'utils-tags.R'
|
||||
'version_bs_date_picker.R'
|
||||
'version_ion_range_slider.R'
|
||||
'version_jquery.R'
|
||||
|
||||
13
NEWS.md
13
NEWS.md
@@ -1,17 +1,12 @@
|
||||
# shiny (development version)
|
||||
# shiny 1.11.1
|
||||
|
||||
## New features
|
||||
This is a patch release primarily for addressing the bugs introduced in v1.11.0.
|
||||
|
||||
* The `icon` argument of `actionButton()`, `downloadButton()`, etc. now accepts values other than `shiny::icon()` (like `fontawesome::fa()` and `bsicons::bs_icon()`). (#4242)
|
||||
|
||||
## Improvements
|
||||
|
||||
* Padding is now provided between the `icon` and `label` of an `actionButton()`. (#4242)
|
||||
## Bug fixes
|
||||
|
||||
* Fixed a regression in v1.11.0 where `InputBinding` implementations that don't pass a value to their `subscribe` callback were no longer notifying Shiny of input changes. (#4243)
|
||||
* Fixed an issue where `InputBinding` implementations that don't pass a value to their `subscribe` callback were no longer notifying Shiny of input changes. (#4243)
|
||||
|
||||
* `updateActionButton()` and `updateActionLink()` once again handle `label` updates correctly (which can now include HTML). (#4242)
|
||||
* `updateActionButton()` and `updateActionLink()` once again handle `label` updates correctly. (#4245)
|
||||
|
||||
# shiny 1.11.0
|
||||
|
||||
|
||||
@@ -56,14 +56,13 @@ actionButton <- function(inputId, label, icon = NULL, width = NULL,
|
||||
|
||||
value <- restoreInput(id = inputId, default = NULL)
|
||||
|
||||
tags$button(
|
||||
id = inputId,
|
||||
tags$button(id=inputId,
|
||||
style = css(width = validateCssUnit(width)),
|
||||
type = "button",
|
||||
class = "btn btn-default action-button",
|
||||
type="button",
|
||||
class="btn btn-default action-button",
|
||||
`data-val` = value,
|
||||
disabled = if (isTRUE(disabled)) NA else NULL,
|
||||
get_action_children(label, icon),
|
||||
list(validateIcon(icon), label),
|
||||
...
|
||||
)
|
||||
}
|
||||
@@ -73,52 +72,30 @@ actionButton <- function(inputId, label, icon = NULL, width = NULL,
|
||||
actionLink <- function(inputId, label, icon = NULL, ...) {
|
||||
value <- restoreInput(id = inputId, default = NULL)
|
||||
|
||||
tags$a(
|
||||
id = inputId,
|
||||
href = "#",
|
||||
class = "action-button",
|
||||
tags$a(id=inputId,
|
||||
href="#",
|
||||
class="action-button",
|
||||
`data-val` = value,
|
||||
get_action_children(label, icon),
|
||||
list(validateIcon(icon), label),
|
||||
...
|
||||
)
|
||||
}
|
||||
|
||||
get_action_children <- function(label, icon) {
|
||||
icon <- validateIcon(icon)
|
||||
|
||||
if (length(icon) > 0) {
|
||||
# The separator elements helps us distinguish between the icon and label
|
||||
# when dynamically updating the button/link. Ideally, we would wrap each
|
||||
# in a container element, but is currently done with a separator to help
|
||||
# minimize the chance of breaking existing code.
|
||||
tagList(
|
||||
icon,
|
||||
tags$span(class = "shiny-icon-separator"),
|
||||
label
|
||||
)
|
||||
} else {
|
||||
# Technically, we don't need the `icon` here, but keeping it maintains
|
||||
# backwards compatibility of `btn$children[[1]][[2]]` to get the label.
|
||||
# The shinyGovstyle package is at least one example of this.
|
||||
tagList(icon, label)
|
||||
}
|
||||
}
|
||||
|
||||
# Throw an informative warning if icon isn't html-ish
|
||||
# Check that the icon parameter is valid:
|
||||
# 1) Check if the user wants to actually add an icon:
|
||||
# -- if icon=NULL, it means leave the icon unchanged
|
||||
# -- if icon=character(0), it means don't add an icon or, more usefully,
|
||||
# remove the previous icon
|
||||
# 2) If so, check that the icon has the right format (this does not check whether
|
||||
# it is a *real* icon - currently that would require a massive cross reference
|
||||
# with the "font-awesome" and the "glyphicon" libraries)
|
||||
validateIcon <- function(icon) {
|
||||
if (length(icon) == 0) {
|
||||
if (is.null(icon) || identical(icon, character(0))) {
|
||||
return(icon)
|
||||
} else if (inherits(icon, "shiny.tag") && icon$name == "i") {
|
||||
return(icon)
|
||||
} else {
|
||||
stop("Invalid icon. Use Shiny's 'icon()' function to generate a valid icon")
|
||||
}
|
||||
|
||||
if (!isTagLike(icon)) {
|
||||
rlang::warn(
|
||||
c(
|
||||
"It appears that a non-HTML value was provided to `icon`.",
|
||||
i = "Try using a `shiny::icon()` (or an equivalent) to get an icon."
|
||||
),
|
||||
class = "shiny-validate-icon"
|
||||
)
|
||||
}
|
||||
|
||||
icon
|
||||
}
|
||||
|
||||
@@ -181,22 +181,14 @@ updateCheckboxInput <- function(session = getDefaultReactiveDomain(), inputId, l
|
||||
updateActionButton <- function(session = getDefaultReactiveDomain(), inputId, label = NULL, icon = NULL, disabled = NULL) {
|
||||
validate_session_object(session)
|
||||
|
||||
message <- dropNulls(list(
|
||||
label = if (!is.null(label)) processDeps(label, session),
|
||||
icon = if (!is.null(icon)) processDeps(validateIcon(icon), session),
|
||||
disabled = disabled
|
||||
))
|
||||
if (!is.null(icon)) icon <- as.character(validateIcon(icon))
|
||||
message <- dropNulls(list(label=label, icon=icon, disabled=disabled))
|
||||
session$sendInputMessage(inputId, message)
|
||||
}
|
||||
#' @rdname updateActionButton
|
||||
#' @export
|
||||
updateActionLink <- function(session = getDefaultReactiveDomain(), inputId, label = NULL, icon = NULL) {
|
||||
updateActionButton(
|
||||
session,
|
||||
inputId = inputId,
|
||||
label = label,
|
||||
icon = icon
|
||||
)
|
||||
updateActionButton(session, inputId=inputId, label=label, icon=icon)
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
# Check if `x` is a tag(), tagList(), or HTML()
|
||||
# @param strict If `FALSE`, also consider a normal list() of 'tags' to be a tag list.
|
||||
isTagLike <- function(x, strict = FALSE) {
|
||||
isTag(x) || isTagList(x, strict = strict) || isTRUE(attr(x, "html"))
|
||||
}
|
||||
|
||||
isTag <- function(x) {
|
||||
inherits(x, "shiny.tag")
|
||||
}
|
||||
|
||||
isTagList <- function(x, strict = TRUE) {
|
||||
if (strict) {
|
||||
return(inherits(x, "shiny.tag.list"))
|
||||
}
|
||||
|
||||
if (!is.list(x)) {
|
||||
return(FALSE)
|
||||
}
|
||||
|
||||
all(vapply(x, isTagLike, logical(1)))
|
||||
}
|
||||
@@ -1,2 +1,2 @@
|
||||
/*! shiny 1.11.0.9000 | (c) 2012-2025 Posit Software, PBC. | License: GPL-3 | file LICENSE */
|
||||
/*! shiny 1.11.1 | (c) 2012-2025 Posit Software, PBC. | License: GPL-3 | file LICENSE */
|
||||
:where([data-shiny-busy-spinners] .recalculating){position:relative}[data-shiny-busy-spinners] .recalculating{min-height:var(--shiny-spinner-size, 32px)}[data-shiny-busy-spinners] .recalculating:after{position:absolute;content:"";--_shiny-spinner-url: var(--shiny-spinner-url, url(spinners/ring.svg));--_shiny-spinner-color: var(--shiny-spinner-color, var(--bs-primary, #007bc2));--_shiny-spinner-size: var(--shiny-spinner-size, 32px);--_shiny-spinner-delay: var(--shiny-spinner-delay, 1s);background:var(--_shiny-spinner-color);width:var(--_shiny-spinner-size);height:var(--_shiny-spinner-size);inset:calc(50% - var(--_shiny-spinner-size) / 2);mask-image:var(--_shiny-spinner-url);-webkit-mask-image:var(--_shiny-spinner-url);opacity:0;animation-delay:var(--_shiny-spinner-delay);animation-name:fade-in;animation-duration:.25s;animation-fill-mode:forwards}[data-shiny-busy-spinners] .recalculating:has(>*),[data-shiny-busy-spinners] .recalculating:empty{opacity:1}[data-shiny-busy-spinners] .recalculating>*:not(.recalculating){opacity:var(--_shiny-fade-opacity);transition:opacity .25s ease var(--shiny-spinner-delay, 1s)}[data-shiny-busy-spinners] .recalculating.html-widget-output{visibility:inherit!important}[data-shiny-busy-spinners] .recalculating.html-widget-output>*{visibility:hidden}[data-shiny-busy-spinners] .recalculating.html-widget-output :after{visibility:visible}[data-shiny-busy-spinners] .recalculating.shiny-html-output:not(.shiny-table-output):after{display:none}[data-shiny-busy-spinners][data-shiny-busy-pulse].shiny-busy:after{--_shiny-pulse-background: var( --shiny-pulse-background, linear-gradient( 120deg, transparent, var(--bs-indigo, #4b00c1), var(--bs-purple, #74149c), var(--bs-pink, #bf007f), transparent ) );--_shiny-pulse-height: var(--shiny-pulse-height, 3px);--_shiny-pulse-speed: var(--shiny-pulse-speed, 1.2s);position:fixed;top:0;left:0;height:var(--_shiny-pulse-height);background:var(--_shiny-pulse-background);z-index:9999;animation-name:busy-page-pulse;animation-duration:var(--_shiny-pulse-speed);animation-direction:alternate;animation-iteration-count:infinite;animation-timing-function:ease-in-out;content:""}[data-shiny-busy-spinners][data-shiny-busy-pulse].shiny-busy:has(.recalculating:not(.shiny-html-output)):after{display:none}[data-shiny-busy-spinners][data-shiny-busy-pulse].shiny-busy:has(.recalculating.shiny-table-output):after{display:none}[data-shiny-busy-spinners][data-shiny-busy-pulse].shiny-busy:has(#shiny-disconnected-overlay):after{display:none}[data-shiny-busy-pulse]:not([data-shiny-busy-spinners]).shiny-busy:after{--_shiny-pulse-background: var( --shiny-pulse-background, linear-gradient( 120deg, transparent, var(--bs-indigo, #4b00c1), var(--bs-purple, #74149c), var(--bs-pink, #bf007f), transparent ) );--_shiny-pulse-height: var(--shiny-pulse-height, 3px);--_shiny-pulse-speed: var(--shiny-pulse-speed, 1.2s);position:fixed;top:0;left:0;height:var(--_shiny-pulse-height);background:var(--_shiny-pulse-background);z-index:9999;animation-name:busy-page-pulse;animation-duration:var(--_shiny-pulse-speed);animation-direction:alternate;animation-iteration-count:infinite;animation-timing-function:ease-in-out;content:""}[data-shiny-busy-pulse]:not([data-shiny-busy-spinners]).shiny-busy:has(#shiny-disconnected-overlay):after{display:none}@keyframes fade-in{0%{opacity:0}to{opacity:1}}@keyframes busy-page-pulse{0%{left:-14%;right:97%}45%{left:0%;right:14%}55%{left:14%;right:0%}to{left:97%;right:-14%}}.shiny-spinner-output-container{--shiny-spinner-size: 0px}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
/*! shiny 1.11.0.9000 | (c) 2012-2025 Posit Software, PBC. | License: GPL-3 | file LICENSE */
|
||||
/*! shiny 1.11.1 | (c) 2012-2025 Posit Software, PBC. | License: GPL-3 | file LICENSE */
|
||||
"use strict";(()=>{document.documentElement.classList.add("autoreload-enabled");var c=window.location.protocol==="https:"?"wss:":"ws:",s=window.location.pathname.replace(/\/?$/,"/")+"autoreload/",i=`${c}//${window.location.host}${s}`,l=document.currentScript?.dataset?.wsUrl||i;async function u(o){let e=new WebSocket(o),n=!1;return new Promise((a,r)=>{e.onopen=()=>{n=!0},e.onerror=t=>{r(t)},e.onclose=()=>{n?a(!1):r(new Error("WebSocket connection failed"))},e.onmessage=function(t){t.data==="autoreload"&&a(!0)}})}async function d(o){return new Promise(e=>setTimeout(e,o))}async function w(){for(;;){try{if(await u(l)){window.location.reload();return}}catch{console.debug("Giving up on autoreload");return}await d(1e3)}}w().catch(o=>{console.error(o)});})();
|
||||
//# sourceMappingURL=shiny-autoreload.js.map
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
/*! shiny 1.11.0.9000 | (c) 2012-2025 Posit Software, PBC. | License: GPL-3 | file LICENSE */
|
||||
/*! shiny 1.11.1 | (c) 2012-2025 Posit Software, PBC. | License: GPL-3 | file LICENSE */
|
||||
#showcase-well{border-radius:0}.shiny-code{background-color:#fff;margin-bottom:0}.shiny-code code{font-family:Menlo,Consolas,Courier New,monospace}.shiny-code-container{margin-top:20px;clear:both}.shiny-code-container h3{display:inline;margin-right:15px}.showcase-header{font-size:16px;font-weight:400}.showcase-code-link{text-align:right;padding:15px}#showcase-app-container{vertical-align:top}#showcase-code-tabs{margin-right:15px}#showcase-code-tabs pre{border:none;line-height:1em}#showcase-code-tabs .nav,#showcase-code-tabs ul{margin-bottom:0}#showcase-code-tabs .tab-content{border-style:solid;border-color:#e5e5e5;border-width:0px 1px 1px 1px;overflow:auto;border-bottom-right-radius:4px;border-bottom-left-radius:4px}#showcase-app-code{width:100%}#showcase-code-position-toggle{float:right}#showcase-sxs-code{padding-top:20px;vertical-align:top}.showcase-code-license{display:block;text-align:right}#showcase-code-content pre{background-color:#fff}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
/*! shiny 1.11.0.9000 | (c) 2012-2025 Posit Software, PBC. | License: GPL-3 | file LICENSE */
|
||||
/*! shiny 1.11.1 | (c) 2012-2025 Posit Software, PBC. | License: GPL-3 | file LICENSE */
|
||||
"use strict";(()=>{var f=400;function c(e,i){let t=0;if(e.nodeType===3){let n=e.nodeValue.replace(/\n/g,"").length;if(n>=i)return{element:e,offset:i};t+=n}else if(e.nodeType===1&&e.firstChild){let n=c(e.firstChild,i);if(n.element!==null)return n;t+=n.offset}return e.nextSibling?c(e.nextSibling,i-t):{element:null,offset:t}}function r(e,i,t){let n=0;for(let s=0;s<e.childNodes.length;s++){let l=e.childNodes[s];if(l.nodeType===3){let o=/\n/g,d;for(;(d=o.exec(l.nodeValue))!==null;)if(n++,n===i)return c(l,d.index+t+1)}else if(l.nodeType===1){let o=r(l,i-n,t);if(o.element!==null)return o;n+=o.offset}}return{element:null,offset:n}}function w(e,i){if(!document.createRange)return;let t=document.getElementById("srcref_"+e);if(!t){t=document.createElement("span"),t.id="srcref_"+e;let n=e,s=document.getElementById(i.replace(/\./g,"_")+"_code");if(!s)return;let l=r(s,n[0],n[4]),o=r(s,n[2],n[5]);if(l.element===null||o.element===null)return;let d=document.createRange();l.element.parentNode.nodeName==="SPAN"&&l.element!==o.element?d.setStartBefore(l.element.parentNode):d.setStart(l.element,l.offset),o.element.parentNode.nodeName==="SPAN"&&l.element!==o.element?d.setEndAfter(o.element.parentNode):d.setEnd(o.element,o.offset),d.surroundContents(t)}$(t).stop(!0,!0).effect("highlight",null,1600)}Shiny&&Shiny.addCustomMessageHandler("showcase-src",function(e){e.srcref&&e.srcfile&&w(e.srcref,e.srcfile)});var a=!1,m=function(e,i){let t=i?f:1,n=e?document.getElementById("showcase-sxs-code"):document.getElementById("showcase-code-inline"),s=e?document.getElementById("showcase-code-inline"):document.getElementById("showcase-sxs-code");if(document.getElementById("showcase-app-metadata")===null){let o=$("#showcase-well");e?o.fadeOut(t):o.fadeIn(t)}$(n).hide(),$(s).fadeOut(t,function(){let o=document.getElementById("showcase-code-tabs");s.removeChild(o),n.appendChild(o),e?h():document.getElementById("showcase-code-content").removeAttribute("style"),$(n).fadeIn(t),e||(document.getElementById("showcase-app-container").removeAttribute("style"),i&&$(document.body).animate({scrollTop:$(n).offset().top}));let d=document.getElementById("readme-md");d!==null&&(d.parentElement.removeChild(d),e?(s.appendChild(d),$(s).fadeIn(t)):document.getElementById("showcase-app-metadata").appendChild(d)),document.getElementById("showcase-code-position-toggle").innerHTML=e?'<i class="fa fa-level-down"></i> show below':'<i class="fa fa-level-up"></i> show with app'}),e&&$(document.body).animate({scrollTop:0},t),a=e,u(e&&i),$(window).trigger("resize")};function u(e){let t=960,n=1,s=document.getElementById("showcase-app-code").offsetWidth;s/2>960?t=s/2:s*.66>960?t=960:(t=s*.66,n=t/960);let l=document.getElementById("showcase-app-container");$(l).animate({width:t+"px",zoom:n*100+"%"},e?f:0)}var g=function(){m(!a,!0)},p=function(){document.body.offsetWidth>1350&&m(!0,!1)};function h(){document.getElementById("showcase-code-content").style.height=$(window).height()+"px"}function y(){let e=document.getElementById("showcase-markdown-content");if(e!==null){let i=e.innerText||e.innerHTML,t=window.Showdown.converter;document.getElementById("readme-md").innerHTML=new t().makeHtml(i)}}$(window).resize(function(){a&&(u(!1),h())});window.toggleCodePosition=g;$(window).on("load",p);$(window).on("load",y);window.hljs&&window.hljs.initHighlightingOnLoad();})();
|
||||
//# sourceMappingURL=shiny-showcase.js.map
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
/*! shiny 1.11.0.9000 | (c) 2012-2025 Posit Software, PBC. | License: GPL-3 | file LICENSE */
|
||||
/*! shiny 1.11.1 | (c) 2012-2025 Posit Software, PBC. | License: GPL-3 | file LICENSE */
|
||||
"use strict";(()=>{var t=eval;window.addEventListener("message",function(a){let e=a.data;e.code&&t(e.code)});})();
|
||||
//# sourceMappingURL=shiny-testmode.js.map
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/*! shiny 1.11.0.9000 | (c) 2012-2025 Posit Software, PBC. | License: GPL-3 | file LICENSE */
|
||||
/*! shiny 1.11.1 | (c) 2012-2025 Posit Software, PBC. | License: GPL-3 | file LICENSE */
|
||||
"use strict";
|
||||
(() => {
|
||||
var __create = Object.create;
|
||||
@@ -1137,8 +1137,6 @@
|
||||
|
||||
// srcts/src/bindings/input/actionbutton.ts
|
||||
var import_jquery7 = __toESM(require_jquery());
|
||||
var iconSeparatorClass = "shiny-icon-separator";
|
||||
var iconSeparatorHTML = `<span class='${iconSeparatorClass}'></span>`;
|
||||
var ActionButtonInputBinding = class extends InputBinding {
|
||||
find(scope) {
|
||||
return (0, import_jquery7.default)(scope).find(".action-button");
|
||||
@@ -1167,23 +1165,24 @@
|
||||
getState(el) {
|
||||
return { value: this.getValue(el) };
|
||||
}
|
||||
async receiveMessage(el, data) {
|
||||
receiveMessage(el, data) {
|
||||
const $el = (0, import_jquery7.default)(el);
|
||||
if (hasDefinedProperty(data, "label") || hasDefinedProperty(data, "icon")) {
|
||||
let { label, icon } = this._getIconLabel(el);
|
||||
const deps = [];
|
||||
let label = $el.text();
|
||||
let icon = "";
|
||||
if ($el.find("i[class]").length > 0) {
|
||||
const iconHtml = $el.find("i[class]")[0];
|
||||
if (iconHtml === $el.children()[0]) {
|
||||
icon = (0, import_jquery7.default)(iconHtml).prop("outerHTML");
|
||||
}
|
||||
}
|
||||
if (hasDefinedProperty(data, "label")) {
|
||||
label = data.label.html;
|
||||
deps.push(...data.label.deps);
|
||||
label = data.label;
|
||||
}
|
||||
if (hasDefinedProperty(data, "icon")) {
|
||||
icon = data.icon.html;
|
||||
deps.push(...data.icon.deps);
|
||||
icon = Array.isArray(data.icon) ? "" : data.icon ?? "";
|
||||
}
|
||||
if (icon.trim()) {
|
||||
icon = icon + iconSeparatorHTML;
|
||||
}
|
||||
await renderContent(el, { html: icon + label, deps });
|
||||
$el.html(icon + " " + label);
|
||||
}
|
||||
if (hasDefinedProperty(data, "disabled")) {
|
||||
if (data.disabled) {
|
||||
@@ -1196,21 +1195,6 @@
|
||||
unsubscribe(el) {
|
||||
(0, import_jquery7.default)(el).off(".actionButtonInputBinding");
|
||||
}
|
||||
_getIconLabel(el) {
|
||||
const nodes = Array.from(el.childNodes);
|
||||
const nodeContents = nodes.map(
|
||||
(node) => node instanceof Element ? node.outerHTML : node.textContent
|
||||
);
|
||||
const separator = el.querySelector(`.${iconSeparatorClass}`);
|
||||
if (!separator) {
|
||||
return { icon: "", label: nodeContents.join("") };
|
||||
}
|
||||
const idx = nodes.indexOf(separator);
|
||||
return {
|
||||
icon: nodeContents.slice(0, idx).join(""),
|
||||
label: nodeContents.slice(idx + 1).join("")
|
||||
};
|
||||
}
|
||||
};
|
||||
(0, import_jquery7.default)(document).on("click", "a.action-button", function(e4) {
|
||||
e4.preventDefault();
|
||||
@@ -7213,7 +7197,7 @@ ${duplicateIdMsg}`;
|
||||
// srcts/src/shiny/index.ts
|
||||
var ShinyClass = class {
|
||||
constructor() {
|
||||
this.version = "1.11.0.9000";
|
||||
this.version = "1.11.1";
|
||||
const { inputBindings, fileInputBinding: fileInputBinding2 } = initInputBindings();
|
||||
const { outputBindings } = initOutputBindings();
|
||||
setFileInputBinding(fileInputBinding2);
|
||||
|
||||
File diff suppressed because one or more lines are too long
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
32
inst/www/shared/shiny.min.js
vendored
32
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
@@ -3,7 +3,7 @@
|
||||
"homepage": "https://shiny.rstudio.com",
|
||||
"repository": "github:rstudio/shiny",
|
||||
"name": "@types/rstudio-shiny",
|
||||
"version": "1.11.0-alpha.9000",
|
||||
"version": "1.11.1",
|
||||
"license": "GPL-3.0-only",
|
||||
"main": "",
|
||||
"browser": "",
|
||||
|
||||
@@ -1,19 +1,13 @@
|
||||
import $ from "jquery";
|
||||
import type { HtmlDep } from "../../shiny/render";
|
||||
import { renderContent } from "../../shiny/render";
|
||||
import { hasDefinedProperty } from "../../utils";
|
||||
import { InputBinding } from "./inputBinding";
|
||||
|
||||
type ActionButtonReceiveMessageData = {
|
||||
label?: { html: string; deps: HtmlDep[] };
|
||||
icon?: { html: string; deps: HtmlDep[] };
|
||||
label?: string;
|
||||
icon?: string | [];
|
||||
disabled?: boolean;
|
||||
};
|
||||
|
||||
// Needs to mirror shiny:::icon_separator()'s markup.
|
||||
const iconSeparatorClass = "shiny-icon-separator";
|
||||
const iconSeparatorHTML = `<span class='${iconSeparatorClass}'></span>`;
|
||||
|
||||
class ActionButtonInputBinding extends InputBinding {
|
||||
find(scope: HTMLElement): JQuery<HTMLElement> {
|
||||
return $(scope).find(".action-button");
|
||||
@@ -45,31 +39,38 @@ class ActionButtonInputBinding extends InputBinding {
|
||||
getState(el: HTMLElement): { value: number } {
|
||||
return { value: this.getValue(el) };
|
||||
}
|
||||
async receiveMessage(
|
||||
el: HTMLElement,
|
||||
data: ActionButtonReceiveMessageData
|
||||
): Promise<void> {
|
||||
receiveMessage(el: HTMLElement, data: ActionButtonReceiveMessageData): void {
|
||||
const $el = $(el);
|
||||
|
||||
if (hasDefinedProperty(data, "label") || hasDefinedProperty(data, "icon")) {
|
||||
let { label, icon } = this._getIconLabel(el);
|
||||
const deps: HtmlDep[] = [];
|
||||
// retrieve current label and icon
|
||||
let label: string = $el.text();
|
||||
let icon = "";
|
||||
|
||||
// to check (and store) the previous icon, we look for a $el child
|
||||
// object that has an i tag, and some (any) class (this prevents
|
||||
// italicized text - which has an i tag but, usually, no class -
|
||||
// from being mistakenly selected)
|
||||
if ($el.find("i[class]").length > 0) {
|
||||
const iconHtml = $el.find("i[class]")[0];
|
||||
|
||||
if (iconHtml === $el.children()[0]) {
|
||||
// another check for robustness
|
||||
icon = $(iconHtml).prop("outerHTML");
|
||||
}
|
||||
}
|
||||
|
||||
// update the requested properties
|
||||
if (hasDefinedProperty(data, "label")) {
|
||||
label = data.label.html;
|
||||
deps.push(...data.label.deps);
|
||||
label = data.label;
|
||||
}
|
||||
|
||||
if (hasDefinedProperty(data, "icon")) {
|
||||
icon = data.icon.html;
|
||||
deps.push(...data.icon.deps);
|
||||
// `data.icon` can be an [] if user gave `character(0)`.
|
||||
icon = Array.isArray(data.icon) ? "" : data.icon ?? "";
|
||||
}
|
||||
|
||||
if (icon.trim()) {
|
||||
icon = icon + iconSeparatorHTML;
|
||||
}
|
||||
|
||||
await renderContent(el, { html: icon + label, deps });
|
||||
// produce new html
|
||||
$el.html(icon + " " + label);
|
||||
}
|
||||
|
||||
if (hasDefinedProperty(data, "disabled")) {
|
||||
@@ -84,29 +85,6 @@ class ActionButtonInputBinding extends InputBinding {
|
||||
unsubscribe(el: HTMLElement): void {
|
||||
$(el).off(".actionButtonInputBinding");
|
||||
}
|
||||
|
||||
// Split the contents of the element into the icon and label.
|
||||
private _getIconLabel(el: HTMLElement): { icon: string; label: string } {
|
||||
const nodes = Array.from(el.childNodes);
|
||||
const nodeContents = nodes.map((node) =>
|
||||
node instanceof Element ? node.outerHTML : node.textContent
|
||||
);
|
||||
|
||||
// Query the separator element
|
||||
const separator = el.querySelector(`.${iconSeparatorClass}`);
|
||||
|
||||
// No separator found, so the entire contents are the label.
|
||||
if (!separator) {
|
||||
return { icon: "", label: nodeContents.join("") };
|
||||
}
|
||||
|
||||
// Find the index of the separator element in the child nodes.
|
||||
const idx = nodes.indexOf(separator);
|
||||
return {
|
||||
icon: nodeContents.slice(0, idx).join(""),
|
||||
label: nodeContents.slice(idx + 1).join(""),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// TODO-barret should this be put in the init methods?
|
||||
|
||||
14
srcts/types/src/bindings/input/actionbutton.d.ts
vendored
14
srcts/types/src/bindings/input/actionbutton.d.ts
vendored
@@ -1,14 +1,7 @@
|
||||
import type { HtmlDep } from "../../shiny/render";
|
||||
import { InputBinding } from "./inputBinding";
|
||||
type ActionButtonReceiveMessageData = {
|
||||
label?: {
|
||||
html: string;
|
||||
deps: HtmlDep[];
|
||||
};
|
||||
icon?: {
|
||||
html: string;
|
||||
deps: HtmlDep[];
|
||||
};
|
||||
label?: string;
|
||||
icon?: string | [];
|
||||
disabled?: boolean;
|
||||
};
|
||||
declare class ActionButtonInputBinding extends InputBinding {
|
||||
@@ -20,9 +13,8 @@ declare class ActionButtonInputBinding extends InputBinding {
|
||||
getState(el: HTMLElement): {
|
||||
value: number;
|
||||
};
|
||||
receiveMessage(el: HTMLElement, data: ActionButtonReceiveMessageData): Promise<void>;
|
||||
receiveMessage(el: HTMLElement, data: ActionButtonReceiveMessageData): void;
|
||||
unsubscribe(el: HTMLElement): void;
|
||||
private _getIconLabel;
|
||||
}
|
||||
export { ActionButtonInputBinding };
|
||||
export type { ActionButtonReceiveMessageData };
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
# Action button allows icon customization
|
||||
|
||||
Code
|
||||
actionButton("foo", "Click me")
|
||||
Output
|
||||
<button id="foo" type="button" class="btn btn-default action-button">Click me</button>
|
||||
|
||||
---
|
||||
|
||||
Code
|
||||
actionButton("foo", "Click me", icon = icon("star"))
|
||||
Output
|
||||
<button id="foo" type="button" class="btn btn-default action-button">
|
||||
<i class="far fa-star" role="presentation" aria-label="star icon"></i>
|
||||
<span class="shiny-icon-separator"></span>
|
||||
Click me
|
||||
</button>
|
||||
|
||||
@@ -55,38 +55,3 @@ test_that("Action link accepts class arguments", {
|
||||
get_class(make_link("extra extra2")), sub("\"$", " extra extra2\"", act_class)
|
||||
)
|
||||
})
|
||||
|
||||
|
||||
test_that("Action button allows icon customization", {
|
||||
|
||||
# No separator between icon and label
|
||||
expect_snapshot(actionButton("foo", "Click me"))
|
||||
|
||||
# Should include separator between icon and label
|
||||
expect_snapshot(
|
||||
actionButton("foo", "Click me", icon = icon("star"))
|
||||
)
|
||||
|
||||
# Warn on a non-HTML icon
|
||||
expect_warning(
|
||||
actionButton("foo", "Click me", icon = "not an icon"),
|
||||
"non-HTML value was provided"
|
||||
)
|
||||
|
||||
# Allows for arbitrary HTML as icon
|
||||
btn <- expect_no_warning(
|
||||
actionButton("foo", "Click me", icon = tags$svg())
|
||||
)
|
||||
btn2 <- expect_no_warning(
|
||||
actionButton("foo", "Click me", icon = tagList(tags$svg()))
|
||||
)
|
||||
btn3 <- expect_no_warning(
|
||||
actionButton("foo", "Click me", icon = list(tags$svg()))
|
||||
)
|
||||
btn4 <- expect_no_warning(
|
||||
actionButton("foo", "Click me", icon = HTML("<svg></svg>"))
|
||||
)
|
||||
expect_equal(as.character(btn), as.character(btn2))
|
||||
expect_equal(as.character(btn2), as.character(btn3))
|
||||
expect_equal(as.character(btn3), as.character(btn4))
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user